designMode

最近HTMLフォーム上でリッチテキスト編集ができるようになっているのを良く見かけますが、それを書かなければいけなくなったので調べたことメモ。ちなみに、環境はGoogle Chrome5.0.375.99 betaとfirefox(iceweasel)3.5.9で確認してます。

原理

iframeを作って、javascriptからiframe_obj.contentDocument.designMode="on";とすると編集可能な窓ができる。また、iframe_obj.contentDocument.execCommand("Bold", false, null);のようにすると、第1引数のコマンドに応じてiframe内の文字列がデコレーションされる。選択範囲のあり、なしも勝手に判別して空気を読んで振舞ってくれる。えらい。
第1引数がデコレーションの種類。上に挙げたMidas Specificationで見れる。第2引数は今のところfalse固定らしい。よく判らない。第3引数はオプション。第1引数だけじゃ指定しきれない書式の時に使う。
サンプル。

<!-- vi:et si:set sts=2 sw=2: -->
<html>
  <head>
    <script>
      function f() {
        tt = document.getElementById("tt");
        tt.contentDocument.designMode = "on";

        bold = document.getElementById("bold");
      }
      function edit(cmd, param) {
        tt = document.getElementById("tt");
        tt.contentDocument.execCommand(cmd, false, param);
      }
    </script>
  </head>

  <body onload="f();">
    <div>
      <a href="javascript:;" onclick="edit('Bold');return false;" onmousedown="return false;">Bold</a>
      <a href="javascript:;" onclick="edit('Italic');return false;" onmousedown="return false;">Italic</a>
      <a href="javascript:;" onclick="edit('Underline');return false;" onmousedown="return false;">Underline</a>
      <a href="javascript:;" onclick="edit('Heading', 'H1');return false;" onmousedown="return false;">Head</a>
    </div>
    <div>
      <iframe id="tt" width="500px" height="100px"></iframe>
    </div>
  </body>
</html>

注意点

iframe生成までjavascriptに任せようとすると、appendChild()のタイミングが重要になる。

elem = document.createElement("iframe");
elem.contentDocument.designMode = "on";
document.appendChild(elem);

だとエラー。先にappendChildしないといけないらしい。

elem = document.createElement("iframe");
document.appendChild(elem);
elem.contentDocument.designMode = "on";

これならOK。
あと、この入力をフォームに渡したいときは外にtextareaでも作っておいてsubmit時に中身をコピーすることになる。このとき、文書のHTMLはiframe_obj.contentDocument.body.innerHTMLで参照する。
また、textareaへの書き込みはぐぐるとinnerTextという属性が出てくるけど、これはfirefoxだと動かないのでtextContentにしないといけない。

<script>
  function prepost() {
    textarea = document.getElementById("post_text");
    editor = document.getElementById("editor");
    editor_body = entry_body.contentDocument.getElementsByTagName("body")[0];
    textarea.textContent = editor_body.innerHTML;
  }
  /////////////  editorをエディタ化するコードがここに入る  //////////////////
</script>
<form action="hogehoge.cgi" method="post">
  <textarea id="post_text" style="display:none"></textarea>
  <iframe id="editor"></iframe>
  <input type="submit" onclick="prepost();" value="Submit" />
</form>