uhyohyo.net

JavaScript初級者から中級者になろう

十七章第二回 新しいメソッド

実は、DOM4では基本的な機能に対して新しいメソッドがいろいろ追加されました。これらのメソッドはjQueryというライブラリの影響を受けていると考えられます。

定義

メソッドを紹介する前に、DOM4で導入されたノードの分類について解説します。ノードというのは、たくさん種類がありました。要素を表すElement、木構造の頂点にあるDocument、テキストノードであるTextを最もよく使うでしょう。他にDocumentFragmentも使い所がありましたね。他にも、Attr、Comment、DocumentTypeなど普段あまり目にしないタイプのノードもあります。

これらのノードを特徴により分類したものがあります。まず最初に紹介するのはParentNodeです。これは名前の通り、他のノードの親になりえるノードが該当します。具体的には、Document、DocumentFragment、Elementです。その他のノード、例えばTextは、テキストノードなので子を持つということがありませんね。

逆にChildNodeというのもあります。これは、他のノードの子になりえるノードの分類です。具体的には、DocumentType、Element, Text, Comment, ProcessingInstructionです。後者3つはテキストデータを保持するもので、CharacterDataという分類がつくられています(CharacterData自体の概念はDOM4より前から存在しました)。

ちなみに、ProcessingInstructionというのはXML文書で登場するもので、こういうやつです。

<?xml-stylesheet href="foo.xsl" type="text/xsl" ?>

このように、<?で始まり?>で終わるのがProcessing Instruction(処理命令)で、それを示すノードがProcessingInstructionです。

一見タグのように見えますが、中身の書式は自由です。これはxml-stylesheetという処理命令がたまたまこういう文法を持っていただけです。ただし、<?の直後の文字列(今回の場合はxml-stylesheet)の部分はtargetと呼ばれ、他の部分(data)と区別されます。

dataの部分は一切文法を持たないので、XMLやDOM的には中身はただの文字列にすぎません。そこで文字列を保持するノードであるCharacterDataの一種とされています。コメントについても中身はただの文字列なわけですね。ちなみに、CharacterDataは共通して、その保持する文字列をdataプロパティにより取得できます。

メソッド・プロパティたち

さて、話を戻してメソッドを紹介することにします。上でParentNodeとChildNodeの定義を紹介したのは、これに対して定義されたメソッドが存在するからです。例えばParentNodeが持つメソッドというのは、Document、DocumentFragment、Elementに共通するメソッドということになります。

このような方法で定義するメリットとしては、例えばappendChildについて考えます。これは二章第五回で登場しましたが、何がもつメソッドなのかあまり明示せずに使ってきましたが、実はNodeが持つメソッドです。だから、Elementに対してもDocumentFragmentに対しても共通に利用できました。しかし、Nodeが持つメソッドということは、子を持たないようなノード、例えばTextに対してもappendChildを呼び出すことができます。ところが、テキストノードに対して子を追加することはできないので、エラーになります。そこでDOM4では、メソッドをもつ範囲をParentNodeに制限することでこの問題を解消し、無意味なメソッドを無くそうとしています。

とはいえ、互換性の問題から、appendChildといった旧来のメソッドをいきなり消すことができません。それでも意味はあります。最近はJavaScriptの開発環境が充実し、自動補完や(TypeScriptでは)型チェックなどもできるようになりました。その精度を向上させることができます。

前置きが長くなりましたが、それでは最初はParentNodeが持つメソッドを紹介します。ParnetNodeの特徴は子を持てることだったので、子に関するメソッドやプロパティがあります。

まずはappendです。これは子ノードを追加するメソッドです。appendChildが進化したメソッドです。

進化したポイントは、まず引数は1つだけではなく任意の数可能です。これにより、複数のノードを一度に追加することができます。一つ一つappendChildする手間が省けるのでとてもうれしいですね。

もう1つは、テキストノードを挿入したいときはdocument.createTextNode(二章第五回)を使ってテキストノードを作るかわりに、引数に文字列を渡すと勝手にテキストノードに変換してくれる点です。

