前言
内容主要是跟着慕课网上的jQuery源码解析系列课程以及自己的实践+理解来写的,可能会有错误,欢迎指出^_^。
以往笔记:【笔记】jQuery源码(第一天):第二天:文档处理
这里相关的方法主要有:
jQuery.fn.extend({ text: function() {}, append: function() {}, prepend: function() {}, before: function() {}, after: function() {}, clone: function() {}, html: function() {}, replaceWith: function() {}, domManip: function() {}, })
domManip方法
我们在使用appendChild()等方法添加内容的时候,必须是元素类型。但是在jQuery里面我们可以传入字符串来添加内容,还有添加script等操作。domManip方法就是一个处理中间过渡的函数。
所以针对所有接口的操作,jQuery会抽象出一种参数的处理方案,domManip方法主要做的事情如下: 1:解析参数,字符串,函数,对象。 2:针对大数据引入文档碎片处理。 3:如果参数中包含script的处理。
以及一些细节问题:
IE下面innerHTML会忽略没作用域元素,no-scope element(script,link,style,meta,noscript)等,所以这类通过innerHTML创建需要加前缀。 innerHTML在IE下面会对字符串进行trimLeft操作,去掉空白。 innerHTML不会执行脚本的代码,如果支持defer除外。 很多标签不能作为div的子元素、td、tr, tbody等等 jQuery是合集对象,文档碎片的与事件的复制问题。
模拟简单的append()
function buildFragment(elems, context) { //创建Document的文档碎片 var fragment = context.createDocumentFragment(), nodes = [], i = 0, elem, l = elems.length; for (; i < l; i++) { elem = elems[i]; //创建一个元素div做为容器 tmp = fragment.appendChild(context.createElement("div")); //放到文档碎片中 tmp.innerHTML = elem; } //在这个例子里,运行到此处fragment相当于return fragment;}/** parentEles:被塞内容的容器->数组* target:要添加的内容* callback:创建成功后调用的函数*/function domManip(parentEles, target, callback) { var l = parentEles.length; if (l) { //创建一个碎片,以document形式获取父元素中的第一个 //ownerDocument获取元素所属的Document var fragment = buildFragment([target], parentEles[0].ownerDocument); //获取创建成功的DOM、也就是我们需要添加的对象 first = fragment.childNodes[0]; //把结果返回给回调函数处理 if (first) { callback.call(parentEles, first); } }}function append(parentEles, target) { /** * parentEles:被塞内容的容器 * elem: 要添加的内容 */ return domManip([parentEles], target, function(elem) { //回调函数获取经过domManip处理后的DOM节点,可以直接添加 parentEles.appendChild(elem) });}append(document.getElementById('test'),'慕课网通过append加入慕课网')
Tip:这里的代码我稍微修改了一下,因为似乎有一些并没有用,可能是老师在放的时候是侧重讲这个点的,其他的没删干净。而且这个结果其实是向test添加了一个内容是<div><div>通过append加入慕课网</div></div>,按照常理应该没有外面包裹的div,但是childNodes[0]也好firstchild也好,都只能获取到这个结果,如果div里面有多个div要添加,直接再获取它下面的节点好像也不太好,所以我觉得这里应该是一个粗略的模拟。
处理script
div.innerHTML = "<script>alert('慕课网')";
不会被执行,但是$('div').append("<script>alert('慕课网')")
可以。它类似的函数调用过程是:.append()-> .domManip() -> buildFragment() ->clean()。
append(document.querySelectorAll('div')[0],"<script>alert('慕课网')" )
操作,改进原来的domManip方法: //关闭脚本执行function disableScript(elem) { elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; //"
这里最后的/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g
正则解析图如下:
clean() 中会动态产生一个div, 将div 的innerHTML设为传入的字符串,再用getElementsByTagName('script') 的方式把所有的script 抓出来另行储存。clean()执行完毕回到domManip() 中, domManip() 再将script 们一一拿出来执行。
代码中没有模拟实现,但是看得出clean()函数的作用其实就是把混杂的代码中的脚本单独抽出来进行处理。