LoginSignup
169
190

More than 3 years have passed since last update.

DOMのまとめ

Last updated at Posted at 2016-03-05

参照

パーフェクトJavaScript

DOM

  • DOMとは、HTMLドキュメントやXMLドキュメントをプログラムから利用するためのAPI。

  • DOMではHTMLドキュメントやXMLドキュメントを「オブジェクトのツリー状の集合」として取り扱う。このツリーをDOMツリーと呼ぶ。

  • DOMツリーの一つ一つのオブジェクトはノードと呼ばれる。ある1つのノードから他のノードを参照する時は、親ノード、子ノード、兄弟ノード、先祖ノード、子孫ノードなどと呼ぶ。

  • DOMの仕様は、Level1〜3がW3Cにより定義されている。

タグと要素とノード

  • タグ (文書構造を指定するためのマークアップとして記述する文字列)
  • 要素とノード (要素とノードは継承関係 ノードがスーパータイプ) 

DOM操作

[なぜやるのか]

  • インタラクティブな機能

  • ある特定のDOM操作を選択しそのDOMの内容や属性を書き換えたり、新しい機能のDOM要素を作成したりして、ユーザーに視覚的なフィードバックを与える。

[手順]

  • 1.ノードの選択
  • 2.ノードの作成
  • 3.ノードの変更
  • 4.ノードの削除

Documentオブジェクト

  • DOMツリー構造のルートノード

  • Documentオブジェクトは、そのHTMLドキュメント全体を表現するオブジェクト

  • DocumentオブジェクトはJavaScriptにおいて、documentというグローバル変数でアクセスできる。

1.ノードの選択

var element = document.getElementById('foo');
  • fooというIDを持つ要素を取得。

  • IDはDOMツリーの中で一意でなければならない。

タグ名による検索とワイルドカードによる選択

//ドキュメント全体からspan要素だけを取得する
var spanElements = document.getElementByTagName('span');

//ドキュメント全体から全ての要素を取得する
var allElements = document.getElementByTagName('*');

Document.getElementById()は、オブジェクトだけのメソッドだったが、

Element.getElementByTagName()は、DocumentオブジェクトとElementオブジェクト両方のメソッド。

あるElementオブジェクトのgetElementByTagName()メソッドを実行した場合、そのElementオブジェクトの子孫ノードの中から特定のタグ名を持つ要素が取得される。

<body>
<p id="foo">
 <span>a</span>
 <span>b</span>
 <span>c</span>
</p>

<p id="bar">
 <apan>x</span>
</p>

<script>

//fooの子孫ノードの中からspan要素を取得する
var fooSpans = foo.getElementsByTagName('span');
alert(foospans.length); //3

//ドキュメント全体からspan要素を取得する
var allSpans = document.getElementsByTagName('span');
alert(allspans.length); //4

</script>

ライブオブジェクト


<div id="foo">
 <span>first</span>
  <span>second</span>
</div>

<script>
 var elems = document.getElementByTagName('span');
 alert(elems.length); //2

 var newspan = document.createElement('span');
 newSpan.appendChild(document.createTextNode('third'));

 var foo = document.getElementById('foo');
 foo.appendChild(newSpan);
 alert(elems.length); //3

</script>

なぜ、spanを追加する前に取得していたNodeListオブジェクトがspanを追加した後の状態も知っているのか?

ライブオブジェクトは、常に、DOMツリー実体への参照を持っているから、
DOMツリーへ加えられた変更は、ライブオブジェクトからも参照できる。

ライブオブジェクトを操作する上での注意点

罠.html

//ライブオブジェクトの罠

<div>sample text</div>

<script>
var divs = document.getElementByTagName('div');
var newDiv;

for(var i = 0; i < divs.length; i++){
 newDiv = document.createElement('div');
  newDiv.appendChild(document.createTextNode('new div'));
  divs[i].appendChild(newDiv);
}

</script>
  • div.lengthの値が1つ大きくなり、ループが抜けられなくなる
正しい.html

<div>sample text</div>

<script>
var divs = document.getElementByTagName('div');
var newDiv;

