吉本集の個人ブログ
Web制作の技術について書いています。たまに日記も書きます。

【JS入門】DOMについて Vol.2(ノードの参照・取得)

2018年2月4日 / category : javascript

ノードを参照・取得する方法をいくつか紹介していきます。

1. documentオブジェクトが持つプロパティからの参照

DOMツリーの先頭にあるdocumentオブジェクトは、HTMLDocumentインターフェースのプロパティを継承しているオブジェクトです。さらには、Documentインターフェース、Nodeインターフェース、そしてEventTargetインターフェースのプロパティも継承しています。これらのインターフェースが持っているプロパティを使用して、DOMオブジェクトを参照します。次のHTMLを例にして、コードを見てみます。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<div>
<h1><span>見出しです。</span></h1>
<p id="txt">お腹減った</p>
</div>
<script src="./js/sample.js"></script>
</body>
</html>
sample.js
let html = document.documentElement;
console.log( html ); // <html lang="ja">...</html>

let head = document.head;
console.log( head ); // <head>...</head>

let body = document.body;
console.log( body ); // <body>...</body>

let scripts = document.scripts;
console.log( scripts ); // HTMLCollection [script]
console.log( scripts[0] ); // <script src="./js/test2.js"></script>

プロパティdocumentElementは、ルート要素を参照します。HTMLの場合は、html要素がそれに当たります。プロパティhead、プロパティbodyは、それぞれhead要素、body要素のDOMオブジェクトを参照します。プロパティscriptsは、HTMLにあるscript要素を全て参照し、HTMLCollectionインターフェースを継承したオブジェクトを参照します。上のHTMLにscript要素は1つしかありませんので、0番目(最初の要素)を指定することで参照できます。そのほかにDOMオブジェクトを参照できるプロパティを次に挙げます。

フォーカス中の要素を参照します。

document.activeElement

body要素を参照します。

document.body

文書型宣言を参照します。

document.doctype

ルート要素(HTMLの場合はhtml要素)を参照します。

document.documentElement

全てのembed要素を参照します。返り値:HTMLCollection

document.embeds

全てのform要素を参照します。返り値:HTMLCollection

document.forms

head要素を参照します。

document.head

全てのimg要素を参照します。返り値:HTMLCollection

document.images

href属性を持つ全てのa,area要素を参照します。返り値:HTMLCollection

document.links

全てのscript要素を参照します。返り値:HTMLCollection

document.scripts

スクロール対象の要素を参照します。

document.scrollingElement

全てのstyle要素を参照します。返り値:StyleSheetList

document.styleSheets

2. 要素が持つ属性から取得する

documentオブジェクトには、指定したid属性の要素を返すメソッドgetElementById、指定したclass属性の要素を全て返すメソッドgetElementsByClassName、指定したname属性の要素を返すメソッドgetElementsByName、指定したタグ名の要素を全て返すメソッドgetElementsByTagNameがあります。これを使ってノード・DOMオブジェクトを取得することができます。

メソッドgetElementByIdを使用したコード

Document.getElementById(id):指定したid名を持つElementオブジェクトを返す。
引数 id : id名(string)
戻り値(return): Element
構文

let element = Document.getElementById('txt');

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<div>
<h1><span>見出しです。</span></h1>
<p id="txt">お腹減った</p>
</div>
<script src="./js/sample.js"></script>
</body>
</html>
sample.js
let p = document.getElementById('txt');
console.log( p ); // <p id="txt">お腹減った</p>

メソッドgetElementsByClassNameを使用したコード

Document.getElementsByClassName(className):指定したクラス名を持つElementオブジェクトを全てHTMLCollectionオブジェクトに格納して返す。
引数 className : クラス名(string)
戻り値(return): HTMLCollection
構文

let elements = Document.getElementsByClassName('txt');

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<div class="element">
<h1 class="element"><span class="element">見出しです。</span></h1>
<p class="element">お腹減った</p>
</div>
<script src="./js/sample.js"></script>
</body>
</html>
sample.js
let element = document.getElementsByClassName('element');
console.log( element ); // HTMLCollection(4) [div.element, h1.element, span.element, p.element]

メソッドgetElementsByNameを使用したコード

Document.getElementsByName(name):指定したname属性の値を持つノードリストを返す。
引数 name : name属性の値(string)
戻り値(return): NodeList
構文

let elements = Document.getElementsByName('txt');

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<div>
<h1 name="heading"><span>見出しです。</span></h1>
<p>お腹減った</p>
</div>
<script src="./js/sample.js"></script>
</body>
</html>
sample.js
let heading = document.getElementByName('heading');
console.log( heading ); // NodeList [h1]

メソッドgetElementByNameで取得したオブジェクトは、HTMLCollectionインターフェースを継承したオブジェクトではなく、NodeListインターフェースを継承したオブジェクトになります。

メソッドgetElementsByTagNameを使用したコード

Document.getElementsByTagName(tagName):指定したタグ名のElementオブジェクトを全てHTMLCollectionオブジェクトに格納して返す。
引数 tagName : タグ名(string)
戻り値(return): HTMLCollection
構文

let elements = Document.getElementsByTagName('p');

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<div>
<h1><span>見出しです。</span></h1>
<p>お腹減った</p>
</div>
<script src="./js/sample.js"></script>
</body>
</html>
sample.js
let div = document.getElementsByTagName('div');
console.log( div ); // HTMLCollection [div]

3. セレクタの条件にあった要素を取得する

documentオブジェクトには、セレクタの条件にあった最初の要素を取得するメソッドquerySelector、セレクタの条件にあった全ての要素を取得するメソッドquerySelectorAllがあります。

