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
转载于:https://blog.51cto.com/hoogle/719252