你應該知道的 DOM

概念

DOM 定義了訪問 HTML 和 XML 文檔的標準,同時它也是一個編程接口。DOM 早期針對於 XML 後來通過擴展後可用於 HTML,它把頁面映射爲一個多層節點結構,並提供方法、屬性使得程序能夠對該結構進行訪問及修改,從而改變文檔對應節點的樣式和內容。javascript

簡單來講,瀏覽器在顯示頁面的時候須要解析文檔,而腳本語言也須要獲取這份文檔,可是它們最終須要獲取到的信息是不一樣的,因此 DOM 至關於一個橋樑,溝通了腳本語言和瀏覽器。因此當咱們修改這個由 DOM 構建出的對象時,頁面也會發生相應的變化。html

若是將一個web頁面當作一個文檔,因爲不一樣的瀏覽器有各自的 DOM 標準,因此瀏覽器在解析頁面的時候會映射出不一樣的 DOM 樹模型,從而致使在操做、訪問頁面節點上存在差別。例如IE中全部的DOM對象都是以 COM 對象的形式實現的,因此和其餘瀏覽器實現的DOM對象行爲不一樣。java

注:SVG、MathML、SMIL都是基於XML的語言,它們都發布了針對了本身的 DOM 標準node


產生緣由

W3C 組織爲了防止 Netscape 公司和微軟按本身意願開發致使瀏覽器兼容困難狀況發生。規劃了 DOM 標準,Web 瀏覽器在 DOM 標準出現一段時間後纔開始實現它,如今的瀏覽器的首要目的就是支持 DOM,有了標準就對開發者更加的友好了。web

同時須要注意的是 DOM 不只針對 JavaScript,其餘語言也實現了DOM。編程


W3C DOM標準

  • 核心 DOM-適用於全部文檔類型的標準模型
  • XML DOM-XML 文檔的標準模型
  • HTML DOM-HTML 文檔的標準模型

核心DOM和html DOM的區別

核心DOM

  • 對象:Document、Node、ElementNode、TextNode、AttributeNode、CommentNode、NodeList跨域

  • 接口:createElement、appendChild、setAttribute等數組

  • 接口分類:瀏覽器

    • 視圖接口:跟蹤不一樣文檔的接口(添加CSS前和後)
    • 事件接口:定義了事件和事件處理
    • 樣式接口:定義了基於CSS的接口
    • 遍歷和範圍接口:定義了遍歷和操做文檔樹的接口
    • ...
  • 提供了全部文檔類型增刪改查操做,經過對象提供的接口修改 DOM 樹及其表明的文檔。安全

html DOM

  • 對象:Image、Table、Form、 Input 等

  • 接口:createElement、appendChild、setAttribute等

  • 只提供了HTML增刪改查操做,經過將 HTML 類型的封裝,來簡化和對象對應DOM的訪問和操做。

    var img = new Image();
    img.src = 'xxxx';
    img.style.width = '100px';
    複製代碼

總結:他們都爲指定的文檔類型提供了對象、屬性、方法和事件。只不過核心DOM提供的是文檔基本類型的擴展,HTML DOM提供的只是HTML類型的擴展。


節點層次

DOM樹結構由一個個的節點組成,節點又分爲不一樣的類型,不一樣的類型有其特有的行爲和特徵。每一個節點都有包含着本身的信息和接口等,因爲節點和節點間有必定的關係,能夠造成了層次關係,最終的體現就是以HTML元素即文檔元素爲根節點的樹狀結構。

image-20200714205746268

Node類型

JavaScript 中全部的節點類型都是繼承於Node 類型,因此全部的節點都共享着相同的基本屬性和基本方法。

節點關係
  • childNodes 是一個類數組,能夠經過 slice 方法進行轉換也可使用 ES6 中的擴展運算符進行轉換。
  • 關係指針都只是可讀的
  • hasChildNodes()能夠判斷該節點是否包含一個或以上的子節點,若是是那麼返回 true。
  • 若是沒有子節點 fristChild 和 lastChild 都爲 null,若是隻有一個節點那麼這倆值指向同一個節點。

image-20200714170442235

節點操做
  • 因爲關係指針都是可讀的,因此 DOM 提供了操做節點的方法。操做節點的時候,節點的信息以及和其餘節點的關係也會相應進行更新。

  • 插入節點

    • appendChild() 是最經常使用的節點插入方法,它將子節點插入到childNodes列表的最後。若是插入的節點已經在以前存在過,那麼該節點將從以前的位置移除,在當前childNodes 列表最後進行添加。這是因爲同一個節點不能出如今文檔的多個位置上。
    • insertBefore(insert,refer) 能夠將節點插入到任意位置,其參數爲插入的節點以及參考的節點,最終插入的節點會在參考節點前面,若是參考節點爲null,那麼至關於appendChild()
  • 替換節點

    • replaceChild(insertNode,replaceNode)能夠將被替換的節點替換成指定的節點(insertNode)。
  • 移除節點

    • removeChild() 移除節點,並將要被移除的節點做爲返回值 。
  • 克隆節點

    • cloneNode( boolean),接受一個boolean值,若是爲true那麼就是深拷貝節點。深拷貝節點會把該節點和其下面節點及關係進行拷貝,淺拷貝只會拷貝當前節點。須要注意的是,cloneNode 不會拷貝節點的節點中 JavaScript 屬性,例如事件處理程序等。可是在 IE 中該方法會拷貝事件處理程序,因此在拷貝以前最好先移除事件。
  • 優化文本節點

    • normalize() 若是對節點使用該方法,那麼當該節點下全部子節點出現,文本節點爲空或者有 2 個連續的子節點時會進行 刪除或和並文本節點的操做。

