|
事業仕分けによる次世代スーパーコンピューターの開発予算削減について、どうお考えですか?
|
JavaScriptでDOMレンジを扱うはじめに DOMはWebページを処理するための非常に優れたAPIですが、一般的には標準のDOM機能ばかりが注目されています。多くの開発者は、DOMにただの レンジを使用すると、ドキュメントのセクションをノード境界に関係なく選択できます(この選択は内部的に行われるため、ユーザーには見えません)。レンジは、通常のDOM操作では処理できないドキュメントの細部を変更したいときに役立ちます。 DOM Level 2では、レンジを作成する
var oRange = document.createRange();
ノードと同様に、レンジはドキュメントに直接結び付いています。ドキュメントがDOMスタイルのレンジに対応しているかどうかを判断するには、 var supportsDOMRanges = document.implementation.hasFeature("Range", "2.0"); DOMのレンジを使用する場合には、まずこの点を確認し、次のようにコードをif文で囲んでおくことをお勧めします。 if (supportsDOMRange) { var oRange = document.createRange(); //range code here } DOMレンジによる単純な選択 DOMレンジを使ってドキュメントの一部を選択する最も単純な方法は、 <p id="p1"><b>Hellob> Worldp> このドキュメントに対して、次のJavaScriptを実行したとしましょう。 var oRange1 = document.createRange(); var oRange2 = document.createRange(); var oP1 = document.getElementById("p1"); oRange1.selectNode(oP1); oRange2.selectNodeContents(oP1); この例の2つのレンジには、ドキュメント内のそれぞれ異なるセクションが含まれます。 図1 ![]() レンジを作成すると、そのレンジには次のようなプロパティが割り当てられます。
レンジの開始位置を含んでいるノード(選択内の最初のノードの親)。
レンジの開始位置を含んでいるstartContainer内でのオフセット。startContainerがテキストノード、コメントノード、またはCDataノードである場合、このオフセットは、レンジを開始するまでにスキップする文字の数を表します。それ以外の場合は、レンジ内の最初の子ノードのインデックスを表します。
レンジの終了位置を含んでいるノード(選択内の最後のノードの親)。
レンジの終了位置を含んでいるendContainer内でのオフセット(startOffsetと同様のルールに従います)。
startContainerとendContainerの両方が含まれている最初のノード。
これらのプロパティはすべて読み取り専用で、レンジについての補足情報を提供するために用意されています。 これらのプロパティの使用例を次に示します。 <html> <head> <title>DOM Range Exampletitle> <script type="text/javascript"> function useRanges() { var oRange1 = document.createRange(); var oRange2 = document.createRange(); var oP1 = document.getElementById("p1"); oRange1.selectNode(oP1); oRange2.selectNodeContents(oP1); document.getElementById("txtStartContainer1").value = oRange1.startContainer.tagName; document.getElementById("txtStartOffset1").value = oRange1.startOffset; document.getElementById("txtEndContainer1").value = oRange1.endContainer.tagName; document.getElementById("txtEndOffset1").value = oRange1.endOffset; document.getElementById("txtCommonAncestor1").value = oRange1.commonAncestorContainer.tagName; document.getElementById("txtStartContainer2").value = oRange2.startContainer.tagName; document.getElementById("txtStartOffset2").value = oRange2.startOffset; document.getElementById("txtEndContainer2").value = oRange2.endContainer.tagName; document.getElementById("txtEndOffset2").value = oRange2.endOffset; document.getElementById("txtCommonAncestor2").value = oRange2.commonAncestorContainer.tagName; } script> head> <body><p id="p1"><b>Hellob> Worldp> <input type="button" value="Use Ranges" onclick="useRanges()" /> <table border="0"> <tr> <td> <fieldset> <legend>oRange1legend> Start Container: <input type="text" id="txtStartContainer1" /><br /> Start Offset: <input type="text" id="txtStartOffset1" /><br /> End Container: <input type="text" id="txtEndContainer1" /><br /> End Offset: <input type="text" id="txtEndOffset1" /><br /> Common Ancestor: <input type="text" id="txtCommonAncestor1" /><br /> fieldset> td> <td> <fieldset> <legend>oRange2legend> Start Container: <input type="text" id="txtStartContainer2" /><br /> Start Offset: <input type="text" id="txtStartOffset2" /><br /> End Container: <input type="text" id="txtEndContainer2" /><br /> End Offset: <input type="text" id="txtEndOffset2" /><br /> Common Ancestor: <input type="text" id="txtCommonAncestor2" /><br /> fieldset> td> tr> table> body> html> この例をFirefoxなどのDOM対応ブラウザで実行した結果を図2に示します。 図2 ![]() 図を見てもわかるように、 一方、 さらに、選択レンジをより細かく指定するために、次のようなメソッドが用意されています。これらのメソッドを使用すると、前述のプロパティには自動的に値が割り当てられます。
レンジの開始位置をrefNodeの前に設定します(したがって、refNodeは選択内の最初のノードになります)。startContainerプロパティはrefNodeの親に等しくなり、startOffsetプロパティはrefNodeの親のchildNodesコレクションにおけるrefNodeのインデックスに等しくなります。
レンジの開始位置をrefNodeの後ろに設定します(したがって、refNodeは選択内に含まれなくなり、その次の兄弟が選択内の最初のノードになります)。startContainerプロパティはrefNodeの親に等しくなり、startOffsetプロパティは「refNodeの親のchildNodesコレクションにおけるrefNodeのインデックス+1」に等しくなります。
レンジの終了位置をrefNodeの前に設定します(したがって、refNodeは選択内に含まれなくなり、その前の兄弟が選択内の最後のノードになります)。endContainerプロパティはrefNodeの親に等しくなり、endOffsetプロパティはrefNodeの親のchildNodesコレクションにおけるrefNodeのインデックスに等しくなります。
レンジの終了位置をrefNodeの後ろに設定します(したがって、refNodeは選択内の最後のノードになります)。endContainerプロパティはrefNodeの親に等しくなり、endOffsetプロパティは「refNodeの親のchildNodesコレクションにおけるrefNodeのインデックス+1」に等しくなります。
これらのメソッドを使用すると、前述のプロパティに値が自動的に割り当てられます。複雑なレンジ選択を行いたい場合には、各プロパティに値を直接指定することもできます。 DOMレンジによる複雑な選択 複雑なレンジを作成するには、レンジの これらのメソッドを使用して、 function useRanges() { var oRange1 = document.createRange(); var oRange2 = document.createRange(); var oP1 = document.getElementById("p1"); var iP1Index = -1; for (var i=0; i < oP1.parentNode.childNodes.length; i++) { if (oP1.parentNode.childNodes[i] == oP1) { iP1Index = i; break; } } oRange1.setStart(oP1.parentNode, iP1Index); oRange1.setEnd(oP1.parentNode, iP1Index + 1); oRange2.setStart(oP1, 0); oRange2.setEnd(oP1, oP1.childNodes.length); //textbox assignments here } ノードを選択する( たとえば、 Hello World まず、通常のDOMメソッドを使用して、「Hello」と「World」を含む2つのテキストノードの参照を取得します。 var oP1 = document.getElementById("p1"); var oHello = oP1.firstChild.firstChild; var oWorld = oP1.lastChild; テキストノード「Hello」は、実際には 次に、レンジを作成して適切なオフセットを設定します。 var oP1 = document.getElementById("p1"); var oHello = oP1.firstChild.firstChild; var oWorld = oP1.lastChild; var oRange = document.createRange(); oRange.setStart(oHello, 2); oRange.setEnd(oWorld, 3); 図3 ![]() 当然ながら、ドキュメントのセクションを選択しただけでは何の役にも立たないので、この選択範囲に対して何らかの操作を行うことになります。以降では、この点について説明します。 DOMレンジの内容の操作レンジを選択すると、内部的にドキュメントフラグメントノードが作成され、そこに選択内のすべてのノードがアタッチされます。ただし、この処理が行われる前に、選択内容が整形式であることが要求されます。 先ほどの例に示したように、この方法では、途中に 図4 ![]() レンジを使ったときにこの制限を回避できるのは、レンジでは欠けている開始タグと終了タグが認識されるからです。前述の例では、選択内に <p><b>Heb><b>llob> Worldp> このレンジに含まれるドキュメントフラグメントは図5のようになります。 図5 ![]() このドキュメントフラグメントが作成されると、レンジの内容をさまざまなメソッドで操作できるようになります。 最もわかりやすく使いやすいのは <p><b>Heb>rldp> ドキュメントフラグメント全体が削除されるので、そのままだとタグが足りなくなりますが、レンジによってタグが補われるため、ドキュメントは整形式に保たれます。 var oP1 = document.getElementById("p1"); var oHello = oP1.firstChild.firstChild; var oWorld = oP1.lastChild; var oRange = document.createRange(); oRange.setStart(oHello, 2); oRange.setEnd(oWorld, 3); var oFragment = oRange.extractContents(); document.body.appendChild(oFragment); この例では、ドキュメントフラグメントを抽出した後、ドキュメントの また、 var oP1 = document.getElementById("p1"); var oHello = oP1.firstChild.firstChild; var oWorld = oP1.lastChild; var oRange = document.createRange(); oRange.setStart(oHello, 2); oRange.setEnd(oWorld, 3); var oFragment = oRange.cloneContents(); document.body.appendChild(oFragment); このメソッドは 筆者注
ドキュメントフラグメントとそのレンジ選択に対する変更は、上記のいずれかのメソッドが呼び出されるまでは発生しません。その時点までは、元のHTMLには何の変化もありません。
DOMレンジの内容の挿入前述の3つのメソッドでは、それぞれ異なる方法でレンジの情報を削除またはコピーしました。また別のメソッドを使用して、レンジに内容を追加することもできます。 <span style="color: red">Inserted textspan> この処理は次のコードで実現できます。 var oP1 = document.getElementById("p1"); var oHello = oP1.firstChild.firstChild; var oWorld = oP1.lastChild; var oRange = document.createRange(); var oSpan = document.createElement("span"); oSpan.style.color = "red"; oSpan.appendChild(document.createTextNode("Inserted text")); oRange.setStart(oHello, 2); oRange.setEnd(oWorld, 3); oRange.insertNode(oSpan); このJavaScriptを実行すると、次のHTMLコードが生成されます。 <p id="p1"><b>He<span style="color: red">Inserted textspan>llob> Worldp> レンジ選択の前半部分である「Hello」の「llo」の直前に レンジに内容を挿入するだけでなく、
この機能は、Webページ内の特定の単語を強調表示するときなどに役立ちます。次に例を示します。 var oP1 = document.getElementById("p1"); var oHello = oP1.firstChild.firstChild; var oWorld = oP1.lastChild; var oRange = document.createRange(); var oSpan = document.createElement("span"); oSpan.style.backgroundColor = "yellow"; oRange.setStart(oHello, 2); oRange.setEnd(oWorld, 3); oRange.surroundContents(oSpan); このコードを実行すると、選択レンジの背景が黄色で強調表示されます。 DOMレンジの折りたたみレンジを空にする(つまりドキュメントのどの部分も選択していない状態にする)には、レンジの折りたたみ(collapse)を行います。レンジの折りたたみは、テキストボックスの動作に似ています。テキストボックスにテキストが含まれている場合、マウスを使用して単語全体を強調表示することができます。しかし、もう一度マウスの左ボタンをクリックすると、選択が解除され、2つの文字の間にカーソルが置かれます。レンジの折りたたみを行うときは、ドキュメントのパーツ間の位置を指定します(具体的には、レンジ選択の先頭または末尾になります)。図6に、レンジの折りたたみを行ったときの様子を示します。 図6 ![]() レンジを折りたたむには oRange.collapse(true); //collapse to the starting point alert(oRange.collapsed); //outputs "true" レンジが折りたたまれているかどうかのテストは、レンジ内の2つのノードが隣り合っているかどうかを確認するときに役立ちます。たとえば、次のようなHTMLコードがあるとします。 <p id="p1">Paragraph 1p><p id="p2">Paragraph 2p> このコードの正確な構造がわからない場合は(たとえばコードを自動生成した場合など)、次のようにしてレンジを作成してみます。 var oP1 = document.getElementById("p1"); var oP2 = document.getElementById("p2"); var oRange = document.createRange(); oRange.setStartAfter(oP1); oRange.setStartBefore(oP2); alert(oRange.collapsed); //outputs "true" この例では、p1の末尾とp2の先頭の間に何もないので、作成したレンジは折りたたまれています。 DOMレンジの比較 複数のレンジがある場合は、
次に例を示します。 var oRange1 = document.createRange(); var oRange2 = document.createRange(); var oP1 = document.getElementById("p1"); oRange1.selectNodeContents(oP1); oRange2.selectNodeContents(oP1); oRange2.setEndBefore(oP1.lastChild); alert(oRange1.compareBoundaryPoints(Range.START_TO_START, oRange2)); //outputs 0 alert(oRange1.compareBoundaryPoints(Range.END_TO_END, oRange2)); //outputs 1; このコードでは、2つのレンジがどちらも 図7 ![]() DOMレンジの複製 必要に応じて、
var oNewRange = oRange.cloneRange();
新しいレンジには元のレンジとまったく同じプロパティが含まれており、これらのプロパティの値は、元のレンジに影響を与えずに修正できます。 クリーンアップ レンジを使い終わったら、 oRange.detach();
この記事はNicholas C. Zakas著『Professional JavaScript for Web Developers』(Wrox, 2006, ISBN: 0-471-77778-1)の第10章「Advanced DOM Techniques」からの抜粋です。
Copyright 2005 by WROX. All rights reserved. Reproduced here by permission of the publisher. 著者紹介Nicholas C. Zakas(Nicholas C. Zakas)
『Professional Ajax』(WROX, ISBN: 0-471-77778-1)および『Professional JavaScript for Web Developers』(WROX, ISBN: 0-7645-7908-8)の著者。
関連記事 最新トップニュース
|
japan.internet.com 10周年記念
インターネットコムマーケティングセミナー ROI を最適化するパフォーマンスマーケティングの最前線 【12/16(水)13時〜 東京・赤坂】 申込はコチラ>>
|