BOM和DOM相關API
一、DOM相關API
1. DOM API
-
DOM(Document Object Model)文檔對象模型得樹形結構
- 文檔對象模型就是一個樹形結構,類似于家譜樹
html標簽里面包裹了所有的文檔內容。他是一個父親節點(parent) 它沒有父親節點,也沒有兄弟節點,我們稱html標簽為樹根,也就是根節點,整個html表示整個文檔。
在html節點內部通常有兩個同級節點head和body,html是head的父親節點,html也是body的父親節點,他們同一層級并且相互不包含,我們稱同屬于同一個父親的節點為兄弟節點,而head和body都是html的子節點。這樣一層一層的關系就是節點樹。各個標簽在頁面中都是元素節點(element node)
-
節點(node)的種類
- 元素節點(element node)
文檔對象模型中的標簽就是最基本的元素節點。它們層層嵌套形參整個頁面。內部可能包含了文本和屬性
- 文本節點(text node)
我們稱DOM內的文本為文本節點。文本節點有的被包含在元素節點中的,比如p標簽內部的文字。但是有的元素節點沒有文本節點,
- 屬性節點(attribute node)
屬性節點從屬于元素。比如其中type='radio'是元素節點p的屬性節點。不是所有的元素都有屬性節點,但是所有的屬性節點都必然屬于某一個元素。如:class/id/style
-
獲取元素的方式
document.getElementById //通過ID獲取某個元素,所有瀏覽器兼容 document.getElementsByClassName //通過class類名獲取,獲取是一組,不支持IE8及以下 document.getElementsByTagName //通過標簽名獲取,獲取是一組,所有瀏覽器兼容 document.getElementsByName //通過name獲取,獲取是一組,很少用,所有瀏覽器兼容 querySelector、querySelectorAll其中getElementById和querySelector獲取的是元素單個節點。
而其余的方法都是獲取若干個元素節點,那么會以類數組的形式存儲
let arr1 = document.getElementsByClassName("item") let arr2 = document.querySelectorAll(".item") //arr1 HTMLCollection //arr2 NodeList
HTMLCollection和NodeList本質上共同點和不同:
? 都是DOM節點的集合,兩者都屬于Collections范疇,但是NodeList的范圍要更廣泛一點,它可以包含節點對象和文本對象。HTMLCollection比NodeList多了一個namedItem方法,其他方法保持一致
? HTMLCollection是以節點為元素的列表,可以憑借索引、節點名稱、節點屬性來對獨立的節點進行訪問。HTML DOM中的Collections是實時變動的,當原始文件變化,Collections也會隨之發生變化。
? NodeList返回節點的有序集合,DOM中的NodeList也是實時變動的
Node是一個基礎類型,document, element, text, comment, documentFragment等都繼承于Node. 在這篇文章最開始的測試中`NodeList`結果中有非常多的`text`,其實element, text, comment都是Node的子類,可以將它們視為:**elementNode**, **textNode**以及**commentNode**.平時在DOM中最常用的Element對象,其本質就是elementNode.
<ul class="box">
<li class="item"></li>
<li class="item"></li>
<li class="item"></li>
</ul>
let box = document.querySelector(".box")
let qli = document.querySelectorAll(".item")
let cli = document.getElementsByClassName("item")
let nli = document.getElementsByTagName("li")
box.innerHTML+=`<li class="item"></li>`//添加元素
qli.length//3 NodeList 不會動態改變
cli.length//4 HTMLCollection 會動態改變(相當于記錄了一個獲取方式)
nli.length//4 HTMLCollection 會動態改變
-
DOM中的增刪改查
- 增加:
以前學習得時候我們用的是innerHTML添加的方式,這中添加方式的本質是什么?
box.innerHTML+="BLABLA"將box內部的DOM節點轉換成字符串,然后字符串添加內容,然后瀏覽器重新渲染。重新生成DOM結構。等價于重繪和重排
其中缺點有兩點:1. 增加瀏覽器渲染不必要的開銷 2. 原先的節點綁定的事件可能會消失。
for(let i = 0; i < 100; i++){ box.innerHTML+=`<li class="item">${i}</li>` } //此時瀏覽器重寫了100次box的內部結構,每次重寫都會重新渲染一次,等價于加載了100次頁面 let str ="" for(let i = 0; i < 100; i++){ str+=`<li class="item">${i}</li>` } box.innerHTML+=str//兩段代碼效率完全不同上面的效率其實不是最重要的。最重要的是你的新的節點已經不是原先的節點了
let box = document.querySelector(".box") let qli = document.querySelectorAll(".item") let cli = document.getElementsByClassName("item") let nli = document.getElementsByTagName("li") box.innerHTML+=`<li class="item"></li>`//添加元素 qli[0].onclick = function(){ alert(1) }//不會有任何改變,因為是靜態獲取,你綁定的元素已經消失了let box = document.querySelector(".box") let qli = document.querySelectorAll(".item") let cli = document.getElementsByClassName("item") let nli = document.getElementsByTagName("li") cli[0].onclick = function(){ alert(1) }//雖然是動態獲取,但是你綁定的元素還是會消失,注意順序不同 box.innerHTML+=`<li class="item"></li>`//添加元素所以綜上,不要使用innerHTML添加元素。
正確的方式: DOM的生成節點的方式和添加元素的方式
let li = document.createElement("li")//生成一個li的標簽節點。 let text1 = document.createTextNode("哈哈哈")//生成一個文本節點 li.appendChild(text1)//往li盒子中添加一個文本節點,默認添加到最后 let text2 = document.createTextNode("哇") li.insertBefore(text2, text1)//在li標簽里面text1前面添加text2 document.body.appendChild(li)這種添加方式只是對DOM樹的某一個枝葉做修改,不會重新種樹。但是枝葉的修改也是DOM操作,也會有渲染重排的問題。如何減少dom操作類似于str作為承接減少添加次數得方法有嗎?有
let box = document.createDocumentFragment()//生成一個只存在于內存中的節點片段集合,它不是數組,更類似一個虛擬節點,里面存放元素 for(let i =0;i<100;i++){ let li = document.createElement("li")//如果生成元素寫在循環外面? box.appendChild(li) } document.body.appendChild(box)//100個li就直接添加到body中了,直接父子關系,不存在中間層如果生成元素寫在循環外面從頭到尾就只生成了一個節點。所以添加也只添加一次
- 刪除
同樣也不要innerHTML。
<ul class='list'> <li></li> <li></li> </ul>let ali = document.querySelectorAll(".list li") let list = document.querySelector(".list") list.removeChild(ali[0])// 從父元素中刪除節點。必須是父子關系。- 修改=增加+刪除
list.replaceChild(document.createElement("a"), ali[0])//選中兒子ali[0]替換成新建的a元素節點 list.relaceWith(document.createElement("li"))// 將自己替換成li元素節點- 查詢
查詢元素。可以通過絕對位值關鍵字符查詢。也可以通過相對位值查詢父節點,相鄰節點,子節點查詢元素。
- 父元素
let li = document.querySelectorAll(".list li")[0]
li.parentNode//返回li的父元素的節點
li.parentElement//返回li的父元素
li.parentNode===li.parentElement //true
li.offsetParent //定位父級
//兩者沒有本質上的區別,都可以返回父元素
document.documentElement// html標簽節點
document.documentElement.parentElement// null,因為這里已經沒有父元素了
document.documentElement.parentNode// #document 文檔
document.documentElement.parentNode===document// true
* 同級元素
nextSibling和previousSibling選擇上一個或者下一個同胞元素,如果不存在就是返回null
```html
<ul>
<li></li>
asdasd
<li></li>
</ul>
let li = document.querySelector("li")
li.previousSibling // null 前面一個節點
li.nextSibling// "asdasd" 后面節點,是文本
li.nextSibling.nextSibling// 第二個li
如果只想要標簽元素節點,可以自行封裝方法
其中節點有節點類型。1是標簽,3是文本,2是屬性
function next(ele){
if(el.nextSibling.nodeType==1) return el.nextSibling
return next(ele.nextSibling)
}
//判斷nodeType如果是標簽就返回,如果不是就繼續找下一個
//pre同理
相鄰元素節點的獲取去方式2:
li.nextElementSibing //下一個元素節點
li.previousElementSibing //前一個元素節點
- 子元素
子元素可能是多個,也可能是一個。
<ul>
<li></li>
asdasd
<li></li>
</ul>
let ul = document.querySelector("ul")
ul.childElementCount //子元素節點個數 2
ul.children // 子元素節點集合HTMLCollection 2個li
ul.childNodes// 子節點集合 NodeList 3個,其中第二個是text
ul.hasChildNodes // 有沒有子節點,有就是true沒有就是false也可以用length
ul.firstChild //第一個子節點
ul.firstElementChild //第一個元素子節點
ul.lastChild //第一個子節點
ul.lastElementChild //第一個元素子節點
- 屬性節點和文本節點
let list = document.querySelector(".list")
let attr = document.createAttribute("asd")
attr.nodeType // 2
list.setAttribute(attr)
let text = document.createTextNode("asd")
text.nodeType // 3
- 節點對象的繼承關系。
任何一個節點都是對象,原型鏈的終點都是Object。那繼承關系是什么呢?在chrome瀏覽器可以測試
let temp = document.createElement("div")// div標簽節點
temp.__proto__ // HTMLDivElement div構造函數的原型,上面有div節點獨有的方法
temp.__proto__.__proto__ // HTMLCollection html元素節點構造函數得原型,節點的共有性質都在這
temp.__proto__.__proto__.__proto__ // Element 本質上和Document同級都是Node下面的一個元素
temp.__proto__.__proto__.__proto__.__proto__ // Node 所有節的構造函數得原型,存放節點基礎方法
temp.__proto__.__proto__.__proto__.__proto__.__proto__ // EventTarget 事件對象的構造函數的原型
temp.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__ // Object的原型
2. 文檔寬高、窗口寬高、元素寬高屬性
-
元素寬高相關的屬性
client: 讀取元素的 content的寬高 + padding 的寬高 , 不包括滾動條和外邊距以及邊框
獲取body的寬高屬性:
console.log(document.body.clientWidth) //body的實際寬度,不包括滾動條文檔可視區域的寬高:
document.documentElement.clientWidth //當前文檔區域可見寬度 document.documentElement.clientHeight //當前文檔區域可見高度注意: 不包括滾動條. 雖然沒有兼容性問題,但是在IE不同的版本的寬度顯示數值有一點差異,大概是3px的差異
注意: window下沒有client屬性
用在元素身上:
clientWidth: 元素的content + padding寬/高, 不包括滾動條, 和border
clientHeight
clientLeft
clientTop: 返回元素的content到邊框的距離,也就是邊框的厚度
注意: 只讀屬性,不能進行設置
<style> #box{ width: 200px; height: 200px; padding: 30px; margin: 70px; border: 20px black solid; background-color: #f60; } </style> <div id="box"></div> <script> var oBox = document.getElementById("box") console.log(oBox.clientWidth) console.log(oBox.clientHeight) </script>獲取窗口對的內部寬度
window.innerWidth; // 窗口的寬, 包括滾動條的寬度如果想獲取滾動條的寬度:
window.innerWidth - document.documentElement.clientWidthoffset 獲取body頁面的實際寬高
document.body.offsetWidth document.body.offsetHeight對于文檔元素,只能獲取文檔的可視區域寬,但是能獲取實際高度
document.documentElement.offsetWidth //當前文檔的可見區域寬度 document.documentElement.offsetHeight//當前文檔的實際高度在元素上:
offsetWidth: 元素的content+padding+border
offsetHeight:
offsetTop 元素頂部到定位父級的頂部距離,不包括定位父級的邊框部分
offsetLeft 元素左邊到定位父級元素左邊距離, 不包括定位父級的邊框部分
元素到body頂部的距離方法: 沒有包括父級邊框
function getOffset(dom){ let o ={ top: 0, left: 0 } while(dom!==document.body){ o.left += dom.offsetLeft o.top += dom.offsetTop dom = dom.offsetParent //定位父級 } return o } //這里忽略掉了所有的邊框包括父級邊框的,元素到body頂部的距離
function getOffset(dom){ let o = { top: 0, left: 0 } while(dom!==document.body){ o.left += dom.offsetLeft+dom.offsetParent.clientLeft o.top += dom.offsetTop+dom.offsetParent.clientTop dom = dom.offsetParent //定位父級 } return o } //包含邊框,兩方法可以合并scroll
讀取元素的content寬度+padding寬度,包含滾動條
scrollWidth
scrollHeight
子元素超出的時候, 會加上 超出得子元素的寬度/高度。當超出的時候,左padding或者上padding加上內容寬度或者高度。右padding或者下padding已經失去意義所以不會結算。
但是當添加overflow屬性的時候。會計算全部的padding + 內容寬/高 + 超出的子級寬/高
滾動高度:可讀可寫
獲取元素的y軸或者x軸方向的被滾動擋住的那部分的寬度高度。等價于滾動過的部分
scrollTop
scrollLeft
document.documentElement.scrollTop document.documentElement.scrollLeft可以在滾動事件中監聽:
window.onscroll = function(){ console.log(document.documentElement.scrollTop) } //滾動的時候返回滾動高度獲取頁面滾動高度存在兼容性問題,需要做兼容性寫法
document.body.scrollTop || document.documentElement.scrollTop鼠標位值相關的坐標
事件對象e
document.body.onclick = function(e){ // e 是 前面onclick事件觸發之后整理的那一時刻的信息對象,叫事件對象 console.log(e.clientX, e.clientY) // 相對于窗口的左上角位值 console.log(e.pageX, e.pageY) // 相對于文檔位置的左上角的位置,包含滾動條隱藏部分 }
事件對象的兼容性寫法
e = e || window.event
3. classList對象
classList是元素節點對象下面的一個屬性,他是一個記錄類名以及相關方法的對象,類數組對象
//<li class="item1 item2 item3"></li>
let li = document.querySelector("li")
li.classList // ["item1","item2","item3"]
li.classList.value // "item1 item2 item3" 獲取類名值
//在其原型上擁有很多方法
li.add("item4") // 返回undefined
li.contain("item4") // true
li.remove("item4","item3") //返回undefined
二-BOM相關
BOM是(Browser Object Model)的簡稱,中文名叫瀏覽器對象模型.在瀏覽器環境中抽象瀏覽器的語言是JavaScript, 所以JS中的原型鏈以及繼承的性質將在BOM中實現.其中最大的就是window.對應的DOM中的document.
1. window
一個瀏覽器窗口(網頁)就是一個window對象.里面包含了各種方法與參數, 這些東西可以幫助我們使用操作瀏覽器.比如瀏覽器的尺寸, 瀏覽器綁定的事件, 視圖動畫相關的方法, css單位系統.
因為是一個普通對象所以有原型鏈, 在原型鏈上有事件對象構造函數的原型(EventTarget), 這個是和DOM對象原型鏈上的(EventTarget)一致, 所以window對象和document對象都是可以訪問EventTarget構造函數的原型的方法...換句話說都可以綁定事件.并且也繼承Object原型的方法.
每當新打開一個頁面的時候. 此頁面都會新建一個window對象.屬性各不相同內容也不近相同(幾乎都會初始化).甚至是跳轉頁面的時候都會使得window變化. 但是在(非新建頁面)跳轉頁面的時候, 會有一個window.name的屬性保留下來.就是當前窗口的名字
window.name = "Gin"
//跳轉或者輸入網址回車
console.log(window.name)//"Gin"
window.name 具有如此性質可以做到跨網站交互信息.所以在全局中請不要嘗試修改window.name或者var name
- 瀏覽器的尺寸數據和方法
瀏覽器和document不同,不具有那么多的尺寸數據, 常用的有:
innerWidth, innerHeight 瀏覽器內部尺寸, 包含滾動條但是不包含書簽欄的尺寸,也不包含控制臺
screenLeft, screenTop(screenX, screenY兼容等價) 瀏覽器距離屏幕左上角的距離
outerHeight, outWidth 瀏覽器整體尺寸,包含外部的書簽欄等
scrollX, scrollY 表示瀏覽器的滾動的位置
-
名詞解釋
- screen:屏幕。這一類取到的是關于屏幕的寬度和距離,與瀏覽器無關,獲取window對象的屬性。
- client:當前選中區域元素,這里是指瀏覽器區域。
- offset:偏移。指的是目標甲相對目標乙的距離。
- scroll:卷軸、卷動。指的是包含滾動條的的屬性。
- inner:內部。指的是內部部分,不含滾動條。
-
方法等:
- alert(str) 警告
- confirm(str) 確認返回true取消返回false
- prompt(message, placeholder) 提示用戶輸入, 返回用戶輸入的內容, message是描述, placehoder是占用默認文本
- close 關閉
- blur 獲取焦點
- scrollBy(x, y)偏移多少像素
- scrollTo(x, y)偏移到多少得位值
- confirm() 確認
- prompt() 輸入信息并返回
- alert(str) 警告
2. location
location是一個對象,描述的是當前文檔的相關位置信息。他是window對象下面的屬性
console.log(window.location)
- host: 主機名加端口
- hostname: 主機名不加端口
- port:端口, 一臺服務器有很多的入口,每個入口都有不同的處理方法和訪問權限,默認是80端口
- pathname: 路徑選擇,網站分區選擇
- protocol: 協議,客戶端服務端請求響應標準如http和https等
- search: 請求的內容,通常是get請求發送給后臺的信息
- href: 整體路徑
通過這個信息你就直到你在哪你在干什么了。
如果我們直接訪問location或者參與字符串操作會有內置toString方法輸出href
3. history
你訪問過的網站都會在瀏覽器內留下歷史記錄(滑稽臉)。并且我們可以通過JS代碼進行跳轉。它也是window對象下面的屬性
window.history.length//該整數表示會話歷史中元素的數目,包括當前加載的頁面
history.back()// 跳轉到上個頁面
history.forward()//跳轉到下個頁面
history.go()// 傳入一個數值,0表示當前頁面,1表示下個頁面,-1表示上個,-2表示上上個。。。
4. navigator
有關瀏覽器的信息。也是window對象下面的屬性。
navigator.appVersion//只讀:返回瀏覽器的平臺和版本信息
navigator.appCodeName// 聲明了瀏覽器的代碼版本, 一般都是Mozilla
navigator.userAgent //用戶本地的信息組合
// 這些信息會在前后端請求的時候編排在請求頭文件中發送給后臺方便后臺識別
//等等
5. console
window對象下面也有一個console對象,也有很多方法
console.log()//輸出當前作用域的值
console.assert(bool, msg) //當為false就彈出msg,當為true就是不返回。
console.clear()//清屏。。。
console.count(str)//傳入字符串并計數
console.countReset(str)//清除計數
console.warn(str) //警告
console.error(str) //打印報錯信息
console.dir()// 輸出可作為對象展開的內容
console.group()//小組內容輸出。
console.groupEnd()//小組內容輸出關閉。
console.time()//計時
console.timeEnd()//計時結束

浙公網安備 33010602011771號