Wiki_JS_DOM操作 - inoueshinichi/Wiki_Web GitHub Wiki

DOM操作の基本

DOM操作

  • DOM (Document Object Model)は, ブラウザがHTMLを解析した結果作成されるタグのノードツリー.
  • HTML要素をプログラムの対象(オブジェクト)として扱うためのAPI.

DOM関連のJSオブジェクト

説明
Element タグ要素
HTMLCollection タグ要素の配列ライクオブジェクト
NodeList タグ要素、属性、テキストなどすべてを含めたNodeの配列ライクオブジェクト
Attr 属性値のオブジェクト
NamedNodeMap Attrオブジェクトの配列ライクオブジェクト
DOMTokenList class属性 class="cls1 cls2 ... clsN" や rel属性 rel="rel1 rel2 ... relN" を示す配列ライクオブジェクト

HTMLタグ要素オブジェクトの取得方法

メソッド 説明 戻り値型
getElementById id属性を使ってオブジェクトを取得する(idは一意) 主要 Element
getElementsByClassName class属性を使ってオブジェクトを取得する. class属性は複数あるので, オブジェクトのリストを取得する HTMLCollection
getElementsByName name属性を使ってオブジェクトを取得する. getElementsByClassと同じ使用方法. フォームで使用することが多い. NodeList
getElementsByTagName タグ名を使って取得する HTMLCollection
querySelector セレクターを使って取得する Element
querySelectorAll セレクターを使って取得する(複数) NodeList

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属性などを装飾して要素を扱う

style属性の操作

メンバー 説明
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}`)
}

NamedNodeMapオブジェクト

  • 反復可能な配列ライクなオブジェクト
  • 各要素はAttributeオブジェクト
  • Attributeオブジェクトは, attr.nameattr.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) 反転

e.g. 要素のclass属性を変更する

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);
 }
}

e.g. 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) 子要素を削除

新しいタグ要素を作る

  • createElement
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に関するプロパティと属性の違い

  • 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

JSからスタイル属性の操作

  • 下記はStyle属性
  1. style属性
  2. class属性
  • 属性なのでgetAttribute()/setAttribute()が使えるが非推奨.

Style属性

  • rel = 'プロパティ: 値, プロパティ: 値, ....

Class属性

  • class = clazz1 clazz2 clazz3 ...`

JS専用のstyle/classListプロパティで操作

styleプロパティの操作

// HTML
// インラインスタイルに対するアクセス
<div style="color: Red">赤</div>

// JS
let divElem = document.querySelector("div")
divElem.addEventListener('mouseenter', () => {
  divElem.style.color='Yello'
  divElem.style.backgroundColor='Black'
}, false)

classListプロパティの操作

例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)

DOMからテキストを操作する

  • element.textContext = new_text // プレーンテキスト (高速)
  • element.innerHTML = new_elem // HTMLテキスト (低速)
  • 両者とも配下の子要素・テキストを完全に置き換える
  • セキリティ(XCC)対策として,textContextを中心に使用する.
  • textContextは, 子要素のテキストノードのみ取り出す.
  • innerHTMLは, HTML要素を入力することができてしまう.
  • innerHTMLは, 子要素のNodeListすべてを返してしまう.

DOMの更新(再レンダリング)のタイミング

  • DOMツリーにDOMを追加したタイミング
  • 断続的にElementをDOMツリーに追加すると描画コスト(大)
  • DocumentFragmentオブジェクトでまとめて追加するのがよろしい
// DocumentFragmentでコンテンツ(DOMElementの塊)を構築してから
// まとめてDOMツリーに追加する

let frag = document.createDocumentFragment()
...
frag.append(elem1)
...
frag.append(elem2)
...

...

targetElem.append(frag) // DOMの塊を一気に追加
⚠️ **GitHub.com Fallback** ⚠️