ってことで、超今さら感たっぷりかもだけど Dom やるよ。
* getElementsByName みたいなあまり使わないものは省くよ
手始めにノードの選択あたりから
サンプル HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Dom Sample</title>
</head>
<body>
<div id="header">
<h1><a href="#"><img src="http://placekitten.com/g/200/300" alt="sample"></a></h1>
<ul>
<li><a href="#">Site Map</a></li>
<li><a href="#">Login</a></li>
</ul>
</div>
<div id="nav">
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Documentation</a></li>
<li><a href="#">Download</a></li>
<li><a href="#">Plugins</a></li>
<li><a href="#">Support</a></li>
</ul>
</div>
<div id="content">
<h2>What is XXX?</h2>
<p class="text">
Sit delectus laboriosam in alias maxime ad accusamus. Delectus provident molestias voluptates quibusdam voluptas dolor alias. Nam eos qui incidunt molestiae modi voluptas earum sint autem dolores accusamus molestiae aliquid.
</p>
<p class="text">
Amet quae perspiciatis dolor quos incidunt amet quaerat sunt itaque eos cupiditate eius voluptate eum. Animi enim rerum voluptates magnam obcaecati eveniet libero. Ea labore aut hic nisi temporibus animi?
</p>
<p class="text">
Consectetur commodi quibusdam sed veniam in accusamus aperiam, voluptatum quasi quaerat eligendi! In sint adipisci ut exercitationem sit expedita, modi porro harum molestiae magni impedit. Accusantium ipsum ea eaque nisi.
</p>
<h2>Corporate Members</h2>
<ul>
<li><a href="#">Google</a></li>
<li><a href="#">Yahoo</a></li>
<li><a href="#">Apple</a></li>
<li><a href="#">Microsoft</a></li>
</ul>
<h2>API</h2>
<div class="LImgRTextWrap">
<dl>
<dt><a href="#"><img src="http://placekitten.com/g/200/300" alt="sample"></a></dt>
<dd>Elit distinctio eveniet porro iste cumque. Atque voluptatum ducimus impedit sed eveniet quo beatae quos. Quam laudantium ad ipsam veritatis quam. Eius suscipit at autem nesciunt aliquid sequi laudantium cum.</dd>
</dl>
</div>
<div class="RImgLTextWrap">
<dl>
<dt>Ipsum voluptatibus cumque perspiciatis possimus possimus molestias! A tempora enim fugit quam voluptatem deserunt perspiciatis exercitationem voluptatibus! Et et adipisci accusantium officia necessitatibus magni necessitatibus ut! Eligendi fugit veritatis reiciendis!</dt>
<dd><a href="#"><img src="http://placekitten.com/g/200/300" alt="sample"></a></dd>
</dl>
</div>
<div class="wrap">
<div class="left"></div>
<div class="right">
<div id="uniqBox"></div>
</div>
</div>
<div id="side">
<h3>Banner</h3>
<ul>
<li><a href="#"><img src="http://placekitten.com/g/200/300" alt="sample"></a></li>
<li><a href="#"><img src="http://placekitten.com/300/300?image=14" alt="sample"></a></li>
<li><a href="#"><img src="http://placekitten.com/400/300?image=15" alt="sample"></a></li>
</ul>
<p class="text">
Elit iure rem voluptatem cupiditate ducimus repudiandae voluptatibus enim delectus nisi eos ipsam molestias delectus? Assumenda tempora cumque pariatur blanditiis ratione ex qui nesciunt error. Ipsum neque nemo illo officia.
</p>
</div>
</div>
<div id="footer">
<h2>Books</h2>
<ul>
<li><a href="#">Learning XXX</a></li>
<li><a href="#">XXX First Step</a></li>
<li><a href="#">Programming XXX</a></li>
</ul>
<p>Copyright 2015 XXX Foundation. XXX License</p>
</div>
</body>
</html>
IDによる指定
// jQuery
var nav = jQuery("#nav");
// Dom
var nav = document.getElementById("nav");
タグ名(要素名)による指定
// jQuery
var divs = jQuery("div");
console.log( divs.length );
7
//dom
var divs = document.getElementsByTagName("div");
console.log( divs.length );
7
クラス名による指定
// jQuery
var txts = jQuery(".text");
console.log(txts)
[p.text, p.text, p.text, p.text, prevObject: n.fn.init[1], context: document, selector: ".text", jquery: "2.1.4", constructor: function…]
// dom
var txts = document.getElementsByClassName("text");
console.log(txts);
[p.text, p.text, p.text, p.text, item: function, namedItem: function]
親を指定
// jQuery
var targetEle = jQuery("#uniqBox");
console.log(targetEle.parent());
[div.right, prevObject: n.fn.init[1], context: document, jquery: "2.1.4", constructor: function, selector: ""…]
// dom
var targetEle = document.getElementById("uniqBox");
console.log(targetEle.parentNode);
/* parentNode で親要素が返ってくる
<div class="right">…</div>
*/
嗚呼、closest、嗚呼、closest
jQuery の closest() がすごく便利だったんだけど、jQuery でない方の closest() は IEで使えないっぽい。IEを考えなくてい良い状況ならば使えるかもしれない(参考:Mozilla Developer Network : Element.closest())。スマホでどうなのか、Safariでどうなのかっつー検証する価値はあるな。
// jQuery
var targetEle = jQuery("#uniqBox");
console.log(targetEle.closest('.wrap'));
[div.wrap, prevObject: n.fn.init[1], context: document, jquery: "2.1.4", constructor: function, selector: ""…]
var targetEle = document.getElementById("uniqBox");
console.log(targetEle.closest('.wrap'));
/*
<div class="wrap">…</div>
*/
子を指定 - childNodes
// jQuery
var targetEle = jQuery('.wrap');
console.log(targetEle[0].children);
[div.left, div.right, item: function, namedItem: function]
// dom
var targetEle = document.getElementsByClassName('wrap');
console.log(targetEle[0].childNodes);
[text, div.left, text, div.right, text, item: function]
子を指定 - firstElementChild
// jQuery
var targetEle = jQuery('.wrap :first');
console.log(targetEle);
[div.left, prevObject: n.fn.init[1], context: document, selector: ".wrap :first", jquery: "2.1.4", constructor: function…]
// dom + Element Traversal API
var targetEle = document.getElementsByClassName('wrap');
console.log(targetEle[0].firstElementChild);
/*
<div class="left"></div>
*/
firstChild や nextSibling などの直接的な指定をする場合の注意
parentNode や childNodes はあくまでも Node を取得するため用意されたプロパティです。要素でなく Node ね。
Node には 要素とは違い「空白文字」や「改行」も「テキストノード」として扱われているよ。
そして、firstChild や lastChild、nextSibling や previousSibling はテキストノードも取ってきちゃうプロパティなんだ。
たとえば、さっきのこの例の最初の要素は text になっているよ
// dom
var targetEle = document.getElementsByClassName('wrap');
console.log(targetEle[0].childNodes);
[text, div.left, text, div.right, text, item: function]
この最初の要素を出力してみるとこうなるよ
var targetEle = document.getElementsByClassName('wrap');
console.log(targetEle[0].childNodes[0]);
baseURI: (...)
childNodes: (...)
data: "↵ "
firstChild: (...)
lastChild: (...)
length: 4
localName: null
namespaceURI: null
nextElementSibling: div.left
nextSibling: (...)
nodeName: "#text"
nodeType: 3
nodeValue: "↵ "
ownerDocument: (...)
parentElement: div.wrap
parentNode: (...)
previousElementSibling: null
previousSibling: (...)
textContent: "↵ "
wholeText: "↵ "
こので注目なのが length 。4となっているということは 4文字 存在しているということ。chrome の developer tool で dataプロパティや、textContentプロパティを見ると「改行文字1つ」と「タブ文字3つ」が含まれているのがわかる。
要するに、次の要素までの文字列全てが テキストノードとして扱われている。もし、意図した動き(たとえば要素を取りたいなど)でない場合はこの点に注意が必要。
要素だけを取るためには
children プロパティや Element Traversal API を利用するよ。さっきの例がまさにそれ。
// dom + Element Traversal API
var targetEle = document.getElementsByClassName('wrap');
console.log(targetEle[0].firstElementChild);
/*
<div class="left"></div>
*/
ついでに children を使ってみる
var targetEle = document.getElementsByClassName('wrap');
console.log(targetEle[0].children);
[div.left, div.right, item: function, namedItem: function]
便利だね。
ってことで Element Traversal API の一部を挙げるよ
プロパティ | 取得できる要素 |
---|---|
firstElementChild | 最初の子要素 |
lastElementChild | 最後の子要素 |
nextElementSibling | 次の要素 |
previousElementSibling | 前の要素 |
Selectors API
querySelector() や querySelectorAll() を利用することによってより直感的な dom の取得ができるようになるよ。
// Selectors API
var targetEle = document.querySelector("#nav");
console.log(targetEle);
/*
<div id="nav">…</div>
*/
// Selectors API
var targetEle = document.querySelectorAll("#nav li");
console.log(targetEle);
/*
[li, li, li, li, li, li, item: function]
*/
CSS のセレクター的な指定の仕方ができるのでより直感的だよね
ノードの作成
ノードを作成し反映するには 「作成」→「DOMツリーに追加」の操作が必要です。
* 見ていて胸焼けするコードがちょっと連発します
ノードの作成
var addLiEle = document.createElement('li');
var addAEle = document.createElement('a');
var addImg = document.createElement('img');
属性追加
属性の追加には、「定義」→「値の設定」の操作が必要
var hrefAttr = document.createAttribute('href');
hrefAttr.value = "#";
var srcAttr = document.createAttribute('src');
srcAttr.value = "http://placekitten.com/g/500/500?image=12";
属性を要素に設定
addAEle.setAttributeNode(hrefAttr);
addImg. setAttributeNode(srcAttr);
ノードの追加
addAEle.appendChild(addImg);
addLiEle.appendChild(addAEle);
targetEle.appendChild(addLiEle);
まとめるとこんな感じ
var targetEle = document.querySelectorAll("#side ul")[0];
var addLiEle = document.createElement('li');
var addAEle = document.createElement('a');
var addImg = document.createElement('img');
var hrefAttr = document.createAttribute('href');
hrefAttr.value = "#";
var srcAttr = document.createAttribute('src');
srcAttr.value = "http://placekitten.com/g/500/500?image=12";
addAEle.setAttributeNode(hrefAttr);
addImg. setAttributeNode(srcAttr);
addAEle.appendChild(addImg);
addLiEle.appendChild(addAEle);
targetEle.appendChild(addLiEle);
ノードの削除
ノードの削除には removeChild を使うよ。
var targetEle = document.querySelectorAll("#side ul li")[1];
console.log(targetEle);
var parentEle = targetEle.parentNode;
parentEle.removeChild(targetEle);
querySelector を使って親ノードを指定してもよいよ
var targetEle = document.querySelectorAll("#side ul li")[1];
console.log(targetEle);
var parentEle = document.querySelector("#side ul");
parentEle.removeChild(targetEle);
innerHTML
createElement()やappendChild() の連発はさすがに胸焼けするよね。この胸焼けを解消する1つに innerHTMLプロパティ があるよ。
var newDom = '<li><a href="#"><img src="http://placekitten.com/g/200/300?image=1"></a></li>'+
'<li><a href="#"><img src="http://placekitten.com/400/500?image=3"></a></li>'+
'<li><a href="#"><img src="http://placekitten.com/200/100?image=5"></a></li>';
var targetEle = document.querySelector("#side ul");
targetEle.innerHTML = newDom;
NodeList と StaticNodeList
getElementsByTagName() の様に、ノード取得系メソッドの中には NodeList インターフェースを実装しているものもあるよ。
このインターフェースは item() メソッド と length プロパティ が定義してあり、item() メソッドは引数にインデックスを渡せば DOMツリーて定義されている順の1つにアクセスすることができるよ。
また、length プロパティは その要素数を返すよ。次のコードを見てみよう
//dom
var pTags = document.getElementsByTagName("p");
console.log(pTags);
[p.text, p.text, p.text, p.text, p, p, p, item: function, namedItem: function]
item() という関数を確認できる。length に関しても確認してみよう。
var targetTags = document.getElementsByTagName("p");
console.log(targetTags.length);
5
このポストで使用している サンプル HTML では p 要素が 5つあるので。思い通りの結果が返ってきている。では次のコードはどうでしょう。
var targetWrap = document.getElementById("content");
var targetTags = document.getElementsByTagName("p");
console.log(targetTags.length);
5
var addEle = document.createElement('p');
targetWrap.appendChild(addEle);
console.log(targetTags.length);
6
getElementsByTagName() で取得しなおしていないのに、appendChild で足した分も2回目の targetTags.length で反映されている。
これは NodeList は Live Correction でもあり、常に DOMツリー への参照をを持っているからで、取得時には最新の情報を取得できるわけです。
これに対し、querySelectorAll()で取得されるオブジェクトは StaticNodeList です。StaticNodeList は Live Correction ではないので、オブジェクトに対して加えた変更は HTML ドキュメントに反映されません。
したがって上記の length のテストのようなことを行っても、思い通りの結果は返ってきません。
var targetWrap = document.getElementById("content");
var targetTags = document.querySelectorAll("p");
console.log(targetTags.length);
5 // 初期状態は 5
var addEle = document.createElement('p');
targetWrap.appendChild(addEle);
console.log(targetTags.length);
5 // StaticNodeList なので 要素が追加されても 5
targetTags = document.querySelectorAll("p");
console.log(targetTags.length);
6 // 新たに取得し直すと最新の状態
さしずめよく使いそうなメソッドで NodeList を返すのは、getElementsByTagName()やchildNodesくらいで、逆に StaticNodeList を返すのは querySelectorAll() あたりで。