Element 類型

Element類型用於表現 XML 、HTML元素,提供了對元素標籤名 (TagName)子節點特性的訪問。也是繼承於Node類型。同時在HTML中標籤名始終以所有大寫的形式顯示,在XML中標籤名和源代碼中相同。

if(element.tagName.toLowerCase() == 'div'){
	// 這樣能夠適用 HTML 和 XML 形式。
}
複製代碼
獲取標籤名

獲取標籤名可使用 ele.tagName 或者是 ele.nodeName ,兩者返回的值都是相同的。

標準特性

HTMLElement類型繼承於Element,全部的HTML元素都是由該類型或者其子類型構建。下面的標準特性就是Element特有的特性。他們均可以經過元素訪問特定屬性進行訪問或修改。

div.id = test;
div.id //test;
複製代碼
  • id 文檔中的惟一標識符

  • title 有關元素的附加說明信息,通常會經過提示條顯示出來

  • lang 元素內容的語言代碼,不多使用

  • dir 語言的方向,默認左到右 ‘ltr’ ,能夠修改成右到左 ‘rtl’ 。不多使用

  • className 和元素的 class 特性對應,能夠爲元素指定 CSS 類。

  • childNodes和children的區別

    • childNodes返回元素、文本甚至是註釋

    • children只返回元素,不返回文本。

      <div id="outer">
          text1
          <div>1</div>
          <div>2</div>
          <div>3</div>
          text2
      </div>
      複製代碼
      var dom = document.getElementById('outer');
      console.log(dom.children)// HTMLCollection(3)
      //0: div
      //1: div
      //2: div
      console.log(dom.childNodes)// NodeList(7)
      //0: text
      //1: div
      //2: text
      //3: div
      //4: text
      //5: div
      //6: text
      複製代碼
特性和屬性的區別
  • 特性(attribute )

    特性是 HTML 元素節點自帶的,瀏覽器在解析 HTML 文檔時,會將標準的特性轉換成屬性,從而使得腳本程序也能夠經過屬性來訪問到這些特性(id、title、class等)。

    <div id = 'test'><div>
    複製代碼
    div.id //因爲標籤id 爲test,因此DOM 對象就會有 body.id="test"。
    div.id = 'test1';
    div.id //test1;修改屬性會致使特性修改。
    複製代碼
    • 自定義特性

      除了標準的特性,還存在自定義特性,須要注意的是自定義特性須要加上 data- 的前綴。這是因爲若是咱們直接使用了非標準的特性(不加data-),之後標準更新後可能會致使衝突!

    <div id = 't' data-test="666"></div>
    複製代碼
    let elem = getElementById('t');
    elem.getAttribute('test') // 666,此時添加到DOM的特性中能夠訪問到
    elem.dataset.test //test,能夠在dataset中獲取到自定義特性
    elem.test //undefined,自定義特性不會添加到屬性中 
    複製代碼
    • 方法

      • elem.hasAttribute(name) — 檢查特性是否存在。

      • elem.getAttribute(name) — 獲取這個特性值。

      • elem.setAttribute(name, value) — 設置這個特性值。

      • elem.removeAttribute(name) — 移除這個特性。

  • 屬性(property )

    image-20200714214333474

    • 每一個對象均可以有屬性,DOM對象內置了不少屬性能夠任意的修改,固然暫時不考慮使用Object.defineProperties修改屬性的權限。因此DOM對象也能夠自由操做屬性(此時只是對象)。
  • 特殊特徵

    • style

      • 特性值訪問的時候包含的是CSS文本
      • 經過屬性訪問則會返回一個對象
    • 事件處理程序(onclick)

      • 經過特性訪問獲取到的是字符串
      • 經過屬性訪問獲取到的是函數

    image-20200714221455172

屬性 特性
類型 任何值,標準的屬性具備規範中描述的類型 字符串
名字 名字(name)是大小寫敏感的 名字(name)是大小寫不敏感的

在大多數狀況下,最好使用 DOM 屬性。僅當 DOM 屬性沒法知足開發需求,而且咱們真的須要特性時,才使用特性,例如:

  • 咱們須要一個非標準的特性。可是若是它以 data- 開頭,那麼咱們應該使用 dataset
  • 咱們想要讀取 HTML 中「所寫的」值。對應的 DOM 屬性可能不一樣,例如 href 屬性一直是一個 完整的 URL,可是咱們想要的是「原始的」值。
