Web開發學習筆記11 — DOM、Attribute與Property的差異


Posted by Teagan Hsu on 2020-12-28

文件物件模型(Document Object Model, DOM)是什麼?

簡單來說就是將每個HTML的標籤都物件化,讓JavaScript可以訪問和更改HTML的元素,舉例來說,我們可以點擊按鈕來改變文字。當網頁載入時,瀏覽器會自動幫我們創立這個頁面的DOM。

為何我們需要DOM?

在以前瀏覽器大亂之時,每家瀏覽器的規則都不一樣,讓工程師非常頭疼,直到W3C訂立出了DOM的標準,讓瀏覽器公司可以依照這個準則去設計瀏覽器。

DOM tree與節點(node)

DOM將標籤定義成物件後,會形成一個樹狀結構,也就是DOM tree。
這是我們的HTML檔:

表示為DOM tree後會變成這樣,每個標籤就是一個節點,最上層的是Document,往下延伸出Element,再往下還有TextAttribute

Selecting DOM Elements

  1. getElementById:根據元素ID選取。
  2. getElementsByTagName:根據標籤名稱選取。(例如< p >、< h1 >....)
  3. getElementsByClassName:根據Class選取。
  4. getElementsByName:根據Name選取。
  5. querySelector(selectors):根據selectors選取。(可使用範圍很廣)

HTMLCollection

HTMLCollection是元素(Element)的集合,並提供可選取集合成員的方法與屬性。
如果我們使用getElementById去選取h1,console中會出現HTMLCollection,它看起來長得有點像Array(有length屬性和長得像index的item()),但實質上它是Object,所以不能用陣列方法處理(像是map()filter()...),但用for可以遍歷。

NodeList

NodeList也是物件,是節點的集合。不像是HTMLCollection只能儲存Element,NodeList同時包含了TextAttribute...等等。可以看到圖片上NodeListHTMLCollection多包涵了6個Text

如何得到HTMLCollection和NodeList物件?

  • HTMLCollection:childrengetElementsByTagName()
  • NodeList:childNodesquerySelectorAll()

注意:

  • 使用childNodes回傳的NodeList為動態集合(live collection)。
  • 使用querySelectorAll()回傳的NodeList為靜態集合(static collection)。
  • HTMLCollection具有即時性(live),如果document物件發生改變,HTMLCollection物件也會跟著做變化。

textContent與innerText

textContentinnerText皆是DOM的屬性,兩者名字很像,容易混淆。
差異:

textContent

  • 標準化用法
  • get所有元素的內容,包含<script><style>元素
  • 返回每個節點的元素

innerText

  • 非標準化用法
  • 只顯示“人眼可見”的元素,隱藏的內容不會顯示出來。
  • 在計算中加入了CSS樣式,知道Styling長怎樣,但也會造成應用程序下降

示範textContent與innerText的差異結果,使用以下HTML:

<body>

    <p id='para'>
        <style>
            #para {
                color: blue
            }
        </style>
        HI!!!!!!<br>I'm your new classmate</p>

    <h4>textContent:</h4>
    <textarea id="textContent" rows="4" cols="20" readonly>...</textarea>
    <h4>innerText:</h4>
    <textarea id="innerText" rows="4" cols="20" readonly>...</textarea>

</body>

使用以下JavaScript:

let para = document.querySelector('#para');
let textContent = document.querySelector('#textContent');
let innerText = document.querySelector('#innerText');

//textContent顯示:
textContent.value = para.textContent;
<!--"

            #para {
                color: blue
            }

        HI!!!!!!I'm your new classmate"-->


//innerText顯示:
innerText.value = para.innerText;
<!--"HI!!!!!!
I'm your new classmate"-->

頁面顯示結果:

Attribute與Property的差別

當瀏覽器加載頁面時,它會解析HTML並從中生成DOM property。 大多數形況下,HTML attribute會自動成為DOM property。
舉例來說,如果tag為<div class = 'introduction'>,則DOM property也會具有div.class = 'introduction'
但是必須注意attribute與property的映射不是一對一的,下面會詳細介紹它們的相異之處。

Attributes
Attributes是我們寫在HTML的屬性,像是<div type = 'text'>
Properties
Properties是存在在DOM objects中的屬性,像是div.type

Attribute要符合相對應的Element class

