Learning Objectives
- 讀寫元素節點的常用屬性:id、className、innerHTML、textContent,以及 nodeType、nodeName、parentNode、childNodes
- 用 hasAttribute、getAttribute、setAttribute、removeAttribute 操作 HTML 屬性,並分辨 HTML 屬性(attribute)與 DOM 屬性(property)
- 用 data-* 與 dataset 存放自訂資料,用 classList 新增、移除與切換 class
- 用 style 修改行內樣式,用 getComputedStyle 讀取元素最後套用的樣式
元素身上帶著哪些資訊
上一課學會了找到元素、建立元素。這一課要處理找到之後的事:讀取與修改元素身上的資訊。
一個元素帶著三類資訊,這一課依序介紹:
| 資訊 | 例子 | 怎麼操作 |
|---|---|---|
| 節點本身的屬性 | 標籤名稱、父節點、子節點、文字內容 | nodeName、parentNode、childNodes、textContent |
| HTML 標籤上的屬性 | href、disabled、data-price |
getAttribute、setAttribute、dataset |
| 樣式 | 顏色、寬度、class | classList、style、getComputedStyle |
DOM 樹裡的每一個東西都是節點:document 是文件節點,每個 HTML 標籤是元素節點,標籤裡的文字是文字節點,
標籤上的 id="main" 這類屬性則是屬性節點。
節點的常用屬性
| 屬性 | 意思 |
|---|---|
el.id |
元素的 id |
el.className |
元素的 class(整串字串,例如 'card big') |
el.innerHTML |
元素裡的所有內容,包含 HTML 標籤 |
el.textContent |
元素裡的純文字,標籤會被去掉 |
node.nodeType |
節點類型的數字:元素是 1、文字是 3、document 是 9(屬性節點是 2) |
node.nodeName |
元素是大寫的標籤名稱('DIV')、文字節點一律是 '#text'、document 是 '#document' |
node.nodeValue |
文字節點是它的文字;元素節點與 document 是 null |
node.parentNode |
父節點 |
node.childNodes |
所有子節點,包含文字節點(下一課會再比較它與 children) |
以 <div id="main" class="card">Hello <b>Uedu</b></div> 為例:
main.innerHTML是'Hello <b>Uedu</b>',main.textContent是'Hello Uedu'main.childNodes有兩個子節點:文字節點'Hello '與元素節點<b>main.firstChild.nodeName是'#text',main.firstChild.nodeValue是'Hello '
想一想:節點的屬性
<p id="p">你好,<em>同學</em></p> 中,p.innerHTML 與 p.textContent 分別是什麼?
document.querySelector('p').nodeName 的值是什麼?
元素原本是 class="card big",執行 el.className = 'title' 之後,class 變成什麼?
讀寫 HTML 屬性
寫在 HTML 標籤上的 href、value、disabled、target 等,叫做 HTML 屬性(attribute)。操作它們用下面幾個方法:
| 方法 | 作用 |
|---|---|
el.hasAttribute('href') |
有沒有這個屬性,回傳 true/false |
el.getAttribute('href') |
讀取屬性值(一律是字串);沒有這個屬性回傳 null |
el.setAttribute('target', '_blank') |
設定屬性;已經有就更新值,沒有就新增 |
el.removeAttribute('disabled') |
移除屬性 |
el.attributes |
元素上所有屬性的集合 |
HTML 屬性與 DOM 屬性不一樣。瀏覽器建立元素物件時,會把大部分 HTML 屬性對應成物件上的屬性(property),
例如 link.href、input.value、button.disabled。兩者多數時候同步,但有幾個差別要記得:
| 情況 | HTML 屬性 getAttribute(…) |
DOM 屬性 el.… |
|---|---|---|
| 使用者在輸入框打字之後 | getAttribute('value') 還是初始值 |
input.value 是目前的內容 |
<a href="/codelab"> |
'/codelab'(原本寫的字串) |
完整網址,例如 'https://uedu.tw/codelab' |
| 名稱不同 | class、for |
className、htmlFor |
布林屬性 disabled |
''(只要屬性存在就生效) |
true/false |
布林屬性有一個常見的陷阱:setAttribute('disabled', 'false') 仍然是停用,因為只要屬性存在就生效。
要啟用按鈕,請用 button.disabled = false 或 button.removeAttribute('disabled')。
自訂資料 data-* 與 classList
自訂屬性:需要把資料放在元素上(商品價格、資料庫編號)時,HTML 規定用 data- 開頭的屬性。
JavaScript 從 el.dataset 讀寫,名稱去掉 data-,連字號後面的字母改成大寫(駝峰式):
| HTML | JavaScript |
|---|---|
data-price="120" |
el.dataset.price(值是字串 '120',計算前要用 Number() 轉換) |
data-user-id="7" |
el.dataset.userId |
執行 el.dataset.status = 'sold' |
元素上出現 data-status="sold" |
setAttribute('target', 'test') 也能加上任何名稱的屬性,但自己發明的名稱可能與未來的 HTML 屬性衝突,自訂資料請用 data-*。
classList:className 會整串覆蓋,classList 則一次只處理一個 class,其他 class 不受影響:
| 方法 | 作用 |
|---|---|
el.classList.add('highlight') |
加上 class(已經有就不重複加) |
el.classList.remove('highlight') |
拿掉 class |
el.classList.toggle('on') |
有就拿掉、沒有就加上;回傳執行之後有沒有這個 class |
el.classList.contains('on') |
有沒有這個 class,回傳 true/false |
外觀的變化(顏色、邊框)建議寫在 CSS 的 class 裡,JavaScript 只負責加上、拿掉 class。這樣樣式集中在 CSS,比較好維護。
想一想:屬性、dataset 與 classList
使用者把輸入框(HTML 是 <input id="n" value="Amy">)的內容改成 Ben 之後,n.getAttribute('value') 與 n.value 分別是什麼?
元素是 <li data-user-id="7">,在 JavaScript 裡怎麼讀到 7?
按鈕目前沒有 on 這個 class,連續執行兩次 btn.classList.toggle('on') 之後,btn.classList.contains('on') 是什麼?
要讓 <button id="b" disabled> 變成可以按,下列哪一個寫法沒有效果?
修改與讀取樣式
修改樣式:el.style 對應元素的 style="…" 行內樣式。CSS 屬性名稱有連字號的,改成駝峰式:
h1.style.color = 'red'; // color
main.style.backgroundColor = 'blue'; // background-color
main.style.setProperty('font-size', '20px'); // setProperty 用 CSS 原本的名稱
main.style.setProperty('color', 'red', 'important'); // 第三個參數可以加上 !important
el.setAttribute('style', 'height: 100px') 也能設定,但會整段覆蓋原本的行內樣式;其他寫法一次只改一個屬性。
讀取樣式:el.style.width 只讀得到行內樣式,寫在 CSS 檔或 <style> 裡的樣式讀不到(是空字串)。
要知道元素最後實際套用的樣式,用 getComputedStyle:
const cs = window.getComputedStyle(main); // 第二個參數可以傳 '::before' 這類偽元素,不需要時省略
console.log(cs.width); // '200px'
console.log(cs.getPropertyValue('background-color')); // 'rgb(238, 242, 246)'
getComputedStyle 回傳的是計算後的值:顏色一律是 rgb(…)、長度一律是 px,而且唯讀,要改樣式還是用 el.style。
常用的樣式屬性:width、height、border、margin、padding、textAlign、color、backgroundColor、display。
同時要改好幾個樣式時,建議寫成 CSS 的 class,再用 classList 切換。
想一想:樣式
CSS 檔裡寫了 #box { color: green; },元素沒有 style 屬性,box.style.color 會得到什麼?
要把元素的 CSS background-color 改成黃色,下列哪一個寫法正確?
重點精華回顧
- 節點的常用屬性:
id、className、innerHTML(含標籤)、textContent(純文字)、nodeType(元素 1、文字 3)、nodeName(元素是大寫標籤名稱、文字是#text)、parentNode、childNodes。 - HTML 屬性用
hasAttribute、getAttribute、setAttribute、removeAttribute操作;getAttribute讀到的是原本寫的字串,DOM 屬性(input.value、link.href)是目前的狀態。 - 布林屬性(
disabled、checked)只要存在就生效,要取消請移除屬性或設成false。 - 自訂資料用
data-*,JavaScript 從dataset讀寫,名稱改成駝峰式,值一律是字串。 - 切換 class 用
classList的add、remove、toggle、contains,不會影響其他 class;className會整串覆蓋。 el.style修改與讀取行內樣式(屬性名稱用駝峰式);getComputedStyle(el)讀取最後實際套用的樣式,顏色是rgb(…)、長度是px。