javascriptで継続
業務で、wysiwygエディタを書くことになったんだけど、iframeのcontentWindow.documentには
iframeノード生成時にはアクセスできない。HTMLドキュメントに書き込んでからアクセス可能となるようだ。
var id = get_uniq_id(); var textarea = convert_to_html_node( ["iframe",{ id: "edit_"+id, onmouseout:function(){make_html();}, className: "editor"}]); var doc = textarea.contentWindow.document; //!!!この時点ではアクセスできない。 document.body.appendChild(textarea); //こうすると var doc = $("edit_"+id).contentWindow.document; // できる。 doc.designMode = "on"; ...
これだと、動的にwysiwygエディタや他の要素をjsで作っていく際に、関数的に書いているとちょっと面倒だ(手続き的なら大丈夫だが)。つまり、こちらはreturnの連鎖などでノードツリーをすべて作ってから、一気にHTMLに書き込みたいのに、その部分の処理だけ、後で実行する必要がある。
しかし、ノードを作る時点ではそのid名や各種パラメータなどがわかっていても、HTMLに書き込む時点では不明となったりする。
また、もしわかっていたとしても、ノードの初期化処理はノードの生成場所にまとめて定義したい。
var form = function(){ var id = get_uniq_id(); var make_html = function(){ ... }; var node = ...; var text_area = ...; var doc = textarea.contentWindow.document; // !!!この時点ではアクセスできない。 return ["div", {id:"form_"+id}, node, text_area]; } function main(){ var node = textarea(); document.body.appendChild(node); var doc = $("edit_"+id).contentWindow.document; //書き込む時点でidパラメーターが不明。 //この例だとidをこちらで定義して渡すとかはできるが、初期化処理が分散しよくない。 //また、iframe以外のノードも帰ってくる可能性がある際に面倒が増す。 ... }
そこで継続を使う。
継続とはSchemeなどで使われるやりかたで、一時的にあるコンテキストでの環境を関数に閉包し、それを任意の時点で起動させることらしい。
結論はたいしたことじゃないが、以下のようにする。
var form = function(){ var id = get_uniq_id(); var make_html = function(){ ... }; var text_area = ...; //この時点ではアクセスできないのでパラメーターと処理をクロージャーにしまう。 var after_func = function(){ var doc = $("edit_"+id).contentWindow.document; doc.designMode = "on"; }; return [["div",{id: "form_"+id},text_area], after_func];//返す } function main(){ var node_and_after = textarea(); var node = node[0]; var after = node[1]; document.body.appendChild(node); after();//ここで実行。 }
こんな感じて実行できる。
実際はこれでもだめで、更にtimerで1ms遅らさないとだめだった。
js言語の形としては関数的にもかけるが、関数的に書こうとすると微妙に相性の悪い機能が多い。
- この件とか、appendChildとか、pop()、shift()系とかもそうだし、sortとかも。
- 結構落とし穴があるので書くときには気をつけなければならない。
- どっちかにすればいいのにと思う。