Learning Objectives
- 說明 DOM 把 HTML 文件看成一棵樹,document 是樹的根
- 用 getElementById、getElementsByClassName、getElementsByTagName 與 querySelector、querySelectorAll 取得元素
- 用 createElement、appendChild、insertBefore 建立並放入新元素
- 用 remove、removeChild、replaceChild 刪除與取代元素,並分辨 textContent 與 innerHTML
DOM:網頁是一棵樹
瀏覽器讀進 HTML 之後,會把它變成一棵由節點(node)組成的樹,叫做 DOM(Document Object Model,文件物件模型)。 JavaScript 不是直接修改 HTML 原始碼,而是修改這棵樹;樹一改變,畫面就跟著改變。
document
└── html
├── head
└── body
├── h3 「色塊」
├── div#green 「green」
└── ul#list
├── li 「蘋果」
└── li 「香蕉」
document是整棵樹的根,存取網頁幾乎都從document開始- 每個 HTML 標籤是一個元素節點(element),標籤裡的文字是文字節點(text node)
- 下面的練習右邊會畫出 DOM 樹:程式改了哪個節點,那一列就會變色
取得元素的方法
| 方法 | 回傳 | 找不到時 |
|---|---|---|
document.getElementById('green') |
一個元素(id 在同一頁應該唯一) | null |
document.getElementsByClassName('red') |
所有 class 含 red 的元素(HTMLCollection) |
長度 0 的集合 |
document.getElementsByTagName('li') |
所有 <li>(HTMLCollection) |
長度 0 的集合 |
document.querySelector('#black .red') |
符合 CSS 選擇器的第一個元素 | null |
document.querySelectorAll('li') |
符合選擇器的所有元素(NodeList) | 長度 0 的集合 |
querySelector 用的就是寫 CSS 時的選擇器:#id、.class、標籤名稱,也可以組合,例如 #black .red(#black 裡面的 .red)、
ul > li(ul 的直接子元素 li)。現在的程式大多用 querySelector/querySelectorAll,寫法和 CSS 一致。
getElementsBy… 也可以從某個元素開始找,只找它底下的元素:document.getElementById('black').getElementsByClassName('red')。
取得元素之後,常用的屬性:
| 屬性 | 意思 |
|---|---|
el.textContent |
元素裡的純文字(設定時一律當成文字,不會解析 HTML) |
el.innerHTML |
元素裡的 HTML(設定時會解析成元素) |
el.style.backgroundColor = 'gold' |
修改行內樣式(CSS 屬性名稱改成駝峰式) |
想一想:取得元素
頁面上有三個 <p class="note">,document.querySelector('.note') 會得到什麼?
document.getElementById('Green') 在頁面只有 id="green" 的元素時會得到什麼?
要取得 id="menu" 底下所有的 <a>,下列哪一個寫法正確?
建立、移動、刪除與取代元素
建立新元素分三步:建立 → 設定內容 → 放進樹裡。還沒放進樹裡之前,畫面上看不到它。
const p = document.createElement('p'); // 1. 建立 <p>(還不在頁面上)
p.textContent = '段落三'; // 2. 設定文字
main.appendChild(p); // 3. 放到 main 的子元素最後面
| 方法 | 作用 |
|---|---|
parent.appendChild(child) |
把 child 放到 parent 的子元素最後面 |
parent.insertBefore(newNode, refNode) |
把 newNode 插到 refNode 的前面 |
parent.append(a, b, '文字') |
新寫法:一次放入多個節點或文字 |
el.remove() |
把元素自己從樹上移除(新寫法) |
parent.removeChild(child) |
從 parent 移除指定的子元素,回傳被移除的元素 |
parent.replaceChild(newNode, oldNode) |
用 newNode 取代 oldNode |
兩個容易忽略的規則:
- 一個節點在樹上只能出現在一個地方。把已經在頁面上的元素再
appendChild到別處,是移動,不是複製(要複製用el.cloneNode(true))。 - 投影片用
document.createTextNode('段落三')建立文字節點再appendChild,效果和設定textContent相同。
textContent 與 innerHTML:innerHTML = '<b>粗體</b>' 會把字串解析成元素;textContent 則原樣當成文字。
要放進頁面的內容如果來自使用者輸入,請用 textContent,否則別人輸入的 HTML 或程式碼可能被執行(這種攻擊叫 XSS)。
想一想:建立與刪除
執行 const p = document.createElement('p'); p.textContent = 'Hi'; 之後,畫面上看得到「Hi」嗎?
#a 已經在頁面上,執行 document.getElementById('box').appendChild(document.getElementById('a')) 會怎樣?
要把使用者在輸入框打的名字顯示在 <span id="name"> 裡,哪一個寫法比較安全?
重點精華回顧
- DOM 把網頁看成一棵節點組成的樹,
document是根;修改 DOM 樹,畫面就跟著改變。 - 取得一個元素:
getElementById('id')、querySelector('CSS 選擇器'),找不到回傳null。 - 取得一組元素:
getElementsByClassName、getElementsByTagName(HTMLCollection)、querySelectorAll(NodeList)。 - 建立元素:
createElement→ 設定textContent→appendChild/insertBefore/append放進樹裡。 - 刪除與取代:
el.remove()、parent.removeChild(child)、parent.replaceChild(new, old);把已在頁面上的元素放到別處是移動。 - 顯示使用者輸入的內容用
textContent,不要用innerHTML。