Document.querySelector(selectors):セレクタの条件にあった最初のElementオブジェクトを返す。
引数 selectors : セレクタ(string)
戻り値(return): Element
構文

let element = Document.querySelector('div > p');

Document.querySelectorAll(selectors):セレクタの条件にあった全てのノードリストを返す。
引数 selectors : セレクタ(string)
戻り値(return): NodeList
構文

let elements = Document.querySelectorAll('div > p');

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<div>
<h1><span>見出しです。</span></h1>
<p>最初のテキスト</p>
<p>2番目のテキスト</p>
<p>3番目のテキスト</p>
</div>
<script src="./js/sample.js"></script>
</body>
</html>
sample.js
let p = document.querySelector('div > p');
console.log( p ); // <p>最初のテキスト</p>

let plist = document.querySelectorAll('div > p');
console.log( plist ); // NodeList(3) [p, p, p]

メソッドquerySelectorAllの引数にclass属性を指定することでNodeListオブジェクトを取得することができますが、推奨できません。class属性で取得する場合は、メソッドgetElementsByClassNameを使用してください。メソッドquerySelectorAllは、メソッドgetElementsByClassNameに比べ処理速度が遅いためです。

4. ノードリストを順に辿って要素を参照する

ノードリストとは、ノードを順序付けてコレクションしたオブジェクトで、NodeListインターフェースを継承したオブジェクトです。配列のように扱うことができますが、Arrayオブジェクトを継承しているわけではありません。

全ての子ノードを参照する

Node.childNodes:子ノードをコレクションしたノードリストを参照する。
構文

let nodelist = Node.childNodes;

次のコードは、documentオブジェクトの全ての子ノードを参照するコードです。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<div>
<h1><span>見出しです。</span></h1>
<p>最初のテキスト</p>
<p>2番目のテキスト</p>
<p>3番目のテキスト</p>
</div>
<script src="./js/sample.js"></script>
</body>
</html>
sample.js
let nodelist = document.childNodes;
nodelist.forEach(function(element, index){
	console.log( index, element );
});

コンソール画面

<!DOCTYPE html>
<html lang=​"ja">​<head>​...</head>​<body>​...</body>​</html>​

プロパティchildNodesは、Nodeインターフェースが持っているプロパティで、自身の持っている子ノードをNodeListオブジェクトで返します。上のコードでは、documentオブジェクトから参照しているので、doctype, htmlの2つのDOMオブジェクトがコレクションされています。(doctype宣言もDOMオブジェクトです。)

プロパティchildNodesは、Nodeインターフェースを継承しているオブジェクトで使用できるので、html要素からも参照できます。

sample.js
let html = document.documentElement;
let nodelist = html.childNodes;
nodelist.forEach(function(element, index){
	console.log( index, element );
});

コンソール画面

<head>...</head>
#text
<body>...</body>

上のコードは、html要素から参照した子ノードのコレクションです。html要素には、head要素とbody要素の2つの要素のみ参照できると想像したかと思いますが、そのほかに「text」が参照されています。この「text」はなんでしょうか。

HTMLのコードを見ると、head要素とbody要素の間に「改行」が入っています。実はこの「改行」はテキストデータとして認識され、このデータがDOMオブジェクトとして生成されています。

話を少し脱線して、テキストデータのDOMオブジェクトを見てみます。

sample.js
let html = document.documentElement;
let nodelist = html.childNodes;
let text = nodelist[1];
console.log( text ); // #text
console.log( text.__proto__ ); // Text {...}
console.log( Text.__proto__ ); // CharacterData() { [native code] }
console.log( CharacterData.__proto__ ); // Node() { [native code] }

テキストデータのDOMオブジェクトは、Textインターフェースを継承したオブジェクトであることがわかりました。

子ノードの最初のノードを参照する / 子ノードの最後のノードを参照する

Node.firstChild:子ノードの最初のノードを参照する。
構文

let node = Node.firstChild;

Node.lastChild:子ノードの最後のノードを参照する
構文

let node = Node.lastChild;

それでは話を戻します。次のコードは、子ノードの最初のノード、子ノードの最後のノード参照するコードです。

sample.js
let html = document.documentElement;
let first = html.firstChild;
let last = html.lastChild;
console.log( first ); // <head>...</head>
console.log( last ); // <body>...</body>

子ノードの最初のノードを参照するには、プロパティfirstChildを使用します。子ノードの最後のノードを参照するには、プロパティlastChildを使用します。

親ノードを参照する

Node.parentNode:親ノードを参照する。
構文

let node = Node.parentNode;

sample.js
let html = document.documentElement;
let parent = html.parentNode;
console.log( parent ); // #document

直前のノードを参照する / 直後のノードを参照する

Node.previousSibling:直前のノードを参照する。
構文

let node = Node.previousSibling;

Node.nextSibling:直後のノードを参照する。
構文

let node = Node.nextSibling;

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<p><span>直前のテキスト</span><span id="target">ターゲット</span><span>直後のテキスト</span></p>
<script src="./js/sample.js"></script>
</body>
</html>
sample.js
let target = document.getElementById('target');
let prev = target.previousSibling;
let next = target.nextSibling;
console.log( prev ); // <span>直前のテキスト</span>
console.log( next ); // <span>直後のテキスト</span>

以上が、ノードへのアクセスする方法の紹介になります。そのほかにメソッドelementFromPointを使用した当たり判定でのノードの取得方法がありますが、これは別の機会に解説できればと思います。