JavaScript - DOM

DOM

DOM (document object modal) 是一種應用程式介面 (API),能以樹狀結構描述頁面元素在瀏覽器的組成結構。DOM 定義特性和方法,供程式存取和變更模型中的每個物件。DOM 讓文檔可以採取為由節點和物件 (包含屬性和方法的物件)的組成結構。簡言之,DOM 連接了網頁、腳本及程式語言。

參考來源一

下面範例,HTML 裡面每個元素、屬性都代表著其中一個節點

1
2
3
4
5
6
<html>
<head>my title</head>
<body>
<h1>Header</header>
</body>
</html>

DOM 操作基本語法

選取元素

getElementById():
利用元件的 id 屬性值,由於元素的 ID 在頁面中具有唯一性,因此是一個高效查找特定元素的方法。

1
var first = document.getElementById("first");

querySelector():

1
<h1 id="titleId" class="special"></h1>

利用 CSS 選擇器尋找第一個符合條件的元件,id 用#,class 用.。

1
2
var first = document.querySelector("#titleId");
var first = document.querySelector(".special");

querySelector 也可以用來選擇下一層的元素,下面例子就會選取到 em 這個元素。

1
<h1 id="titleId" class="titleClass"><em>123</em></h1>
1
document.querySelector(".titleClass em");
  • 選取多個標籤

getElementsByClassName():
選取具有指定 Class 屬性值的所有元件。
選取具有指定標籤名稱的所有元件。會取得一個類似陣列(array-like) 的
node-list (節點列表),可運用 index 及 length 查找,但不能運用其他陣列方法。

1
2
<div class="titleClass">1</div>
<div class="titleClass">2</div>
1
var first = document.getElementsByClassName("titleClass")[0];

document.getElementsByTagName():
選取具有指定標籤名稱的所有元件。會取得一個類似陣列(array-like) 的
node-list (節點列表),可運用 index 及 length 查找,但不能運用其他陣列方法。

1
2
<p>1</p>
<p>2</p>
1
2
3
4
5
6
7
8
9
10
var first = document.getElementsByTagName("p");
//在ES6前
first.forEach(function (item) {
console.log(item); //Uncaught TypeError: items.forEach is not a function
});
//ES6
const betterItems = [...first];
betterItems.forEach(function (item) {
console.log(item);
});

document.querySelectorAll():
利用選擇器選擇所有符合條件的元件

1
2
<p>1</p>
<p>2</p>
1
var first = document.querySelectorAll("p")[0];

遍歷 DOM

  • 獲取子元素
1
2
3
4
5
6
7
<ul id="result">
<li>apple</li>
<li>orange</li>
<li>banana</li>
<li>pear</li>
<li>tomato</li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//childNode 會把空白字元當做一個文字節點一同回傳
var result = document.querySelector("#result");
var allChildren = result.childNodes;
console.log(allChildren);

![](https://i.imgur.com/H0UYxUZ.png)
// children 會回傳父節點的子元素集合
var children = result.children;
![](https://i.imgur.com/sVXAFe7.png)

//firstChild,lastChild
console.log(result.firstChild);
console.log(result.lastChild);

也會把空白字元當做文字節點一同回傳
![](https://i.imgur.com/mTVgNlh.png)
  • 獲取父元素
1
2
3
4
5
6
<div id="result">
<h1>hello world</h1>
<div class="second">
<h2>second heading</h2>
</div>
</div>
1
2
3
const heading = document.querySelector("h2");
console.log(heading.parentElement);
![](https://i.imgur.com/BI9EXQK.png)
  • nextSibling、previousSibling)

回傳當前節點的前一個或後一個兄弟節點,若中間有空白字元,則可能回傳的是空白字元,沒有則回傳 null。

1
2
3
4
5
6
<ul id="result">
<li class="first">first</li>
<li>listitem 2</li>
<li>listitem 3</li>
<li class="last"></li>
</ul>
1
2
3
4
5
6
7
var first = document.querySelector(".first");
var second = first.nextSibling.nextSibling;
console.log(second);
![](https://i.imgur.com/A7AxrgC.png)
var last = document.querySelector(".last");
console.log(last.nextSibling.nextSibling);
![](https://i.imgur.com/ZYUmq6c.png)
  • nextElementSibling、previousElementSibling
    跳過空白字元,立即回傳下一個或上一個兄弟節點
1
2
var last = document.querySelector(".last");
console.log(last.previousElementSibling);
  • nodeValue、textContent
    nodeValue 回傳當前節點的值。會把空白字元當做一個文字節點一同回傳。

設定/取得屬性值

要獲取屬性當下的值可使用 getAttribute();移除某個屬性 removeAttribute()。

1
2
3
4
5
<ul>
<li class="first" id="special">I have class of first</li>
<a href="first.html" id="link">random link</a>
<li>I have no attributes</li>
</ul>
1
2
3
4
5
6
const first = document.querySelector(".first");
const classValue = first.getAttribute("id");
console.log(classValue); // special
const link = document.getElementById("link");
const showLink = link.getAttribute("href");
console.log(showLink); //first.html

setAttribute(name, value) 設定指定元素的屬性值,如果屬性值已存在就更新該值,否則使用指定名稱和值添加一個新的屬性。

1
2
3
const last = link.nextElementSibling;
last.setAttribute("class", "first");
last.textContent = "I have class a first";

插入元素至指定位置

  • insertBefore

// 語法 insertBefore(‘element’,’location’)

1
2
3
<div id="result">
<h1 class="colors">i am the child of result</h1>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
.colors {
background: red;
color: white;
}

.blue {
color: blue;
}

.text {
font-size: 2rem;
letter-spacing: 1rem;
}
1
2
3
4
5
6
7
8
9
10
11
12
const result = document.querySelector("#result");
const first = document.querySelector(".colors");
const bodyDiv = document.createElement("div");
const text = document.createTextNode("a simple body div");
bodyDiv.appendChild(text);
document.body.insertBefore(bodyDiv, result);

const heading = document.createElement("h2");
const headingText = document.createTextNode("dynamic heading");
heading.appendChild(headingText);
heading.classList.add("blue");
result.insertBefore(heading, first);

  • 替代子元素
    用指定的節點替換當前某一個子節點
1
2
3
4
5
6
// 語法   replaceChild(新的節點,要被替換的舊節點);
const smallHeading = document.createElement("h6");
const smallText = document.createTextNode("Iam small heading");
smallHeading.classList.add("colors");
smallHeading.appendChild(smallText);
document.body.replaceChild(smallHeading, bodyDiv);

replaceChild、replace

兩種移除子元素的方式。

選定父層,直接移除所有子元素

1
2
const result = document.querySelector("#result");
result.remove();

選定父層與指定子元素

1
2
3
const result = document.querySelector("#result");
const heading = result.querySelector("h1");
result.removeChild(heading);

新增/移除類別屬性

1
2
3
<h1 id="first" class="colors">first element</h1>
<h1 id="second">second element</h1>
<h1 id="third">third element</h1>
  • className
    後面新增的類別屬性會覆蓋前一個新增的
1
2
3
const second = document.getElementById("second");
second.className = "colors";
second.className = "text";


解決辦法是寫在一起

1
second.className = "colors text";
  • classList

或使用 classList 新增類別屬性

1
2
3
4
second.classList.add("colors");
second.classList.add("text");
//用逗點隔開的新增法
second.classList.add("text", "colors");

使用 classList 移除類別屬性

1
second.classList.remove("text");

使用 contains()方法檢查類別屬性是否存在

1
2
3
4
5
6
let result = third.classList.contains("color");
if (result) {
console.log("yes color");
} else {
console.log("does not have the class");
}

innerHTML、textContent

innerHTML 的方式會讓瀏覽器將字串解析為 HTML 的屬性,帶有 HTML 屬性作用,如果用 <script> 寫一段攻擊是會有作用的。

textContent 是解析為純文字,比較安全。

點擊第一個連結會執行跳出視窗動作,而點擊第二個連結不會執行

1
<div id="test"></div>
1
2
3
4
5
6
7
8
9
10
let test = document.querySelector("#test");
let linkText = "Link to page";
let maliciousCode = 'javascript:alert(\'test\');" style="color:red;"';

test.innerHTML = ' <a href=" ' + maliciousCode + ' "> ' + linkText + "</a><br>";

let linkTag = document.createElement("a");
linkTag.textContent = linkText;
linkTag.href = maliciousCode;
test.appendChild(linkTag);

新增元素

createElement()從字面看就是增加元素,但是只有單純新增元素,如果需要修改內容或是設定屬性還需要用到上面提到的其他設定值,像是 textContent、setAttribute。
例如創見一個新的

插入到 ID“div1”的元素前。

1
2
3
4
5
6
7
8
<html>
<head>
<title>||Working with elements||</title>
</head>
<body>
<div id="div1">The text above has been created dynamically.</div>
</body>
</html>
1
2
3
4
5
6
7
8
9
//創一個新的 div 元素
let newDiv = document.createElement("div");
//新增一些文字節點
let newContent = document.createTextNode("Hi there and greetin
//把文字節點新增到div元素內
newDiv.appendChild(newContent);
// 新的元素和它的文本添加到 DOM 中
let currentDiv = document.getElementById("div1");
document.body.insertBefore(newDiv, currentDiv);

比較用 Javascript 操控 HTML 的兩個方法

innerHTML:

  • 方法:直接插入 DOM 結構中
  • 優點:效能快
  • 缺點:資安風險,
    createElement:
  • 方法:以 DOM 節點來處理
  • 優點:安全性高

缺點:效能差

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2020 CYC'S BLOG All Rights Reserved.

UV : | PV :