Help us understand the problem. What is going on with this article?

脱 jQuery の為の Dom 事情

ってことで、超今さら感たっぷりかもだけど 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() あたりで。

Chrowa3
C#だのJSだの
http://webya.in
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした