テキスト:JavaScriptのDOM操作
DOM操作とは?
DOM : Document Object Modelの略
HTML や XML 文書のためのプログラミングインターフェイスで、ページを表現するため、プログラムが文書構造、スタイル、内容を変更することができます。
HTML文書の内容を動的に変更することができる。
やってみよう
code:domsample.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
function sayAnswer() {
const answer = document.getElementById('answer').innerText;
alert('今日の昼ごはんは' + answer + 'です');
}
</script>
</head>
<body>
<h2>Q.今日の昼ごはんは何ですか?</h2>
<h3>A. <span id="answer">寿司</span></h3>
<button onclick="sayAnswer()">回答</button>
</body>
</html>
※「id="answer"」の中身を変更するとalertの内容も変更されることを確認してください。
DOMツリー(文書ツリー)
xmlやhtmlドキュメントをツリー構造として表現したもの。
文書内のオブジェクトは「ノード」と呼ぶ。
例)domsample.htmlをDOMツリーで表したら。
https://gyazo.com/88b8ee8c8726a45a819e61e0344f23a5
主要メソッドについて
絶対的操作例
code:dom-method1.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM sample1</title>
<script>
function getTextById() {
const recipeName = document.getElementById('recipe').innerText;
console.log(recipeName);
}
function getTextByClass() {
const vegetables = document.getElementsByClassName('vegetable');
for(let i = 0; i < vegetables.length; i++) {
console.log(vegetablesi.innerText); }
}
function getTextByTag(tag) {
const elements = document.getElementsByTagName(tag);
for(let i = 0; i < elements.length; i++) {
console.log(elementsi.innerText); }
}
function getHtmlByTag(tag) {
const ulElements = document.getElementsByTagName(tag);
for(let i = 0; i < ulElements.length; i++) {
console.log(ulElementsi.innerHTML); }
}
function getTextContentByTag(tag) {
const ulElements = document.getElementsByTagName(tag);
for(let i = 0; i < ulElements.length; i++) {
console.log(ulElementsi.textContent); }
}
</script>
</head>
<body>
<h3>ノードを指定する</h3>
<br>
<h3 id = 'recipe'>カレー</h3>
カレーの
材料について
<ul>
<li class="vegetable">玉ねぎ</li>
<li class="vegetable">にんじん</li>
<li class="vegetable">じゃがいも</li>
<li id="meat">お肉</li>
<li>ルー</li>
</ul>
<button onclick="getTextById()">「recipe」id要素のテキスト</button><br>
<button onclick="getTextByClass()">「vegetable」クラスのテキスト</button><br>
<button onclick="getTextByTag('li')">「li」タグのテキスト</button><br>
<button onclick="getTextByTag('ul')">「ul」タグのテキスト</button><br>
<button onclick="getHtmlByTag('ul')">「ul」タグのHTML</button><br>
<button onclick="getTextContentByTag('ul')">「ul」タグのtextContent</button><br>
</body>
</html>
getElementByIdメソッド
指定したIDを持つノードを取得する。
id属性はHTML文書の中で一つである必要がある。
※ 試してみた限りは、同じid属性が存在した場合は文書の最初に出てきたノード以外は無視される。
getElementsByClassNameメソッド
指定した値をclass属性値に持つノードを取得する。
戻り値は配列(正確には異なるが扱い方はほぼ同じ「arguments のような配列風のオブジェクト」)
getElementsByTagNameメソッド
指定したタグ名を持つノードを取得する。
戻り値はgetElementsByClassNameメソッドと一緒。
戻り値について参考。
参考) innerText/innerHTML/textContentの違い
相対的操作例
サンプル
code:dom-method2.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM sample2</title>
<script>
function getParentElement() {
const currentNode = document.getElementById('meat');
const result = currentNode.parentElement;
console.log(result);
}
function getPreviousElementSlibling() {
const currentNode = document.getElementById('meat');
const result = currentNode.previousElementSibling;
console.log(result);
}
function getNextElementSlibling() {
const currentNode = document.getElementById('meat');
const result = currentNode.nextElementSibling;
console.log(result);
}
function getChildren() {
const ulElements = document.getElementsByTagName('ul')0; const resultArray = ulElements.children;
for(let i = 0; i < resultArray.length; i++) {
console.log(resultArrayi); }
}
function getFirstElementChild() {
const currentNode = document.getElementsByTagName('ul')0; const result = currentNode.firstElementChild;
console.log(result);
}
function getLastElementChild() {
const currentNode = document.getElementsByTagName('ul')0; const result = currentNode.lastElementChild;
console.log(result);
}
function getNode() {
const currentNode = document.getElementById('recipe');
const result = currentNode.nextSibling;
console.log(result);
}
function getElement() {
const currentNode = document.getElementById('recipe');
const result = currentNode.nextElementSibling;
console.log(result);
}
</script>
</head>
<body>
<h3>相対的にノードを指定する</h3>
<br>
<h3 id = 'recipe'>カレー</h3>
カレーの
材料について
<ul>
<li class="vegetable">玉ねぎ</li>
<li class="vegetable">にんじん</li>
<li class="vegetable">じゃがいも</li>
<li id="meat">お肉</li>
<li>ルー</li>
</ul>
<button onclick="getParentElement()">お肉から親要素</button><br>
<button onclick="getPreviousElementSlibling()">お肉から前(兄)要素</button><br>
<button onclick="getNextElementSlibling()">お肉から後(弟)要素</button><br>
<button onclick="getChildren()">「ul」の子要素全て</button><br>
<button onclick="getFirstElementChild()">「ul」の子要素の中で一番最初</button><br>
<button onclick="getLastElementChild()">「ul」の子要素の中で一番最後</button><br>
<button onclick="getNode()">カレー(id=recipe)のnextSibling</button><br>
<button onclick="getElement()">カレー(id=recipe)のnextElementSibling</button><br>
</body>
</html>
https://gyazo.com/2f4f87baa05331f43ae21144d94b9ee7
Sibling : 兄弟
相対的操作時のデメリット
HTMLの文書を書き換えると、想定していた処理にならない場合がある。
属性値の取得・設定
code:dom-method3.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM sample3</title>
<script>
function gerLink1() {
const linkName = document.getElementsByTagName('a')0.href; console.log(linkName);
}
function setLink1() {
}
function gerLink2() {
const linkName = document.getElementsByTagName('a')0.getAttribute('href'); console.log(linkName);
}
function setLink2() {
}
</script>
</head>
<body>
<h3>属性値の取得・設定</h3>
<br>
<h3 id = 'recipe'>カレー</h3>
カレーの
材料について
<ul>
<li class="vegetable">玉ねぎ</li>
<li class="vegetable">にんじん</li>
<li class="vegetable">じゃがいも</li>
<li id="meat">お肉</li>
<li>ルー</li>
</ul>
<button onclick="gerLink1()">要素ノード.プロパティ名で値を取得</button><br>
<button onclick="setLink1()">要素ノード.プロパティ名で値を変更</button><br>
<button onclick="gerLink2()">getAttributeで値を取得</button><br>
<button onclick="setLink2()">setAtrributeで値を取得</button><br>
</body>
</html>
属性値、aタグのhref以外だとimgタグのsrc属性とか。
要素ノード.プロパティ名
必ずしも属性値とプロパティ名が一致するとは限らない。
例)class属性は、「.className」というプロパティ名になる。属性値をそのまま使いたい場合は、getAttribute ・ setAtrributeを使う。
ノードの追加・削除・置換
code:dom-method4.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM sample4</title>
<script>
function createNode() {
// 追加するノードの作成
const newElement = document.createElement('li');
newElement.innerText = 'しょうゆ'
// const text = document.createTextNode('しょうゆ');
// newElement.appendChild(text);
// 追加
const ulElement = document.getElementsByTagName('ul')0; ulElement.appendChild(newElement);
// ulElement.after(newElement);
// ulElement.before(newElement);
}
function deleteNode() {
const targetElement = document.getElementById('meat').nextElementSibling;
targetElement.remove();
// 子要素のノードを削除する場合 removeChild
// const parentElement = document.getElementsByTagName('ul')0; // parentElement.removeChild(document.getElementById('meat').nextElementSibling);
}
function replace() {
const newElement1 = document.createElement('h3');
newElement1.innerText = '肉じゃが'
const newExplain = document.createTextNode('肉じゃがの材料について');
const newElement2 = document.createElement('a');
newElement2.innerText = '肉じゃがのWiki'
newElement2.setAttribute('target', '_brank');
newElement2.setAttribute('rel', 'noopener noreferrer');
const explainNode = document.getElementById('recipe').nextSibling;
explainNode.replaceWith(newExplain);
const recipeNode = document.getElementById('recipe');
recipeNode.replaceWith(newElement1);
const aNode = document.getElementsByTagName('a')0; aNode.replaceWith(newElement2);
}
</script>
</head>
<body>
<h3>ノードの追加・削除</h3>
<br>
<h3 id = 'recipe'>カレー</h3>
カレーの
材料について
<ul>
<li class="vegetable">玉ねぎ</li>
<li class="vegetable">にんじん</li>
<li class="vegetable">じゃがいも</li>
<li id="meat">お肉</li>
<li>ルー</li>
</ul>
<button onclick="createNode()">ノードの追加</button><br>
<button onclick="deleteNode()">ノードの削除</button><br>
<button onclick="replace()">ノードの置換</button><br>
</body>
</html>
追加の流れ
document.createElementで新しいノードを作成
追加する位置の親のノードを探索
appendChildで追加
↑
親要素から追加する情報の方が多かった。
同じ階層で追加ならafterやbefore
-
削除の流れ
削除するノードを探索
removeで削除
親要素から子要素を削除する場合removeChild
-
置換の流れ
document.createElementで新しいノードを作成
置換する位置のノードを探索
replaceWithで置換
親要素から子要素を置換する場合replaceChild
-
演習
以下のHTMLにおいてボタン内の関数にJavaScriptで処理を追加してください。
ただしHTMLの文書(CSS)を変更してはいけません。(idやclassを直接書いてはいけない、という意味)
※ 2021/10/2時点での情報
ボタン1 午前の試験範囲を青文字に変更する処理。
ボタン2 選択問題で選択予定のものを赤文字に変更する処理。
決まっていない場合は以下を赤文字にしてください。
ソフトウェア・ハードウェア
データベース
経営戦略・企業と法務
ボタン3 「基本情報の試験範囲」の下に公式サイトのリンクを追加する処理
code:ex-dom.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="ex-dom.css">
<script>
function btn1() {
// ここを編集
}
function btn2() {
// ここを編集
}
function btn3() {
// ここを編集
}
</script>
<title>DOM操作の演習</title>
</head>
<body>
<h3 id="title">基本情報の試験範囲</h3>
<h4>午前</h4>
<ul id="morning-list">
<li>テクノロジ系</li>
<li>マネジメント系</li>
<li>ストラテジ系</li>
</ul>
<h4>午後</h4>
<ul id="afternoon-list">
<li>セキュリティ</li>
<li>ソフトウェア・ハードウェア</li>
<li>データベース</li>
<li>ネットワーク</li>
<li>ソフトウェア設計</li>
<li>プロジェクトマネジメント</li>
<li>サービスマネジメント</li>
<li>システム戦略</li>
<li>経営戦略・企業と法務</li>
<li>データ構造及びアルゴリズム</li>
<li>ソフトウェア開発</li>
</ul>
<button onclick="btn1()">ボタン1</button>
<button onclick="btn2()">ボタン2</button>
<button onclick="btn3()">ボタン3</button>
</body>
</html>
code:ex-dom.css
.blue-text {
color: blue;
}
.red-text {
color: red;
}
回答