ページエレメントを自在に取得したいなあ(その2)

getElementsByTagName(‘値’)とgetElementsByClassName(‘値’)は、それぞれHTMLの’タグ名’とclass属性(アトリビュート)の’値’を参照して、指定した’値’と一致する要素(エレメント)を取得します。
でも、対象がウェブページであれば、一致する要素が一つだけということはあまり考えられません。そがために、これらメソッドは複数の要素を格納した配列(Array)のようなオブジェクトを返してくれます。
それでは、私が開発中ウェブページを対象にgetElementsByTagName(‘値’)の動作を確認してみます。環境はmacOSのSafariです。

> e = document.getElementsByTagName('span')
< HTMLCollection (9) = $1
 0 <span dir name="inputFileBtn">ファイルを一つ選択</span>
 1 <span dir id="inputFileName" name="inputFileName">選択されていません。</span>
 2 <span dir name="TextSpecLetPreprocessing">都度毎に前処理を行ってから抽出を行ってください。(現在はこの処理まで)</span>
 3 <span id="inputCateFileBtn">inputCateFileBtn</span>
 4 <span id="inputCateFileName">inputCateFileName</span>
 5 <span dir name="TextCopyResultPR">表のHTMLソースをコピーする。</span>
 6 <span dir name="TextMailtoResultPR">表のHTMLソースをメールする。</span>
 7 <span dir name="TextCopySampleData">サンプルデータをコピーする。</span>
 8 <span dir name="TextMailtoSampleData">サンプルデータをメールする。</span>

ページ内にあるspanタグのエレメントを9つ取得しました。左に並ぶ0から8の数字は配列のIDと同じようなものです。まずはインスタンスの確認までconstructorプロパティを参照します。

> e.constructor
< function HTMLCollection() {[native code]}

取得されたものは、HTMLCollectionインターフェイスのインスタンスであり、ここではページ内の要素を参照して集めた’arguments’のような配列風のオブジェクトだそうです。で、配列と似たように扱えるというわけでlengthプロパティが参照できます。

> e.length
< 9

ついでにinstanceofもかましておきます。

> e instanceof HTMLCollection
< true

これが配列でもなく、エレメントオブジェクトでもないことを確認します。

> e instanceof Array
< false

> e instanceof Element
< false

それでも配列のようにIDで参照できます。

> e[0]
< <span dir name="inputFileBtn">ファイルを一つ選択</span>

最初の項目が取得できました。テキストではありませんよ。では、これは何のインスタンスでしょうか?constructorプロパティを参照して、instanceofもかましてみます。

> e[0].constructor
< function HTMLSpanElement() {[native code]}

> e[0] instanceof HTMLElement
< true

> e[0] instanceof Element
< true

HTMLSpanElementインターフェースのインスタンスで、Elementからの継承を受けています。getElementsByID()で取得できるオブジェクトと同じです。
ちなみにエレメントオブジェクトは、その属性や値、内容(innerHTMLね。)を変更すると、ページのHTMLが動的に変化します。これ重要ですよ。
そして、HTMLCollectionオブジェクトの真骨頂?です。name属性の値で参照して取得ができます。インスタンスの確認まで一気に記載します。

> i = e.namedItem('inputFileName')
< <span dir id="inputFileName" name="inputFileName">選択されていません。</span>

> i.constructor
< function HTMLSpanElement() {[native code]}

> i instanceof Element
< true

ほかにもありますが、オブジェクトやインターフェースごとで機能に差異がありますから、ここら辺を押さえた上でHTMLを構築でききると比較的明示的で判りやすいソースと運用になるのではないかと期待できます。

つづいてgetElementsByClassName(‘値’)の動作確認をします。

> e = document.getElementsByClassName('dynamic')
< HTMLCollection (6) = $5
 0 <article class="content dynamic" id="FileConfigure">…</article>
 1 <article class="content dynamic" id="DataCategory">…</article>
 2 <article class="content dynamic" id="ExtractionParams">…</article>
 3 <article class="content dynamic" id="ProcessingResult">…</article>
 4 <article class="content dynamic" id="ExtractionResult">…</article>
 5 <article class="content dynamic" id="ShowSampleData">…</article>

