DOM操作は、「ライブ」ページを作成するための鍵です。
ここでは、新しい要素を「オンザフライ」で作成し、既存のページコンテンツを変更する方法を見ていきます。
例:メッセージを表示する
例を使って説明しましょう。alert
よりも見栄えの良いメッセージをページに追加します。
どのように見えるかはこちらです
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<div class="alert">
<strong>Hi there!</strong> You've read an important message.
</div>
これはHTMLの例でした。それでは、JavaScriptで同じdiv
を作成してみましょう(スタイルはすでにHTML/CSSにあると仮定します)。
要素の作成
DOMノードを作成するには、2つの方法があります
document.createElement(tag)
-
指定されたタグを持つ新しい_要素ノード_を作成します
let div = document.createElement('div');
document.createTextNode(text)
-
指定されたテキストを持つ新しい_テキストノード_を作成します
let textNode = document.createTextNode('Here I am');
ほとんどの場合、メッセージのdiv
など、要素ノードを作成する必要があります。
メッセージの作成
メッセージdivの作成には3つの手順が必要です
// 1. Create <div> element
let div = document.createElement('div');
// 2. Set its class to "alert"
div.className = "alert";
// 3. Fill it with the content
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";
要素を作成しました。しかし、今のところ、それはページではなく、div
という名前の変数にのみ存在します。そのため、見ることはできません。
挿入方法
div
を表示するには、document
のどこかに挿入する必要があります。たとえば、document.body
によって参照される<body>
要素に挿入します。
そのためには、特別なメソッドappend
があります:document.body.append(div)
。
完全なコードはこちらです
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<script>
let div = document.createElement('div');
div.className = "alert";
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";
document.body.append(div);
</script>
ここでは、document.body
でappend
を呼び出しましたが、他の要素でappend
メソッドを呼び出して、別の要素をその中に配置することもできます。たとえば、div.append(anotherElement)
を呼び出すことで、<div>
に何かを追加できます。
挿入する場所を指定する、さらに挿入方法があります
node.append(...nodes or strings)
– ノードまたは文字列をnode
の_最後に_追加します。node.prepend(...nodes or strings)
– ノードまたは文字列をnode
の_先頭に_挿入します。node.before(...nodes or strings)
-- ノードまたは文字列をnode
の_前に_挿入します。node.after(...nodes or strings)
-- ノードまたは文字列をnode
の_後に_挿入します。node.replaceWith(...nodes or strings)
--node
を指定されたノードまたは文字列に置き換えます。
これらのメソッドの引数は、挿入するDOMノードまたはテキスト文字列(自動的にテキストノードになる)の任意のリストです。
実際に見てみましょう。
リストに項目を追加し、その前後にテキストを追加するためにこれらのメソッドを使用する例を次に示します
<ol id="ol">
<li>0</li>
<li>1</li>
<li>2</li>
</ol>
<script>
ol.before('before'); // insert string "before" before <ol>
ol.after('after'); // insert string "after" after <ol>
let liFirst = document.createElement('li');
liFirst.innerHTML = 'prepend';
ol.prepend(liFirst); // insert liFirst at the beginning of <ol>
let liLast = document.createElement('li');
liLast.innerHTML = 'append';
ol.append(liLast); // insert liLast at the end of <ol>
</script>
メソッドの動作を視覚的に示した図を次に示します
最終的なリストは次のようになります
before
<ol id="ol">
<li>prepend</li>
<li>0</li>
<li>1</li>
<li>2</li>
<li>append</li>
</ol>
after
前述のように、これらのメソッドは、1回の呼び出しで複数のノードとテキストを挿入できます。
たとえば、ここでは文字列と要素が挿入されています
<div id="div"></div>
<script>
div.before('<p>Hello</p>', document.createElement('hr'));
</script>
注意:テキストは「HTMLとして」ではなく「テキストとして」挿入され、<
、>
などの文字は適切にエスケープされます。
最終的なHTMLは次のようになります
<p>Hello</p>
<hr>
<div id="div"></div>
言い換えれば、文字列はelem.textContent
が行うのと同じように安全な方法で挿入されます。
したがって、これらのメソッドはDOMノードまたはテキストを挿入するためにのみ使用できます。
しかし、elem.innerHTML
が行うのと同じように、すべてのタグなどが機能するHTML文字列を「htmlとして」挿入したい場合はどうでしょうか?
insertAdjacentHTML/Text/Element
そのためには、別の非常に用途の広いメソッド:elem.insertAdjacentHTML(where, html)
を使用できます。
最初のパラメーターはコードワードであり、elem
を基準にしてどこに挿入するかを指定します。次のいずれかである必要があります
"beforebegin"
–html
をelem
の直前に挿入します。"afterbegin"
–html
をelem
の先頭に挿入します。"beforeend"
–html
をelem
の最後に挿入します。"afterend"
–html
をelem
の直後に挿入します。
2番目のパラメーターはHTML文字列であり、「HTMLとして」挿入されます。
例えば
<div id="div"></div>
<script>
div.insertAdjacentHTML('beforebegin', '<p>Hello</p>');
div.insertAdjacentHTML('afterend', '<p>Bye</p>');
</script>
…は次のようになります
<p>Hello</p>
<div id="div"></div>
<p>Bye</p>
このようにして、任意のHTMLをページに追加できます。
挿入バリアントの図を次に示します
これと前の図の類似点に簡単に気付くことができます。挿入ポイントは実際には同じですが、このメソッドはHTMLを挿入します。
このメソッドには2つの兄弟がいます
elem.insertAdjacentText(where, text)
– 同じ構文ですが、HTMLの代わりにtext
の文字列が「テキストとして」挿入されます。elem.insertAdjacentElement(where, elem)
– 同じ構文ですが、要素を挿入します。
これらは主に構文を「統一」するために存在します。実際には、ほとんどの場合、insertAdjacentHTML
のみが使用されます。要素とテキストについては、メソッドappend/prepend/before/after
があるため、記述が短く、ノード/テキストを挿入できます。
メッセージを表示する別の方法は次のとおりです
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<script>
document.body.insertAdjacentHTML("afterbegin", `<div class="alert">
<strong>Hi there!</strong> You've read an important message.
</div>`);
</script>
ノードの削除
ノードを削除するには、メソッドnode.remove()
があります。
1秒後にメッセージを消してみましょう
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<script>
let div = document.createElement('div');
div.className = "alert";
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";
document.body.append(div);
setTimeout(() => div.remove(), 1000);
</script>
注意:要素を別の場所に_移動_したい場合は、古い場所から削除する必要はありません。
すべての挿入メソッドは、古い場所からノードを自動的に削除します。
たとえば、要素を交換してみましょう
<div id="first">First</div>
<div id="second">Second</div>
<script>
// no need to call remove
second.after(first); // take #second and after it insert #first
</script>
ノードの複製:cloneNode
同様のメッセージをもう1つ挿入するにはどうすればよいでしょうか?
関数を作成してそこにコードを配置することもできます。しかし、別の方法は、既存のdiv
を_複製_し、その中のテキストを変更することです(必要な場合)。
大きな要素がある場合、それがより高速で簡単な場合があります。
elem.cloneNode(true)
の呼び出しは、すべての属性とサブ要素を含む要素の「ディープ」クローンを作成します。elem.cloneNode(false)
を呼び出すと、子要素のないクローンが作成されます。
メッセージをコピーする例
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<div class="alert" id="div">
<strong>Hi there!</strong> You've read an important message.
</div>
<script>
let div2 = div.cloneNode(true); // clone the message
div2.querySelector('strong').innerHTML = 'Bye there!'; // change the clone
div.after(div2); // show the clone after the existing div
</script>
DocumentFragment
DocumentFragment
は、ノードのリストを渡すためのラッパーとして機能する特別なDOMノードです。
他のノードを thereto に追加できますが、それをどこかに挿入すると、代わりにその内容が挿入されます。
たとえば、以下のgetListContent
は<li>
項目を含むフラグメントを生成し、後で<ul>
に挿入されます
<ul id="ul"></ul>
<script>
function getListContent() {
let fragment = new DocumentFragment();
for(let i=1; i<=3; i++) {
let li = document.createElement('li');
li.append(i);
fragment.append(li);
}
return fragment;
}
ul.append(getListContent()); // (*)
</script>
最後の行(*)
ではDocumentFragment
を追加しますが、「ブレンドイン」されるため、結果の構造は次のようになります
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
DocumentFragment
が明示的に使用されることはめったにありません。ノードの配列を返すことができるのに、なぜ特別な種類のノードに追加するのでしょうか?書き直された例
<ul id="ul"></ul>
<script>
function getListContent() {
let result = [];
for(let i=1; i<=3; i++) {
let li = document.createElement('li');
li.append(i);
result.push(li);
}
return result;
}
ul.append(...getListContent()); // append + "..." operator = friends!
</script>
DocumentFragment
については、後で説明するテンプレート要素など、その上にいくつかの概念があるため、主に言及しています。
従来の挿入/削除メソッド
歴史的な理由で存在する「従来の」DOM操作メソッドもあります。
これらのメソッドは非常に古い時代から来ています。今日では、append
、prepend
、before
、after
、remove
、replaceWith
などの最新のメソッドの方が柔軟性があるため、これらを使用する理由はありません。
これらのメソッドをここでリストする唯一の理由は、多くの古いスクリプトで見つけることができるからです
parentElem.appendChild(node)
-
node
をparentElem
の最後の子として追加します。次の例では、新しい
<li>
を<ol>
の最後に追加します<ol id="list"> <li>0</li> <li>1</li> <li>2</li> </ol> <script> let newLi = document.createElement('li'); newLi.innerHTML = 'Hello, world!'; list.appendChild(newLi); </script>
parentElem.insertBefore(node, nextSibling)
-
node
をparentElem
のnextSibling
の前に挿入します。次のコードは、2番目の
<li>
の前に新しいリスト項目を挿入します<ol id="list"> <li>0</li> <li>1</li> <li>2</li> </ol> <script> let newLi = document.createElement('li'); newLi.innerHTML = 'Hello, world!'; list.insertBefore(newLi, list.children[1]); </script>
newLi
を最初の要素として挿入するには、次のようにしますlist.insertBefore(newLi, list.firstChild);
parentElem.replaceChild(node, oldChild)
-
parentElem
の子の中でoldChild
をnode
に置き換えます。 parentElem.removeChild(node)
-
node
をparentElem
から削除します(node
がその子であると仮定します)。次の例では、最初の
<li>
を<ol>
から削除します<ol id="list"> <li>0</li> <li>1</li> <li>2</li> </ol> <script> let li = list.firstElementChild; list.removeChild(li); </script>
これらのメソッドはすべて、挿入/削除されたノードを返します。言い換えれば、parentElem.appendChild(node)
はnode
を返します。しかし、通常は戻り値は使用されず、メソッドを実行するだけです。
「document.write」について
Webページに何かを追加する、非常に古いメソッドがもう1つあります:document.write
。
構文
<p>Somewhere in the page...</p>
<script>
document.write('<b>Hello from JS</b>');
</script>
<p>The end</p>
document.write(html)
の呼び出しは、html
をページに「今すぐここ」に書き込みます。html
文字列は動的に生成できるため、ある程度柔軟性があります。JavaScriptを使用して本格的なWebページを作成し、書き込むことができます。
このメソッドは、DOMも標準もない時代、本当に古い時代から来ています。それを使用するスクリプトがあるため、まだ生きています。
最新のスクリプトでは、次の重要な制限があるため、めったに見られません
document.write
の呼び出しは、ページの読み込み中にのみ機能します。
後で呼び出すと、既存のドキュメントの内容が消去されます。
例えば
<p>After one second the contents of this page will be replaced...</p>
<script>
// document.write after 1 second
// that's after the page loaded, so it erases the existing content
setTimeout(() => document.write('<b>...By this.</b>'), 1000);
</script>
そのため、「読み込み後」の段階では、上記で説明した他のDOMメソッドとは異なり、使い物になりません。
それが欠点です。
利点もあります。技術的には、ブラウザが受信HTMLを読み込んでいる(「パース」している)間に document.write
が呼び出され、何かを書き込むと、ブラウザはそれをHTMLテキストに最初から存在していたかのように処理します。
そのため、DOM操作が不要 なため、非常に高速に動作します。DOMがまだ構築されていない間に、ページテキストに直接書き込みます。
そのため、ページ読み込み段階で、大量のテキストをHTMLに動的に追加する必要があり、速度が重要な場合は、役立つ可能性があります。しかし、実際にはこれらの要件が揃うことはまれです。そして、通常、このメソッドは古いスクリプトにのみ見られます。
まとめ
-
新しいノードを作成する方法
document.createElement(tag)
– 指定されたタグを持つ要素を作成します。document.createTextNode(value)
– テキストノードを作成します(めったに使用されません)。elem.cloneNode(deep)
– 要素を複製します。deep==true
の場合は、すべての子孫も含めて複製します。
-
挿入と削除
node.append(...nodes or strings)
–node
の末尾に挿入します。node.prepend(...nodes or strings)
–node
の先頭に挿入します。node.before(...nodes or strings)
–node
の直前に挿入します。node.after(...nodes or strings)
–node
の直後に挿入します。node.replaceWith(...nodes or strings)
–node
を置き換えます。node.remove()
–node
を削除します。
テキスト文字列は「テキストとして」挿入されます。
-
「旧式」のメソッドもあります
parent.appendChild(node)
parent.insertBefore(node, nextSibling)
parent.removeChild(node)
parent.replaceChild(newElem, node)
これらのメソッドはすべて
node
を返します。 -
html
にHTMLがいくつかある場合、elem.insertAdjacentHTML(where, html)
はwhere
の値に応じてHTMLを挿入します。"beforebegin"
–elem
の直前にhtml
を挿入します。"afterbegin"
–html
をelem
の先頭に挿入します。"beforeend"
–html
をelem
の最後に挿入します。"afterend"
–elem
の直後にhtml
を挿入します。
また、同様のメソッド
elem.insertAdjacentText
とelem.insertAdjacentElement
があり、テキスト文字列と要素を挿入しますが、これらはめったに使用されません。 -
ページの読み込みが完了する前にHTMLを追加するには
document.write(html)
ページの読み込み後、このような呼び出しはドキュメントを消去します。主に古いスクリプトで見られます。
コメント
<code>
タグを使用し、複数行の場合は<pre>
タグで囲み、10行を超える場合はサンドボックス(plnkr、jsbin、codepen など)を使用してください。