當我們的element有符合標準的的attribute時,相對應的property就會生成。但是當element不符合標準時,相對應的property就不會生成。
像是input元素有type這個attribute(符合標準),但body元素沒有type(不符合標準),所以不會生成相應的property。

那我們要如何取得不符合標準的attribute呢?

以下有幾種方法:

  • el.hasAttribute() - 檢查這個attribute有沒有存在
  • el.getAttribute() - 取得attribute
  • el.setAttribute() - 設定attribute的value
  • el.removeAttribute() - 移除attribute
  • el.attributes - 將該元素所有attribute的節點返回為一個集合

通常Attribute與Property的之間是同步的

通常Attribute與Property的之間是同步的,意思是說當一個標準的attribute變更,與它相對應的property也會自動更新。
以下範例:

//attribute => property

let p = document.querySelector('p');
p.setAttribute('id', 'paragraph');
p.id;    //"paragraph"

//property => attribute
p.id = 'newParagraph';
p.getAttribute('id');    //"newParagraph"

但也有例外的情況,像input.value只能同步attribute => property。

<input id = 'input' type = 'password'>Password

<script>
//attribute => property
input.setAttribute('value', 'password');
input.value;    //password

//property =X> attribute
input.value = 'newPassword';
input.getAttribute('value');    //password(沒有更新成newPassword)

</script>

DOM property的值通常為string但也有例外的狀況,例如:當利用element.property取得style屬性時是回傳object,另一種情況:用element.getAttribute取得style的屬性時,會回傳string。

<body id="body" style="background-color:#000;">
</body>

<script>
console.log(body.getAttribute('style'));
//background-color:#000;

console.log(document.body.style);
//CSSStyleDeclaration {0: "background-color", alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: "", …}
</script>

相同概念的舉例,input.checked的property是回傳boolean值,而使用getAttribute則會回傳空字串。

<input id = "input" type = "checkbox" checked>Check Box

<script>
console.log(input.getAttribute('checked'));  //empty string
console.log(input.checked);   //true
</script>

上述提到property大多為string。但少數情形下,儘管property的type是string,attribute也不一定是string。
經典的例子就是URL,看以下的例子可以發現element.href回傳的都是完整URL。這是因為
W3C的規定,href property一定要是完整形式的連結。

<a href='/Users/teagan/Desktop/DOM.html'>DOM</a>
<a href='#1'>first-paragraph</a>

<script>
//選取第一個a連結
let dom = document.querySelectorAll('a')[0];

console.log(dom.getAttribute('href'));
// /Users/teagan/Desktop/DOM.html

console.log(dom.href);
// file:///Users/teagan/Desktop/DOM.html


//選取第二個a連結
let google = document.querySelectorAll('a')[1];

console.log(google.getAttribute('href'))'
// #1

console.log(google.href);
// file:///Users/teagan/Desktop/DOM.html#1
</script>

非標準屬性、數據

有時我們使用非標準屬性去自定義數據,或是為JavaScript標記(mark)HTML元素。
注意,使用data-*來自定義屬性,這讓JS或CSS更加容易讀取屬性的值。

範例一:取得屬性的值

<body id="body" data-product-category="noodles">

<script>
//使用element.dataset.property取得(注意-(dash)要轉換成camelCase
console.log(document.body.dataset.productCategory)  //noodles

//或使用getAttribute
console.log(document.body.getAttribute('data-product-category'))  //noodles
</script>

範例二:變更字的顏色與內容

<div class="order" data-order-state="new">
  A new order.
</div>
<style>
  .order[data-order-state="new"] {
    color: black;
  }

  .order[data-order-state="pending"] {
    color: green;
  }
</style>
<script>
order.dataset.orderState = "pending";
let padding = document.querySelector('.order');
padding.textContent = 'A padding order';
</script>

執行JS會改變字的顏色與內容:


參考資料:

  1. Using data attributes - MDN
  2. getAttribute() versus Element object properties?
  3. Attributes and properties - javascript.info
  4. Difference between textContent vs innerText
  5. DOM
  6. HTMLCollection與NodeList
  7. HTMLCollection - MDN
  8. NodeList - MDN

#javascript #DOM #Attribute #Property







Related Posts

「多個參數」不好嗎?

「多個參數」不好嗎?

filter

filter

Day 146

Day 146


Comments