前回のサンプルに登場した、p要素の中身を作る次の部分をappendを使って書き換えてみましょう。

p.textContent="ああ、";
p.appendChild(time);
p.appendChild(document.createTextNode("もいい天気だなあ。"));

これを書き換えると次のようになります。

p.append("ああ、",time,"もいい天気だなあ。");

何をやっているか分かりやすくなりました。

子ノードを追加するメソッドはもう1つあります。prependです。appendでは親ノードの子の最後に全部追加しますが、prependは最初に追加します。例えば、

 A
│
├─ B
│
├─ C
│
└─ D

というときにA.prepend(E,F);を実行した場合次のようになります。

 A
│
├─ E
│
├─ F
│
├─ B
│
├─ C
│
└─ D

では、ここでいくつかParentNodeが持つプロパティを紹介します。

まずはchildrenです。これはchildNodes(Nodeに存在)のParentNode版で、HTMLCollectionを返します。childNodesと同じように使えます。

……

ここで、鋭い方ならあることに気づいたかもしれません。DOMというのは第六章でも紹介した通り、HTMLばかりでなくXML文書も扱うことができる仕様です。それなのにHTMLCollectionなどというものが登場している。これは歴史的経緯によるものです。まあ、あまり気にしないようにするのがよいでしょう。

他に、firstElementChildlastElementChildというプロパティがあります。これは、子ノードでElementであるもののうち最初/最後のものを返します。従来(Nodeのプロパティとして)存在したfirstChildとlastChildは、すべての子ノードのうち最初/最後のノードを返すことになります。例えば、

<ul>
  <li>AAA</li>
  <li>BBB</li>
  <li>CCC</li>
</ul>

のulに対してそのfirstChildはulタグとliタグの間にある改行のテキストノードとなりますが、firstElementChildは最初のli要素になります。

他には、childElementCountというプロパティは、子ノードのうちElementであるものの数を返します。

前回紹介したquerySelectorAllも実はParentNodeのメソッドとして定義されています。他に、AllではないquerySelectorというメソッドもあり、こちらはセレクタにマッチする要素のうち最初のひとつのみを返します。すなわち、返り値はElementです(1つもなければnullになります)。

他に、queryAllqueryという似た名前のメソッドがあります。こちらはセレクタではなく相対セレクタ(> pみたいな)にマッチする要素を返します。


次に、ChildNodeが持つメソッドを紹介します。こちらの特徴は親がいるということなので、やはり親子関係に関わるメソッドが多いです。

まず、最初のメソッドはremoveです。これは引数なしのメソッドで、親ノードから自身を取り除きます。親がいない(まだ他のノードの子として追加されていない)場合は何も起きません。

このメソッドの画期的なところは、親の存在を意識しなくていいところです。今までノードを木構造から除去するには、親ノードのremoveChildメソッドを呼ぶ必要がありました。こちらは子ノードのメソッドとなっている点が新しいですね。

次に、replaceWithです。これは親のreplaceChildを呼ぶに値するメソッドで、引数に渡されたノードで自身を置き換えます。先ほど紹介したappendやprependと同様に、複数のノードや文字列を引数に渡すことができます。

他には、beforeafterメソッドがあります。これは、自分の前または後に、兄弟として引数のノードを追加するメソッドです。もちろん、引数はreplaceWith等と同様に複数指定可能です。これは特に便利ですね。今まではparentNodeのinsertBeforeなどを駆使する必要がありました。

さらに、プロパティとして、preivousElementSiblingnextElementSiblingがあります。previousElementSiblingは、自身の兄弟ノードで、自身より前にあってElementであるもののうち最初のもの(一番自身に近いもの)を返します。nextElementSiblingは自身より後ろにあるElementで最初のものを返します。


というわけで、今回はDOM4時代の基本的なメソッドをいくつか紹介しました。とても便利なメソッドたちですが、現在(2015年5月)まだブラウザの実装がぜんぜん追い付いていないことは書き終わってから気付きました。使う際は気をつけましょう。