> e[0].constructor
< function HTMLElement() {[native code]}

Class属性の値が’dynamic’であるタグがリストされました。おなじく、HTMLCollectionインターフェイスのインスタンスです。’e[0].constructor’と参照すると予想通り、エレメントオブジェクトですね。
ではそのe[0]にgetElementsByTagName(‘値’)を利用してみましょう。

> n = e[0].getElementsByTagName('input')
< HTMLCollection (7) = $10
0 <input type="radio" name="ItemDelimiter" value="Comma" onclick="hustle.MAIN.setCsvItemDlmt(this)" checked>
1 <input type="radio" name="ItemDelimiter" value="Tab" onclick="hustle.MAIN.setCsvItemDlmt(this)">
2 <input type="radio" name="QuotationMark" value="DQut" checked>
3 <input type="radio" name="QuotationMark" value="SQut">
4 <input type="radio" name="QuotationMark" value="None">
5 <input type="checkbox" name="ColumnTitle" value="FirstRow" checked>
6 <input type="button" name="PreprocessingBtn" value="前処理" onclick="hustle.MAIN.letPreprocessing(this)">

e[0]のなかのinputタグがリストされました。HTMLCollectionオブジェクトです。では、つづけて先ほどのnamedItemを参照してみましょう。

> i = n.namedItem('QuotationMark')
< <input type="radio" name="QuotationMark" value="DQut" checked>

おなじ値のname属性から一番目のinputタグだけが取得できました。複数あってもリストを返してくれるわけではありません。これだとチェックボックスなど複数選択が可能なフォームの運用には役不足ですよね。しかたないのでHTMLCollectionオブジェクトの中からname属性の値が同じものを取得することになります。個人的にはタグで限定して対象を取得できることと、処理速度を鑑みた上でこの方法を使っています。
でも、ページ全体にgetElementsByName(‘値’)を利用すると…、

> e = document.getElementsByName('QuotationMark')
< NodeList (3) = $1
 0 <input type="radio" name="QuotationMark" value="DQut" checked>
 1 <input type="radio" name="QuotationMark" value="SQut">
 2 <input type="radio" name="QuotationMark" value="None">

name属性で一気にリストを取得できるということもあります。しかしながら、ページ全体でname属性の値がかぶっていたりしたら混乱の元です。タグ名で限定できる方が確実です。「いやいや、そしたらさっきのinputタグのリストから”e[0].getElementsByName(‘QuotationMark’)”として参照したらいいんでない。」と思うでしょう?それができないんだなあ。

> e[0].getElementsByName('QuotationMark')
< TypeError: e[0].getElementsByName is not a function. (In 'e[0].getElementsByName('QuotationMark')', 'e[0].getElementsByName' is undefined)

「そんなメソッド(function)はねえよ!」って言われちゃいます。おなじくgetElementById()も言われます。でも、今投稿でとりあげたgetElementsByTagName()とgetElementsByClassName()はちゃんと利用できます。nameとid属性に関しては目的を達成できません。タグ名とclass属性は使い方次第です。
前者2メソッドは、Documentオブジェクトのメソッドで、後者2メソッドはDocumentインターフェイスのメソッドだからのようです。追求しても良いのですが、投稿が長くなるのでここまでとします。かならず必然性があっての仕様と予想できます。
ここら辺、よくわきまえてHTMLやらCSSの設計をしないと開発効率に影響があることはなんとなく想像できますよね。

ところで、getElementsByName()で取得できたのはNodeListオブジェクトです。また新人出現です。これはノードの集合オブジェクトで、querySelector()やquerySelectorAll()でも参照ならびに取得できるオブジェクトです。つぎはNodeListオブジェクト系で投稿ができればと思います。

ああ、めんど。いやいや、投稿するのがではありません。そかさ。