DOM 樹 HTML 文檔的骨幹是標簽。 根據文檔對象模型(DOM),每個HTML標簽都是一個對象,同樣標簽內的文本也是一個對象。因此這些對象都可通過 JavaScript 操作 如果文檔中有空格(就像任何字元一樣),那麼它們將成為 DOM 中的文本節點,如果我們刪除它們,則不會有任何內容。 `` ...
DOM 樹
HTML 文檔的骨幹是標簽。
根據文檔對象模型(DOM),每個HTML標簽都是一個對象,同樣標簽內的文本也是一個對象。因此這些對象都可通過 JavaScript 操作
如果文檔中有空格(就像任何字元一樣),那麼它們將成為 DOM 中的文本節點,如果我們刪除它們,則不會有任何內容。
<head>
之前的空格和換行符被忽略
</body>
之後放置了一些東西,那麼它會自動移動到 body 內部,因為 HTML 規範要求所有內容必須位於
通常再瀏覽器中的文本不會顯示開頭/結尾的空文本節點,標簽之間也不會顯示空文本節點。
如果瀏覽器遇到格式不正確的HTML,在形成DOM是會自動修複它
如:
<html>
即使不在文檔中,瀏覽器也會自動創建它
按DOM規範,table 必須具有 <tbody>
,因此table中未使用<tbody>
形成DOM時會自動添加。
其它 節點:
註釋不會以任何方式影響視覺表示,但是必須遵循一條規則 —— 如果HTML中有東西,那麼它必須在DOM樹中。
HTML中所有內容都是DOM的一部分,
註釋是一個節點甚至<!DOCTYPE...>
也是一個節
DOM總有12種節點
遍歷DOM節點
所有對DOM的操作都是從document
對象開始,將這個對象賦予一個變數,對其進行修改操作
最頂端的節點
DOM節點樹可以通過 document
屬性使用
頂端的節點對應<html>
並且 <html> = document.documentElement
而<body> = document.body
,<head> = document.head
docment.body
可能為null,如果將script
腳本放入 <head>
標簽種,那麼此腳本無法訪問到document.body
,即為null
childNodes
childNodes
集合提供對所有子節點包括文本節點的訪問,它看起來是一個數字,實際上只是一個可迭代的類數組對象,因此沒有數組的方法
所有的Dom 集合節點都是只讀的無法通過賦值來替換對應的節點
除小部分節點,幾乎所有的DOM集合都是實時的,它們反應的是DOM的實時狀態
不要是有 for...in
來遍歷DOM集合,此方法會列出其所有的屬性。
註意此屬性只能訪問到當前script
腳本之前對應的節點
可以通過elem.hasChildNodes()
來檢測是否含有子節點
parentNOde / siblingNode
通過elem.parentNode
可訪問當前節點的父節點
通過elem.previousSibling/elem.nextSibling
可訪問對應節點的上/下兄弟節點
只訪問元素節點
- children: 只獲取類型為元素節點的子節點
- firstElementChild,lastElementChild:只獲取第一或最後一個子元素
- previousElementSibling, nextElementSibling:兄弟元素
- parentElement:父元素
parentElement 可能為null,因為其方法返回的是父元素節點,而parentNode返回的是任何類型的父節點,因此,document.documentElement.parentElement === null
HTMLCollection (動態)
通過元素查找子元素如果子元素是一個集合將返回 HTMLCollection
類數組
let tb = documet.querySelector('table')
let tbs = tb.tBodies // HTMLCollection [tbody]
let trs =tbs.rows // HTMLCollection [tr,tr,tr,...]
let tr1 = trs[0]
tr1.sectionRowIndex //0 當前 tr 在集合中的位置
tr1.rowIndex // 1 當前 tr 在整張表中的 位置
let tds = tr1.cells // HTMLCollection [td,td,td,...]
td[0].cellIndex //0 當前 td 在父元素 tr節點 中的位置
NodeList (靜態 如果使用迴圈創建查詢的元素,新的元素不會實時加入到NodeList中)
通過 document
中的方法 document.querySelectorAll
或elem.querySelectorAll
獲取的元素集合將返回NodeList
類數組
getElement*
方法只能通過 document
對象調用
let divs = document.querySelectorAll('div') // NodeList(4) [div.Owen, div#modal, div.main, div]
document.getElementsByTagName('div')//HTMLCollection [div.Owen]
matches
elem.matches(css)
會檢測 elem
是否匹配到給定的css選擇器,返回 true 或 false
closest
elem.closest(css)
此方法會查找css選擇器匹配到的祖先HTML,包括自身,並返回最先找到的元素
contains
elem.catains(dom)
判斷 dom 是否為 elem 的後代,或等於elem,返回true 或false
節點屬性
所有的節點都繼承自根節點 EventTarget
- EventTarget:作為基礎,讓所有DOM 節點都支持事件
- Node:作為DOM 節點的基礎,提供DOM樹的核心功能:
parentNode
、nextSibling
、previousSibling
、ChildNodes
等(只能讀取 getter);文本節點Text
,元素節點Element
,註釋節點Comment
都繼承自Node - Element:做為DOM 元素的基類。提供元素級導航:
nextElementSibling
、children
、getElement*
、querySelector
等等,瀏覽器不僅支持HTML,還支持 XML、SVG等,分別對應的基類HTMLElement
、XMLElement
、SVGElement
- HTMLElement:作為所有元素的基類,被各種元素繼承
innerHTML 和 outerHTML
- innerHTML: 獲取或替換當前節點的所有子節點(不包含當前節點)
- outerHTML: 替換當前節點
文檔片段 DocumentFragment
用於存儲節點的包裝器,不會再瀏覽器中展示,需要通過插值方法才能展示包裝器裡面的內容
function creatEl(){
let frag = new DocumentFragment();
for (let i=1;i<4;i++) {
let li = document.createElement('li')
li.append(i)
frag.append(li)
}
return frag
}
ul.append(creatEl())
類樣式的修改等操作
elem.className
對應元素的類名,多個類目以空格分隔
ul.className // "class1 class2 ..."
同時還要一個 elem.classList
對象可訪問類名,它以類數組的方式存在,同時具有 add/remove/toggle/contains
等方法
ul.classList // DOMTokenList(2) ["333", "444", value: "333 444"]
ul.classList.add('class1')
ul.classList.remove('class1')
ul.classList.toggle('class1') // true 新增
ul.classList.toggle('class1') // false 去除
ul.classList.contains('class1') //false 是否包含
通常我們使用 style.*
單獨對樣式屬性進行修改,如果想要對多種樣式進行調整可使用 cssText
,此方法會直接替換之前的樣式
ul.style.cssText = `
color: red ;
background-color: skyblue;
width: 20px;
text-align: center;`
style 屬性僅針對 style 屬性值進行操作,無法讀取css類中的屬性值
<style>
body {margin:20 auto;}
</style>
<script>
document.body.style.margin // ""
</script>
這時我們需要使用 getComputedStyle(el,[,pseudo])
方法來獲取對應的值
如果不傳參或值無意義,將返回元素所有樣式,其屬性值都為解析值,如 font-size:1em
最後獲取的可能為解析後的值"16px"
let res = getComputedStyle(document.body)
res.marginTop // "20px"
res.margin // 谷歌 "20px 0px" 在火狐中為 "" 因此訪問確切屬性值須使用完整屬性名
獲取元素的尺寸和滾動距離
- offsetTop/Left: 獲取相對於設置有position屬性為 absolute relative、fixed 的值或td、th、table、body的元素的距離
- offsetWidth/Height:獲取外部寬度/高度,包含border,padding,scrollbar (display:none 或自身不在文檔中,其值為0或null,由此可判斷當前元素是否被隱藏)
- clientTop/Left:獲取內側與外側的距離(滾動條在左邊時,包含滾動條的寬度)
- clientWidth/Height:獲取可視區內容的寬高,即不包含滾動條和border
- scrollWidth/Height:獲取全部內容(包含隱藏部分)的寬高
- scrollTop/Left: 獲取 元素隱藏部分的上/左距離,包含border,這兩個屬性可修改,其它屬性能只讀取
HTML 文件里如果沒有 <!DOCTYPE HTML>
上述的屬性可能會有所不同,這不是一個 JavaScript 的問題,但會影響到 JavaScript。
滾動瀏覽器視窗
pageXOffset/pageYOffset: 獲取可視視窗移動的距離 無法設值
可通過 window.scrollBy, window.scrollTo, elem.scrollIntoView來滾動視窗
scrollBy(x,y)
:滾動頁面至相對於現在位置的(x,y)位置scrollTo(x,y)
:滾動到頁面相對於文檔左上方的(x,y),位置,類似於scrollTop/scrollLeft
elem.scrollIntoView(truly)
:如果 truly 為真則使 當前元素 滾動至視窗頂部,元素頂部與視窗頂部對齊,如果truly 為false,則當前元素底部與視窗底部對齊。
如果禁止視窗滾動可使 樣式屬性 overflow
值為 hidden
坐標
getBoundingClientRect()`方法獲取相對於可視視窗的坐標對象
其所有屬性都是以可視視窗左端(X)和頂部(Y)為起點
ul.getBoundingClientRect()
/*
DOMRect {
bottom: 829.59375 // 元素底部的Y坐標
height: 210 // 元素真實高度
left: 0 // 元素左邊 X 坐標
right: 1903 // 元素右邊 X 坐標
top: 619.59375 // 元素頂部 Y 坐標
width: 1903 // 元素自身真實寬度即不包含滾動條
x: 0
y: 619.59375
}
*/
document.elementFromPoint
document.elementFromPoint(x,y)
返回可視視窗坐標(x,y),最頂層的元素
let elem = document.elementFromPoint(0,0) // <p>556666</p>
如果x,y
不在正常範圍內將返回 null,
相對於文檔坐標,JS並未提供原生標準方法,可自己寫一個出來:
function getDomCoords(el){
let {top,left} = el.getBoundingClientRect()
return {
top:top+ window.pageYOffset,
left:left+window.pageXOffest
}
}