天天看點

浏覽器原理 -- DOM樹建構WhatWhyHow

What

DOM定義了通路HTML文檔的标準。

根據DOM标準,HTML文檔内容是由節點構成的,節點之間擁有層級關系,構成了一顆節點樹。

也就是說我們看到的HTML頁面資訊,在記憶體中是存放在一顆節點樹上的。

節點可分為元素節點和文本節點兩大類。

<html>
  <head>
    <title>文檔标題</title>
  </head>
  <body>
    <a href="www.baidu.com" target="_blank" rel="external nofollow" >我的連結</a>
    <h1>我的标題</h1>
  </body>
</html>
           

上面HTML文檔對應的DOM樹如下:

浏覽器原理 -- DOM樹建構WhatWhyHow

從上圖可以看到,文本節點是目前元素節點的子節點。

Why

HTML詞法解析得到了token,這些token還需要經過文法分析組成節點,并最終建構成一顆完整的DOM樹。這樣程式才能通路HTML文檔内容。

How

HTML詞法解析是按順序解析字元流的,第一個開始标簽對應的結束标簽應該是最後出現的。為了比對開始标簽和結束标簽,DOM樹的建構可以使用棧結構來處理。

**

第一步,我們先定義元素節點和文本節點兩者的資料結構。

// 元素節點
function Element(){
    this.type = 'Element';
    this.open;            // 開始标簽, 比如 <div id="1">
    this.name;            // 标簽名稱, 全部轉換為小寫
    this.close;           // 結束标簽, 比如 </div>
    this.attributes = []; // 标簽屬性
    this.childNodes = []; // 子節點
}
// 文本節點
function Text(value){
    this.type = 'Text';
    this.value = value;
}
           

第二步,定義一個parse()函數來接收HTML詞法解析得到的token清單,并用一個棧來存儲處理過程中的節點。

function HTMLSyntaticalParser(){
    var stack = [new HTMLDocument];
    this.parse = function(token) {
        // 1.棧頂就是目前節點
        let curNode = stack[stack.length-1];
        let node = buildElement(token);
        switch(token.type){
            case 'StartTag':
                // 2.遇到開始标簽就入棧,目前節點就是這個節點的父節點;
                curNode.childNodes.push(node);
                stack.push(node);
                break;
            case 'EndTag':
                // 3.遇到結束标簽就出棧;
                stack.pop();
                break;
            case 'Text':
                // 4.遇到文本節點,如果目前節點是文本節點,則跟文本節點合并,
                // 否則入棧成為目前節點的子節點;
                if('Element' == curNode.type){
                    curNode.childNodes.push(node);
                    stack.push(node);
                } else if('Text' == curNode.type){
                    curNode.value += node.value;
                }
         }
    }
    this.getOutput = function(){
        // 5.建構過程中,遇到開始标簽就入棧,遇到結束标簽就出棧,最終棧頂就是根節點。
        return stack[0];
    }
}