網頁物件 (DOM)
早期 Internet Explorer 與 Netscape Navagator 兩大瀏覽器最主要的競爭方式,就是搶先實現出更多可程式化標籤的功能,搶先的結果演變成各自用不同的寫法來實作,導致互不相容,於是 W3C 制定了 DOM (Document Object Model) 統一規範。
DOM 的原理,就是先將 HTML 這類標記文件的結構,用程式設計常見的「樹狀結構」來展現,並制定一套函式介面來存取樹裡面的每個枝節,來達成一致寫法的規範標準。
因此學習 DOM,首先要知道標記文件變成樹狀結構後長怎樣,然後熟練相關的函式來控制這結構當中你想要的節點,這樣就能用程式來操作它。
換句話說,藉由 DOM 讓 JavaScript 得以完全掌控住 HTML 網頁的每個組成,這樣就能透過程式設計的寫法來修改整張 HTML 網頁的內容,完全達成「動態網頁設計」的目的;網頁不再寫死的,而是可以通過程式語言靈活變動的。
節點
在 DOM 中,標籤(tag)、標籤夾起來的內容(text)、屬性(attribute)、<!-- --> 註解(comment)、& 之類的實體(entity),都是節點(node)。
DOM 提供 Node 介面做為共通的屬性和方法,然後視功能需要繼承出其它介面,例如 Element 介面來處理標籤和屬性之間的關係。
appendChild(節點) | 加入節點到這個節點。 |
attributes | 傳回所有屬性。 |
childNodes | 所有子節點。 |
cloneNode() | 複製並傳回這個節點。 |
currentScript | 取得執行程式的 SCRIPT 標籤節點。 |
firstChild | 傳回第一個子節點。 |
hasAttributes() | 檢查這個節點是否有屬性。 |
hasChildNodes() | 檢查這個節點是否有子節點。 |
insertBefore(新節點,子節點) | 將新節點插入在子節點前。 |
lastChild | 傳回最後一個子節點。 |
nextSibling | 傳回這個節點的下一個節點。 |
nodeName | 傳回節點名稱。 |
nodeType | 傳回節點類型。 |
nodeValue | 存取節點內容。 |
parentNode | 傳回這個節點的父節點 |
previousSibling | 傳回這個節點的上一個節點 |
removeChild(節點) | 刪除子節點。 |
replaceChild(新節點,子節點) | 用新節點取代子節點。 |
傳回多個節點時會取得 NodeList 物件,當陣列物件看待即可,透過索引值直接取得裡面的 Node,或使用 length 取得節點數量。
產生新節點:
document.createElement(標籤名稱) | 產生標籤節點 |
document.createText(文字內容) | 產生內容節點 |
document.createAttribute() | 產生屬性節點 |
document.createComment() | 產生註解節點 |
document.createDocumentFragment() | 產生空白節點樹 |
節點類型:
ELEMENT_NODE | 1 |
ATTRIBUTE_NODE | 2 |
TEXT_NODE | 3 |
CDATA_SECTION_NODE | 4 |
ENTITY_REFERENCE_NODE | 5 |
ENTITY_NODE | 6 |
PROCESSING_INSTRUCTION_NODE | 7 |
COMMENT_NODE | 8 |
DOCUMENT_NODE | 9 |
DOCUMENT_TYPE_NODE | 10 |
DOCUMENT_FRAGMENT_NODE | 11 |
NOTATION_NODE | 12 |
來看些範例,首先是插入節點,例如在 BODY 標籤的開頭與結尾,各加入 HR 標籤:
要注意的是,空白和換行也算一個節點!所以 insertBefore() 第二參數使用 childNodes[索引值]
時,必須連空行節點也數進去!
然後是節點樹,用它來增加元素,再把節點樹增加到網頁,避免頻繁更新整個頁面的 DOM 結構,降低運算開銷:
元素
HTML 的標籤 (Tag) 在 DOM 改稱為元素 (Element),底下是 Element 介面的屬性和方法:
children | 所有子元素。 |
getAttribute(屬性) | 傳回屬性值。 |
getAttributeNode(屬性) | 傳回屬性節點。 |
hasAttribute(屬性) | 檢查是否有某個屬性。 |
removeAttribute(屬性) | 以名稱刪除屬性。 |
removeAttributeNode(屬性節點) | 以節點刪除屬性。 |
setAttribute(屬性,值) | 新增或設定屬性。 |
setAttributeNode(屬性節點) | 新增屬性。 |
tagName | 傳回標籤名稱。 |
由於網頁是由標籤和屬性構成,因此元素的使用,大多不在產生新的元素,而是取得現成的元素節點,也就是剖析文件!底下是 DOM 提供的方法,可用於 XML:
document.getElementById('ID 屬性')
document.getElementsByClassName('CLASS 屬性')[索引值]
document.getElementsByTagName('標籤名稱')[索引值]
document.querySelector('CSS 選擇器')
document.querySelectorAll('CSS 選擇器')[索引值]
也可以用 BOM 直接取得 HTML 的節點,但這不是 DOM 標準,不適用於 XML:
document.body | BODY 元素節點 |
document.currentScript | 取得執行程式的 SCRIPT 元素節點 |
document.documentElement | 根元素,也就是 HTML 元素節點 |
document.embeds[索引值] | EMBED 元素節點 |
document.forms[索引值] | FORM 元素節點 |
document.forms[索引值].elements[索引值] | FORM 元素節點和裡面的控制項 |
document.head | HEAD 元素節點 |
document.images[索引值] | IMG 元素節點 |
document.links[索引值] | A 與 AREA 元素節點 |
document.plugins[索引值] | PLUGIN 元素節點 |
document.scripts[索引值] | SCRIPT 元素節點 |
「依 ID 名稱取得節點」以及「直接取得節點」是最直覺的!建議應用程式都採取這種做法來存取元素,有助於保障開發時效。
至於其它做法,通常是為了「剖析資料」用的,例如 Ajax 的通訊、或者像是 docx 附檔名的 Microsoft Office Word XML Format 文件。由於不知道節點的元素名稱與排列架構,只好用巡迴的方式逐筆處理資料,但如果不是這種場合,大可跳過這些做法。
另外,getElementsByTagName() 是傳回節點樹本身,所以查詢速度快,但每增刪節點都會動到整棵節點樹,所以增刪速度慢。querySelectorAll() 則是另外複製一棵小節點樹,所以查詢速度慢,但增刪節點只動這小棵節點樹,所以增刪速度快。
currentScript 如果寫在另一個 SCRIPT 標籤的函式來調用,將無法傳回元素節點,所以只能在當前 SCRIPT 標籤使用。
內容、屬性
取得元素節點後,來看 JavaScript 能用它來做哪些事~
存取標籤所括住的文字內容
元素節點.textContent
以 HTML 格式存取標籤所括住的內容
元素節點.innerHTML
照抄 HTML 標籤的屬性來設定元素
元素節點.標籤屬性=設定值
設定 CSS 樣式
元素節點.style.樣式名稱=設定值
變更 CSS 類型
元素節點.className=樣式類型
設定事件
1) 元素節點.事件=函式;
2) 元素節點.addEventListener(事件,函式,true);
首先來看 textContent,它可以讓你存取元素的文字內容。底下範例在 BODY 裡面新增一個 DIV 元素,然後又在 DIV 裡面新增一個 B 元素,最後在 B 元素使用 textContent 輸入 Hello 做為文字內容:
接著來看 innerHTML,它可以直接在元素節點裡面輸入 HTML 語法,上面的範例可以因此簡化為……
然後來看存取 HTML 標籤的屬性,例如 IMG 標籤有個 SRC 屬性,那 JavaScript 建立一個 IMG 元素時,就可以這樣來設定,比 setAttribute() 省事多了:
再來看設定 CSS 樣式,下面的範例將先前程式的 b 用 CSS 設定為紅色,且字型設定為「微軟正黑體」:
上面程式相當於如下 CSS 描述:
b { color:red; font-family:微軟正黑體 }
請注意 CSS 的 font-family 橫線,到了 JavaScript 改成 fontFamily 字母大寫。
但有些樣式名稱並不規則,例如 CSS 的 float 在 JavaScript 是 cssFolat。
最後來看事件,將 b 加入「在上面按滑鼠左鍵會跳出『別亂按』訊息」的功能:
addEventListener() 是 DOM 標準寫法,不像設定 on 事件會覆寫掉原先的功能,它是追加功能進去,按先後順序執行,並且可用 removeEventListener() 移除。它有第三個參數,設為 true 可以阻止往它層元素傳遞事件,也就是只在自己這層元素觸發事件。