- DOM (Document Object Model)は, ブラウザがHTMLを解析した結果作成されるタグのノードツリー.
- HTML要素をプログラムの対象(オブジェクト)として扱うためのAPI.
| 型 |
説明 |
| Element |
タグ要素 |
| HTMLCollection |
タグ要素の配列ライクオブジェクト |
| NodeList |
タグ要素、属性、テキストなどすべてを含めたNodeの配列ライクオブジェクト |
| Attr |
属性値のオブジェクト |
| NamedNodeMap |
Attrオブジェクトの配列ライクオブジェクト |
| DOMTokenList |
class属性 class="cls1 cls2 ... clsN" や rel属性 rel="rel1 rel2 ... relN" を示す配列ライクオブジェクト |
| メソッド |
説明 |
戻り値型 |
| getElementById |
id属性を使ってオブジェクトを取得する(idは一意) 主要 |
Element |
| getElementsByClassName |
class属性を使ってオブジェクトを取得する. class属性は複数あるので, オブジェクトのリストを取得する |
HTMLCollection |
| getElementsByName |
name属性を使ってオブジェクトを取得する. getElementsByClassと同じ使用方法. フォームで使用することが多い. |
NodeList |
| getElementsByTagName |
タグ名を使って取得する |
HTMLCollection |
| querySelector |
セレクターを使って取得する |
Element |
| querySelectorAll |
セレクターを使って取得する(複数) |
NodeList |
- NodeListは, 要素ノードだけでなくテキストノードや属性ノードも取得する包括的な体系を持つ
| メンバー |
説明 |
| node.length |
長さ |
| node.item(index) |
要素 |
| node.entries() |
(key,value)形式の反復イテレータを返す |
| node.forEach(callback(value, index, nodeList) { statements...; return ret; }, thisArg) |
それぞれの要素に処理 |
| node.keys() |
keyのリストを返す |
| node.values() |
valueのリストを返す |
| 番号 |
種類 |
| 1 |
タグ要素ノード |
| 2 |
属性ノード |
| 3 |
テキストノード ※ タグ間にある改行や空白はテキストノードと見なされる. |
| 4 |
- |
| 5 |
- |
| 6 |
- |
| 7 |
- |
| 8 |
コメントノード <!-- comment -->
|
| 9 |
文書ノード |
| 10 |
文書型宣言ノード <DOCHTML ~>
|
| 11 |
文書の断片(フラグメント) |
| メンバー |
説明 |
| elem.textContent |
プレーンな文字列として要素を扱う |
| elem.innerHTML |
href属性などを装飾して要素を扱う |
| メンバー |
説明 |
| elem.style.prop [= value] |
ハイフン記法ではなく、キャメル記法で取得する. background-color -> backgroundColor border-top-style -> borderTopStyle |
- floatプロパティのみstyleFloatになる.
| メンバー |
説明 |
| elem.getAttribute(name) -> Attr |
タグ要素の属性値を取得 |
| elem.hasAttribute(name) -> Bool |
タグ要素に属性値が存在するかチェック |
| elem.attributes -> NamedNodeMap |
タグ要素が持つすべての属性値をNamedNodeMapオブジェクトとして取得する(配列ライク) |
| Attr.name |
属性の名前を取得 |
| Attr.value |
属性の値を取得 |
// HTML
<img id="logo" src="https://wings.msn.to/images/wings.jpg"
height="67" width="215" title="WINGSロゴ" alt="WINGSのALT" />
// JS
let pic = document.querySelector("#logo")
// 一つの属性を取得
console.log(pic.getAttribute("title"))
// すべての属性を取得
let /* NamedNodeMap */ attrs = pic.attributes
for (let attr of attrs) {
console.log(`${attr.name}=${attr.value}`)
}
- 反復可能な配列ライクなオブジェクト
- 各要素はAttributeオブジェクト
- Attributeオブジェクトは,
attr.nameとattr.valueメンバを持つ.
| メンバー |
説明 |
| nnm.length |
配列の長さ |
| nnm.getNamedItem(name) |
戻り値はAttributeオブジェクト |
| nnm.setNamedItem(attr) |
- |
| nnm.removeNamedItem(name) |
- |
| nnm.item(index) |
順序の保証は無い |
| nnm.getnamedItemNS(namespace, local) |
指定のローカル名と名前空間を持つノードを取得する |
| nnm.setNamedItemNS(attr) |
- |
| nnm.removeNamedItemNS(namespace, local) |
- |
HTMLCollectionに対するNodeListの違い
- NodeListは, 配下ノードの設定と削除に対応
- NodeListは, 個々のノードにインデックス, 名前の両方でアクセスできる.
DOMTokenListオブジェクト(配列ライクオブジェクト)
| メンバー |
説明 |
| dtl.length |
長さ |
| dtl.item(index) |
要素 |
| dtl.contains(v) |
要素の存在 |
| dtl.add(v) |
追加 |
| dtl.remove(v) |
削除 |
| dtl.toggle(v) |
反転 |
var timer_id;
document.getElementById('start_stop').addEventListener('click', function() {
if (this.innerHTML == 'START') {
start = new Date();
// タイマー開始
timer_id = setInterval(goTimer, 10);
// STOPボタンにする
this.innerHTML = 'STOP'; // html要素を書き換える
this.classList.remove('btn-primary'); // class属性を消す
this.classList.add('btn-danger'); // class属性を追加
} else {
// STARTボタンにする
this.innerHTML = 'START';
this.classList.remove('btn-danger');
this.classList.add('btn-primary');
// タイマーを止める
clearInterval(timer_id);
}
}
<script>
document.getElementById('timer').innerHTML = '00:01:00';
</script>
- document.write()以外を使用する場合,
<head></head>内部,<body></body>の最後,または<script></script>に記述する
Array.prototype.forEach.callでループを回す
- HTMLCollectionとNodeListは
forEachを持っていないので, 下記でループを回す
const elements = document.getElementsByTagName('div')
Array.prototype.forEach.call(elements, element => {
console.log(element)
})
| メソッド |
説明 |
| document.createElement(name) |
指定タグで要素を作成 |
| document.createTextNode('string') |
タグの中身を作成 |
| node.insertBefore(newElement, referenceElement) |
referenceElementの前にnewElementを追加 |
| node.appendChild(newElement) |
最後の子要素として追加 |
| node.removeChild(child) |
子要素を削除 |
var img;
img = document.createElement('img');
img.setAttribute('src', images[0].path); // 属性をつける
document.getElementById('img_unit').appendChild(img); // 子要素として追加
| プロパティ |
説明 |
型 |
| element.parentNode |
親要素 |
Element |
| element.firstElementChild |
最初の子要素 |
Element |
| element.lastElementChild |
最後の子要素 |
Element |
| element.children |
すべての子要素 |
NodeList |
| element.previousElementSibling |
一つ前の要素 |
Element |
| element.nextElementSibling |
一つ後の要素 |
Element |
| メソッド |
説明 |
| element.getAttribute(name) |
指定属性を取得 |
| element.setAttribute(name, value) |
指定属性を設定 |
| element.removeAttribute(name) |
指定属性を削除 |
| element.hasAttribute(name) |
指定属性nameの存在チェック |
- ほとんどのDOMのElementは, 属性と同じプロパティを持つ.
- プロパティと属性は厳密には異なる.
// JS (属性)
let imgs = document.querySelectorAll('img')
for (let img of imgs) {
if (!img.hasAttribute(attr)) {
img.setAttribute(/*attr-name*/, /*attr-value*/)
}
}
// JS (プロパティ)
let url = link.href
link.href = 'https://wings.msn.to/'
// HTML
<form>
<div>
<label for="name">氏名:</label>
<input id="name" type="text" name="name" value="匿名" />
</div>
<input id="btn" type="button" value="送信" />
</form>
// JS
let member = document.querySelector("#name")
document.querySelector("#btn").addEventListener('click', () => {
console.log(member.value) // 氏名の入力値
console.log(member.getAttribute('value') // 初期値: 匿名
}, false)
- bool属性: 属性の有無自体でON/OFFが決まる
- boolプロパティ: 値でON/OFFが決まる
- disabled, selected, checked, multiple
// 状況次第で記述が変化する
disabled = "disabled" // 属性:OFF -> elem.disabled -> false, elem.getAttribute('disabled') -> false
disabled = "" // 属性: NONE -> elem.getAttribute('disabled')は, 属性値が存在する場合のみ`disable`が返る.
disabled // プロパティ: OFF -> elem.disabled -> false
- style属性
- class属性
- 属性なので
getAttribute()/setAttribute()が使えるが非推奨.
rel = 'プロパティ: 値, プロパティ: 値, ....
-
class = clazz1 clazz2 clazz3 ...`
JS専用のstyle/classListプロパティで操作
// HTML
// インラインスタイルに対するアクセス
<div style="color: Red">赤</div>
// JS
let divElem = document.querySelector("div")
divElem.addEventListener('mouseenter', () => {
divElem.style.color='Yello'
divElem.style.backgroundColor='Black'
}, false)
例1
// HTML
<p class="hoge foo bar piyo hoge">テスト</p>
# DOMTokenList: 配列ライクオブジェクト
| 0 | 1 | 2 | 3 | 4 |
| hoge | foo | bar | piyo | hoge |
// JS
let pElem = docuemnt.querySelector('p')
let /* DOMTokenList */ clazzList = pElem.classList
pElem.addEventListener('click', () => {
for (let clazz of clazzList) {
console.log(`${clazz}`)
}
}, false)
例2
// CSS (css/class_list.css)
.highlight {
background-color: 'Yellow'
}
// HTML
<link rel="stylesheet" href="css/class_list.css" />
<div id="my-div">マウスポインタ</div>
// JS
let divElem = document.getElementById("my-div")
divElem.addEventListener('mouseenter', () => {
divElem.classList.add('highlight')
}, false)
element.textContext = new_text // プレーンテキスト (高速)
element.innerHTML = new_elem // HTMLテキスト (低速)
- 両者とも配下の子要素・テキストを完全に置き換える
- セキリティ(XCC)対策として,textContextを中心に使用する.
- textContextは, 子要素のテキストノードのみ取り出す.
- innerHTMLは, HTML要素を入力することができてしまう.
- innerHTMLは, 子要素のNodeListすべてを返してしまう.
- DOMツリーにDOMを追加したタイミング
- 断続的にElementをDOMツリーに追加すると描画コスト(大)
- DocumentFragmentオブジェクトでまとめて追加するのがよろしい
// DocumentFragmentでコンテンツ(DOMElementの塊)を構築してから
// まとめてDOMツリーに追加する
let frag = document.createDocumentFragment()
...
frag.append(elem1)
...
frag.append(elem2)
...
...
targetElem.append(frag) // DOMの塊を一気に追加