innerHTML是HTML5的一个标准属性,js用户既可以读也可以写,下面我们主要讲一下在设置innerHTML的时候,webkit中是如何实现该属性(具体的用法可以参照规范)

1. 写

首先Webkit中引入了一个数据结构叫DocumentFragment,翻译成中文是“文档片段”的意思,其实就是只一个非完整的html页面,

该数据结构的继承关系如下:

可以见DocumentFragment是作为一个容器节点而存在,注意DocumentFrament中引用了一个Document,这个Document就是被设置innerHTML元素的Document。

当设置innerHTML属性的时候,webkit会首先生成一个DocumentFragment对象,然后调用DocumentFragment的parseHTML方法.

DF里面会首先生成一个HTMLDocumentParser对象,再通过HTMLDocumentParser的insert(string), 方法,解析出该innerHTML串。最后得到的DF对象其实就是一颗子树,再将该子树append到被设置innerHTML的元素(我们简称为 innerElem)上面,注意这里append的时候,会将innerElem的孩子先remove掉。其实整个过程是比较简单,在规范中也有提到这些 步骤。但是webkit是如何将完整html页面解析和局部的一个html片段解析统一起来的呢?

首先从HTMLDocumentParser讲起,HTMLDocumentParser的输入在webkit其实分为两种(通过构造函数传入),一种是一个元素对应于文档片段的解析,一种是document,对应于完整页面的解析。

当生成一个Parser的时候,前面的文章提到过,在其中其实也有一个HTMLTreebuilder组件会生成,在HTMLTreeBuilder的构造函数中,会有一个条件判断,其实就是判断你对parser的输入如果是一个元素,那么他会做这样几件事情:

第一件事: processFakeStartTag(htmlTag);//默认为你生成一个html标签

第二件事:resetInsertionModeAppropriately();//根据你innerHTML的串来决定dom树构建的模式

如果你设置innerHTML类似:

div.innerHTML="<P></P>";

那么TreeBuilder的插入模式是InBodyMode

第三件事://设置表单元素

 m_tree.setForm(closestFormAncestor(contextElement));

下面我们重点看第二件事,

webkit在构建dom树的时候,定义了很多模式

 enum InsertionMode {

        InitialMode,

        BeforeHTMLMode,

        BeforeHeadMode,

        InHeadMode,

        InHeadNoscriptMode,

        AfterHeadMode,

        InBodyMode,

        TextMode,

        InTableMode,

        InTableTextMode,

        InCaptionMode,

        InColumnGroupMode,

        InTableBodyMode,

        InRowMode,

        InCellMode,

        InSelectMode,

        InSelectInTableMode,

        InForeignContentMode,

        AfterBodyMode,

        InFramesetMode,

        AfterFramesetMode,

        AfterAfterBodyMode,

        AfterAfterFramesetMode,

    };

这些模式其实是一些状态,webkit在解析html页面生成dom树的时候,其实是用的一个状态机完成, 比如初始时一定是initMode,然后必然是BeforeHTMLMode。每一种模式在不同情况下(不同的情况是指:比如处于解析开始标签和解析结尾 标签这是两种情况,还有一种是在标签中,也有一种默认行为,在后续文章我会详细讲到)都会有一种默认行为,在处理开始标签的情况下,比如initMode 的默认行为是设置状态为BeforeHTMLMode,而在状态BeforeHTMLMode的时候(在本文中模式和状态可以混用),默认行为是生成一个 html标签(对应于用户没有在html页面中写入html标签的情况),但是如果用户在html页面中写入了html标签,就不会执行默认行为,以下是 在BeforeHTMLMode下的代码:

 case BeforeHTMLMode:  

ASSERT(insertionMode() == BeforeHTMLMode);

        if (token.name() == htmlTag) {

            m_tree.insertHTMLHtmlStartTagBeforeHTML(token);

            setInsertionMode(BeforeHeadMode);

            return;

        }

        defaultForBeforeHTML();

void HTMLTreeBuilder::defaultForBeforeHTML()

{

    AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName());

    m_tree.insertHTMLHtmlStartTagBeforeHTML(startHTML);

    setInsertionMode(BeforeHeadMode);

    prepareToReprocessToken();

}

//这是BeforeHeadMode状态下的默认行为

void HTMLTreeBuilder::defaultForBeforeHead()

{

    AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName());

    processStartTag(startHead);

    prepareToReprocessToken();

}

比如我们要求html标签下面是必须要有head标签和body标签,如果你设置

html.innerHTML="<DIV>DD</DIV>",(html代表html元素),那么webkit会默认给你生成head标签元素和body标签元素, 而这些就是根据这些状态的默认行为实现的。

转自:http://blog.csdn.net/shunzi__1984/article/details/6329444