//divs.lengthを最初に取得しておき、それをループ継続条件に使う

for(var i = 0; len = divs.length; i < len; i++){
 newDiv = document.createElement('div');
  newDiv.appendChild(document.createTextNode('new div'));
  divs[i].appendChild(newDiv);
}

</script>

ライブオブジェクトのパフォーマンス

  • ライブオブジェクトは、パフォーマンスを考えると不利な実装
  • 一度Arrayに変換してから使う場合の方がパフォーマンスが優れている
  • Arrayに変換するにはArray.prototype.slice()メソッドをNodeListオブジェクトに対して適用する
var nodeList = document.getElementByTagName('span');

//NodeListオブジェクトをArrayオブジェクトに変換する
var array = Array.prototype.slice.call(nodeList);

(※IE8以前ではエラーになる)

NodeListをforループで扱う場合は、Arrayオブジェクトに変換してから利用した方が良い(P255)

クラス名による検索

HTMLElement.getElementsByClassName()メソッド

関連するノードを参照するためのプロパティ

スクリーンショット 2016-03-05 23.47.52.png

(※P258)

XPath

例) 「mainというidが指定されたdiv要素の中にある、contentというclassが指定された3番目のp要素」

(※P261)
evaluateメソッドの第4引数の値と返値の関係
イテレータとスナップショットの違い

Selectors API

CSSで要素を指定する時と同様の指定方法で要素が取得できる

querySelectorAll() 該当する全てを取得
querySelector() 複数の要素が該当したとしても最初の一要素だけ返す


var a = document.querySelector('#foo');
var b = document.getElementById('#foo'); 
alert(a === b); //true

var c = document.querySelectorAll('div');
var d = document.getElementByTagName('div'); 
alert(c[0] === d[0]); //true

querySelectorAll()で取得されるオブジェクトはStaticNodeListオブジェクト。

「NodeListリスト」変更を加えるとHTMLドキュメントに反映
「StaticNodeListオブジェクト」 変更を加えてもHTMLドキュメントに反映されない

StaticNodeListオブジェクトに対して変更を加えてもHTMLドキュメントには反映されないことに注意。

2.ノードの作成


Document.createElement()メソッド
Document.createTextNode()メソッド

ノードを作成しただけではHTMLドキュメントにとって何の変化もない。
作成したノードをDOMツリーに追加して初めてWebブラウザに表示される。


var elem = document.createElement('div'); //div要素を作成
var text = document.createTextNode('テキストノードを作成する');
document.body.appendChild(elem); //body直下にdiv要素を追加
elem.appendChild(text); //作成したdiv要素にテキストノードを追加

var comment = document.createComment('コメントノード');
document.body.insertBefore(comment,elem); //elemの前にコメントノードを挿入

3.ノードの内容変更


var newNode = document.createElement('div');
var oldNode = document.getElementById('foo');
var parentNode = oldNode.parentNode;
parentNode.replaceChild(newNode,oldNode);

4.ノードの削除

var elem = document.getElementById('foo');
elem.parentNode.removeChild(elem);


もっと簡単にHTMLを変更する innerHTML

  • HTML5の仕様
var elem = document.getElementById('foo');
elem.innerHTML = '<div>This is a new div element.</div>';

textContent

var elem = document.getElementById('foo');
elem.textContent = '<div>Is this a new div element?</div>';

div要素は作成されない このまま文字列としてブラウザに表示される

DOM操作のパフォーマンス

パフォーマンスが悪い.js
var parent = document.getElementById('parent');
for (var i = 0; i < 10; i++){
 var child = document.createElement('div');
 parent.appendChild(child);
}

DocumentFragmentを利用して再描画を1回にする

パフォーマンスが良い.js
var fragment = document.createDocumentFragment();
for(var i = 0; i < 10; i++){
 var child = document.createElement('div');
 //DocumentFragmentに対して子要素を追加する
 fragment.appendChild(child);
}

//実際に追加されるのはDocumentFragmentの小要素だけ
document.getElementById('parent').appendChild(fragment);
169
190
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
169
190