元素關係

相似於Node節點之間的關係,只不過當前關係只包含元素沒有text節點、註釋節點等其餘節點。

image-20200715143656603

Document類型

在瀏覽器中document是HTMLDocument對象的一個實例,同時HTMLDocument對象是繼承自 Document 對象。 實際上docuemnt 表示的是 HTML頁面,同時它也是 window 的一個屬性,因此能夠經過全局對象來訪問。經過 document 能夠獲取到瀏覽器文檔中全部節點的數據(包括方法)、以及能夠操做文檔。

文檔信息

如下幾個信息都是 Document 對象沒有的,都和網頁請求有關,這些全部的信息都存在 HTTP 頭部 ,只不過經過這些屬性能夠在Javascript中訪問到它們。

  • document.URL:URL格式爲 <協議>://<域名>:<端口>/<路徑>

  • document.domain:能夠獲取到當前頁面的域名。域名的設置只能愈來愈鬆散,即限制條件愈來愈低。

    可使用document.domain實現跨域,可是前提是必須是同一個基礎域名,且協議和端口都得一致。Javascript 出於對安全的考慮,因此不一樣域的頁面不能互相操做。

    此時若是存在基礎域名和端口都相等的URL,url1 爲a.test.com ,url2 爲 b.test.com, 此時若是url1中的 home.html頁面須要訪問到url2 中的about.html頁面且操做about.html頁面,此時是沒法直接操做的。若是須要操做就須要在url1 和 url2 中分別設置document.domain 爲 test.com。 此時兩個頁面同域,因此能夠互相訪問和修改。

  • document.referrer: 保存着連接到當前頁面的 URL,若是沒有來源頁面可能爲空。

  • document.tiltle:能夠獲取或者修改當前頁面的標題,頁面的標題會在修改後直接更新。

查找元素
  • getElementById 會返回第一個 id 爲指定值的元素且區分大小寫,可是須要注意若是是在 IE8 及更低版本時,不區分大小寫。在 IE7 中調用該方法時會把第一個 name屬性爲該值或者第一個 id 爲該值的元素進行返回.

    <input name="test" type="text" value="Just Test"></input>
    <div id="test"></div>
    
    //document.getElementById('test') 返回input
    複製代碼
  • getElementsByTagName() 能夠獲取到指定節點爲該標籤的全部子節點。子節點的集合即 HTMLCollection 對象。若是須要查看到文檔中全部的元素,能夠經過getElementsByTagName(」*「); HTMLCollection 對象有一個nameItem方法,能夠返回符合當前 name 爲指定值的元素。

  • getElementsByClassName 方法返回文檔中全部指定類名的元素集合,做爲 NodeList 對象。同時能夠同時查找多個類

    var nodeList = document.getElementsByClassName("className1 className2");
    複製代碼
  • getElementsByName() 方法可返回帶有指定名稱的對象的集合

  • querySelector();返回與該模式匹配的第一個元素,可兼容到CSS2

  • querySelectorAll();返回與該模式匹配的全部元素,底層相似於對一組元素的快照,而不是不斷的動態查詢(損耗性能 )

  • document.anchors:包含文檔中全部帶那麼特性的元素。

  • document.forms:包含文檔中全部的元素,與document.getElementByTagName("form")的到的結果相同。

  • document.images:包含文檔中全部的元素,與document.getElementByTagName("img")的到的結果相同。

  • document.links:包含文檔中全部帶href特性的元素。

理解DOM操做的關鍵,就是理解DOM對性能的影響。DOM操做每每是 JavaScript 程序中開銷最大的部分。因爲NodeList是」動態的「,因此每次訪問它的時候都會進行一次新的查詢,最好避免的方法就是少訪問和減小 DOM 操做。

​ -----高程


經常使用擴展

classList

在操做類名的時候能夠經過元素的classList屬性對類名進行添加,刪除或者替換,其實是直接操做className的語法糖,表明着當前元素的集合,其兼容性如圖所示。

image-20200715111352642

  • 方法
    • add(value) 添加類名
    • contains(value) 判斷是否包含類名
    • remove(value) 移除類名
    • toggle(value) 轉換類名,若是該元素沒有這個類名就添加,若是有就刪除這個類名。

焦點管理

  • 得到焦點的辦法

    • 頁面加載
    • 用戶輸入
    • focus方法
    var btn = document.getElementsByTagName('button')[0];
    btn.focus()
    複製代碼
  • 獲取當前焦點元素

    • activeElement() 在文檔加載期間,其返回值爲null,頁面加載成功後爲body
  • 判斷當前文檔內節點是否有焦點

    • hasFocus() 代表當前文檔或者當前文檔內的節點是否得到了焦點。該方法能夠用來判斷當前文檔中的活動元素是否得到了焦點。

參考文檔:

一、特性和屬性(Attributes and properties)

二、《JavaScript 高級程序設計》

相關文章
相關標籤/搜索