高級程序員花1.5小時總結:最最最重要的javascript-DOM知識梳理
DOM
我們知道,JavaScript是由ECMAScript + DOM + BOM組成的。ECMAScript是JS中的一些語法,而BOM主要是瀏覽器對象(window)對象的一些相關知識的集合。而DOM,則是文檔對象相關的知識的集合。
我們知道,HTML和JS之間的交互是通過事件實現的。而DOM是針對HTML(XML)文檔的一個API。因此,如果我們想實現與用戶的交互,那麼就需要使用DOM提供的API,獲取HTML元素,然後在該元素上綁定相應的事件,實現與用戶的交互。所以,對DOM的理解和掌握就顯得相當重要。
這篇文章共享之前我仍是要引薦下我自個的前端群:633169723,不論你是小白仍是大牛,小編我都挺期待,不定期共享乾貨,包含我自個整理的一份2017最新的前端材料和零根底入門教程,期待初學和進階中的小夥伴。
本文章主要基於《JavaScript高級程序設計(三)》中的DOM相關章節,對DOM的主要知識作出一個梳理,並穿插我個人的一些理解。
節點層次
寫過HTML代碼的地球人應該都知道,我們需要給每一個元素添加縮進,然後在書寫相關的HTMl標籤和內容,最後顯示在網頁上。因此這種嵌套的HTML代碼和內容就構成了節點層次。
對ECMAScript理解的地球人應該都知道,JS中的每一個對象都是基於一個引用類型創建的,而引用類型可以是JS原生提供的引用類型(Array、Function、RegExp、Object等),也可以是自定義的引用類型(通過new關鍵字調用引用類型(也可以叫構造函數))。而所有對象都是Object的實例對象,都可以繼承Object.prototype上的屬性和方法
而在DOM中,也同樣有這樣類似的機制。在DOM中,最頂層的類型是Node類型,其他所有節點都可以繼承Node類型下的屬性和方法。而Node類型實際上就相當於JS中的Object構造函數。
既然如此,那就線看看Node類型下有哪些屬性和方法
Node類型
屬性(在某個特定的節點通過繼承的方式調用以下屬性)
nodeType
nodeName
nodeValue
·············
childNodes(指針,指向NodeList對象)
parentNode
nextSibling
previousSibling
firstChild
lastChild
ownDocument(每個節點都只能屬於一個Document節點)
方法(在某個特定的節點通過繼承的方式調用以下方法)
··· 查找節點 ···
查找元素的方法位於Document類型中
························
··· 插入節點 ···
appendChild(ele)
insertBefore(ele, target): 如果target為null,則規則同appendChild
························
··· 刪除節點 ···
removeChild(ele)
························
··· 替換節點 ···
replaceChild(ele, target)
························
··· 複製節點 ···
cloneNode(boolean) true: 表示深複製, false: 表示淺複製
························
··· 處理文檔節點 ··· 很少用~
normalize()
Node類型上的屬性和方法也就那麼多了,再囉嗦一次,所有的其他節點都可以繼承Node類型上的屬性和方法
Document類型
JS通過Document類型表示文檔。document對象是HTMLDocument的一個實例,表示整個HTML頁面。同時,document對象也是window對象下的一個屬性,因此可以將其作為全局對象來訪問。
屬性
document.documentElement (表示HTML元素),同時可以通過document.childNodes[1]獲取HTML元素
document.body (表示body元素)
document.head (表示head元素) ---HTML5新增
document.compatMode (表示瀏覽器採用哪種渲染方式,'CSS1Compat'表示標準模式, 'BackCompat'表示混雜模式) ---HTML5新增
document.charset (表示文檔中實際使用的字符集,也可用來指定新字符集) ---HTML5新增
document.dataset (表示通過dataset訪問自定義屬性,如document.dataset.myname) ---HTML5新增
document.docType (表示 元素), 存在瀏覽器兼容性問題 ---HTML5新增
document.title (表示 < title > 元素)
··· 網頁請求 ···
document.URL (獲取URL地址)
document.domain (獲取URL中的域名,pathname)
document.attributes (獲取某個節點的屬性,返回NamedNodeMap對象,與NodeList類似)
··· 焦點管理,無障礙性訪問 ···
document.activeElement: 獲取頁面上獲得焦點的元素,通過tab鍵或者focus函數獲得焦點 ---HTML5新增
··· 判斷文檔是否加載完成 ···
document.readyState:存在兩個值,一是'loading',二是'complete'。如果document.readyState === 'complete',表明文檔已經加載完成。即在DOMContentLoaded事件之後。 ---HTML5新增
方法
··· 查找元素 ···
document.getElementById(id) 返回該元素
document.getElementsByTagName(classname) 返回包含零個或多個元素的HTMLCollection對象,與NodeList對象相似
document.getElementsByName(ele)返回帶有給定name屬性的元素,同樣返回HTMLCollection對象
document.getElementsByClassName(className) 返回所有匹配的NodeList對象 (可在Document類型、Element類型上調用該方法)
document.querySelector(selector) selector表示CSS選擇符 返回與該模式匹配的第一個元素,如果沒有找到,返回null (Document類型, DocumentFragment類型, Element類型都可以調用此方法)
document.querySelectorAll(selector) selector表示CSS選擇符 返回一個匹配成功的NodeList對象 (Document類型, DocumentFragment類型, Element類型都可以調用此方法)
··· 創建元素 ···
document.createElement() (創建好的元素處於遊離狀態,需要通過appendChild插入)
··· 創建文本節點 ···
document.createTextNode() (創建好的元素處於遊離狀態,需要通過appendChild插入)
··· 確定元素大小 ···
document.getBoundingClientRect()
··· 焦點管理,無障礙性訪問 ···
document.hasFocus():用於判斷頁面上是否獲得焦點,獲得返回true;否則返回false
Element類型
屬性
id
title
lang
className
方法
getAttribute(ele) 獲取某個屬性
setAttribute(name, value) 設置某個屬性
removeAttribute(ele) 移除某個屬性
getElementsByTagName(ele) 獲取標籤名為ele的元素
插入標記
動態插入DOM節點有以下方法:使用諸如 createElement()和 appendChild()之類的DOM方法,以及使用innerHTML。對於小的DOM更改而言,兩種方法效率都差不多。然而,對於大的DOM更改,使用innerHTML要比使用標準DOM方法創建同樣的DOM結構快得多。當把innerHTML設置為某個值時,後臺會創建一個HTML解析器,然後使用內部的DOM調用來創建DOM結構,而非基於JavaScript的DOM調用。由於內部方法是編譯好的而非解釋執行的,所以執行快得多。
在使用innerHTML, outerHTML, inertAdjacentHTML方法時,需要注意一點的是,由於這三個方法都是直接替換指定元素下的DOM節點,因此當被替換的元素存在事件處理程序時,被替換的事件處理程序並未被一同替換,而是仍然存在內存中。所以,在替換前,需要手動接觸被替換元素的事件處理程序。這屬於DOM方面的前端性能優化問題。
div.removeEventListener('click', fn, false)或者是div.onclick = null
屬性
innerHTML屬性:用於替換當前元素下的所有子元素。如document.body.innertHTML = '< h1 > 哈哈哈 < /h1 >',此時就用h1標籤替換掉了body元素下的所有子元素。IE8+以上瀏覽器支持
outerHTML屬性:用於替換自身及其所有子元素。IE9+以上瀏覽器支持
方法
'beforebegin': 在當前元素前插入一個兄弟元素
'beforeend': 在當前元素元素之下插入一個新的子元素或者在第一個子元素之前插入一個新的子元素
'afterbegin': 在當前元素之下插入一個新的子元素或在最後一個子元素之後插入一個新的子元素
'afterend': 在當前元素之後插入一個緊鄰的同輩元素
insertAdjacentHTML方法:這個方法可以配合innerHTML屬性一起使用,代替appendChild, insertBefore方法。IE6+以上的瀏覽器都支持這方法。此方法需要兩個參數,第一個參數為給定的四個字符串。第二個參數是需要插入的DOM節點。使用方法非常簡單。
<div class="inner"> <div class="child">1</div></div>let inner = document.querySelector('.inner')let element = '< div class="element" >new element< /div >'inner.insertAdjacentHTML('beforebegin', element) // 作為.inner前一個兄弟元素存在inner.insertAdjacentHTML('beforeend', element) // 作為.inner第一個子元素inner.insertAdjacentHTML('beforebegin', element) // 作為.inner後一個兄弟元素存在inner.insertAdjacentHTML('beforebegin', element) // 作為.inner最後一個子元素存在
DOM元素節點遍歷
在寫HTML時,我們會用tab或者回車鍵來對各個標籤進行排版佈局。當我們使用DOM中的childNodes或者firstChild、nextSibling方法時,會獲取到換行之後的空文本節點(nodeType === 3)。如
<p>previous</p> <div class="inner"> <div class="child">1</div> <div class="child">2</div> <div class="child">3</div> <div class="child">4</div> <div class="child">5</div> </div> <p>next</p> let div = document.querySelector('.inner') console.log(div.childNodes) // 返回 (9) [div.child, text, div.child, text, div.child, text, div.child, text, div.child] // 這種返回值是不符合我們的預期的。
因此,存在以下方法可以用來獲取元素節點,排除不必要的文本節點。這幾種屬性和方法是擴展在Element.prototype原型對象上的。支持的瀏覽器有IE 9+、Firefox 3.5+、 Safari 4+、Chrome ֖、Opera 10+。
Element.prototype.childElementCount // 子元素節點數量Element.prototype.firstElementChild // 第一個子元素節點Element.prototype.lastElementChild // 最後一個子元素節點Element.prototype.previousElementSibling // 上一個兄弟元素節點Element.prototype.previousElementSibling // 下一個兄弟元素節點div.childElementCount // 5div.firstElementChild // <div class="child">1</div>div.firstChild // <div class="child">1</div>div.lastElementChild // ... 5 ...div.lastElementChild // ... 5 ...div.previousElementSibling // <p>previous</p>div.previousSibling // '' 注意區別 previousSibling返回空文本節點div.nextElementSibling // <p>next</p>div.nextSibling // '' 注意區別 nextSibling返回空文本節點
當然,也可以使用children屬性來代替childNodes。children屬性會返回子元素同樣是元素節點的NodeList集合,而忽略註釋和空白節點。即div.children.length === div.childElementCount
classList屬性
HTML5在Element.prototype上新增了classList屬性,用於對某個元素的類型進行操作。以往通常是是用className屬性對類型進行操作,由於className返回字符串,所以想刪除某個類名時需要進行一些繁瑣的操作。
<div class="outer happy fun">...</div> // 刪除happyvar classNames = div.className.split(/\s+/);var pos = -1, i, len;for (i=0, len=classNames.length; i < len; i++){ if (classNames[i] == "user"){ pos = i; break; }}classNames.splice(i,1);div.className = classNames.join(" ");
使用classList屬性可以避免以上的操作。classList返回類數組對象,存在以下方法
add(value): 增加類名
remove(value): 移除類名
contains(value): 是否包含類名,包含則返回true;否則返回false
toggle(value): 如果存在value類,則刪除該類名;如果不存在value類,則添加該類名
因此,可以使用classList屬性簡化上面的操作
let div = document.querySelector('.outer')div.classList.remove('happy')
contains方法
此方法用於確認某個節點下是否包含另一個DOM節點,如果存在則返回true;否則返回false;需要傳入一個DOM節點作為參數。IE6+以上瀏覽器都支持。
document.documentElement.contains(document.body) // truedocument.body.contains(document.head) // false
Text類型
屬性
nodeValue | data (訪問Text節點中的文本)
DocumentFragment類型
用途:離線操作DOM元素,避免DOM節點大量的重排和重繪,造成性能問題
方法
document.createDocumentFragment() (表示創建文檔片段)
NodeList對象
理解 NodeList 及其“近親”NamedNodeMap 和 HTMLCollection,是從整體上透徹理解 DOM 的關鍵所在。這三個集合都是“動態的”;換句話說,每當文檔結構發生變化時,它們都會得到更新。因此,它們始終都會保存著最新、最準確的信息。從本質上說,所有NodeList 對象都是在訪問 DOM 文檔時實時運行的查詢。
元素大小
偏移量(offset dimension)
要想知道某個元素在頁面上的偏移量,將這個元素的 offsetLeft 和 offsetTop 與其 offsetParent的相同屬性相加,如此循環直至根元素,就可以得到一個基本準確的值。以下兩個函數就可以用於分別取得元素的左和上偏移量。
function getElementLeft(element){ var actualLeft = element.offsetLeft; var current = element.offsetParent; while (current !== null){ actualLeft += current.offsetLeft; current = current.offsetParent; } return actualLeft;}function getElementTop(element){ var actualTop = element.offsetTop; var current = element.offsetParent; while (current !== null){ actualTop += current. offsetTop; current = current.offsetParent; } return actualTop;}
客戶區大小(client dimension)
要確定瀏覽器視口大小,可以使用 document.documentElement 或 document.body(在IE7 之前的版本中)的clientWidth 和 clientHeight。
function getViewport(){ if (document.compatMode == "BackCompat"){ return { width: document.body.clientWidth, height: document.body.clientHeight }; } else { return { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight }; }}
滾動大小(scroll dimension)
·················
確定元素大小
document.getBoundingClientRect()方法, 返回一個矩形對象。包含4個屬性:left、top、right和bottom。這些屬性給出了元素在頁面中相對於視口的位置。
最後在說幾句:
厲害程序員相對於普通程序員的優勢在於:
寫出的代碼更容易排錯,不是高手的代碼就不會錯,而是高手的代碼出了錯容易找。高手的代碼可讀性一定很好,模塊清晰,命名規範,格式工整,關鍵的地方有註釋,出了異常有log,自然容易排錯,即使交給別人去debug也是比較容易的。
今天這個圖片彈窗特效到這裡寫完了,學習web前端的可以加我的群,每天分享對應的學習資料:633169723,歡迎初學和進階中的小夥伴。多寫多練。