
已解决问题
谷歌ylhzxzmx用户在2016.05.07提交了关于“十万个冷笑话jQuery内核 DOM详细操作”的提问,欢迎大家涌跃发表自己的观点。目前共有1个回答,最后更新于2025-03-04T10:38:35。希望大家能够帮助她。详细问题描述及疑问:期待您的答案,你就是当代的活雷锋,太感谢了 !
详细问题描述及疑问:期待您的答案,你就是当代的活雷锋,太感谢了 !
domManip是什么
dom即Dom元
j
append、prepend、
appendTo、prependTo、insertBefore、inse
分2组,
依赖的domManip,buil
在匹配元素集合中的每个元素后面插
对于.after(),选择表达式在函数的前面,参数是将要插
对于.insertAfter(),刚好相反,内容在方法前面
after
之前提过了所有的方法靠this.domManip合并参数处
然
DOM操作并未提供一个直接可以在当前节点后插入一个兄弟节点
insertBefore()方法:可在已有的子节点前插入一个新的子节点。语法:insertBefo
看看jQuery如何处理的
例如
i**就会把'<p>Test</
然后通过t
这里的this就是对应着in
看到这里就很好理解了after的实现了
用原生方法简单模拟
varinner=document.getElementsByinsertAfter
DEMO
$('<p>Test</p>').insertAfter('.inner');通过$('<p>Test</p>')构建一个文档,对象通过insertAfter方法插入到所有class等于inner的节点后
表达的意思与after是一样的,主要的不同是语法——特别是内容和目标的位置
jQuery.fn[name]=function(selector){varelems,ret=[],insert=jQuery(selector),last=insert.length-1,i=0;for(;i<=last;i++){elems=i===last?this:this.clone(true);jQuery(insert[i])[original](elems);//Support:QtWebKit//.get()becausecore_push.apply(_,arraylike)throwscore_push.apply(ret,elems.get());}returnthis.pushStack(ret);};看具体的实现方法中.insertAfter('.inner');inner其实就被当作selector传入进来了
selector可能只是字符串选择器**就需要转化,insert=jQuery(selector),
$('<p>Test</p>')就使建出来的文档碎片节点,那么如果赋给insert有多个的时候就需要完全**一份副本了,所以就直接赋给
elems=i===last?this:this.clone(true);jQuery(insert[i])[original](elems);
依旧是执行after
jQuery(insert[i])[original](elems);
最终还需要返回这个构建的新节点
收集构建的节点
core_push.apply(ret,elems.get());
构建一个新jQuery对象,以便实现链式
this.pushStack(ret);
可见after与insertAfter本质本质其始是一样的,只是通过不同的方式调用
before()
根据参数设定,在匹配元素的前面插入内容
before:function(){returnthis.domManip(arguments,function(elem){if(this.parentNode){this.parentNode.insertBefore(elem,this);}});},类似after只是替换了第二个参数,改变插入的位置
append()
在每个匹配元素里面的末尾处插入参数内容
append:function(){returnthis.domManip(arguments,function(elem)
{if(this.nodeType===1||this.nodeType===11||this.nodeType===9)
{vartarget=manipulationTarget(this,elem);
target.appendChild(elem);}});},**增加节点,直接可以调用appendChild方法
prepend()
将参数内容插入到每个匹配元素的前面(元素**)
prepend:function(){returnthis.domManip(arguments,function(elem){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){vartarget=manipulationTarget(this,elem);target.insertBefore(elem,target.firstChild);}});},类似after只是替换了第二个参数,改变插入的位置
replaceWith()
用提供的内容替换集合中所有匹配的元素并且返回被删除元素的集合。
.replaceWith()可以从DOM中移除内容,然后在这个地方插入新的内容。请看下面的例子:
<divclass="container"><divclass="innerfirst">h**ello</div><divclass="innersecond">And</div><divclass="innerthird">Goodbye</div></div>我们可以用指定的h**TML替换第二个inner<div>:
$('div.second').replaceWith('<h2>Newheading</h2>');结果如下:
<divclass="container"><divclass="innerfirst">h**ello</div><h2>Newheading</h2><divclass="innerthird">Goodbye</div></div>或者我们可以选择一个元素把它当做替换的内容:
$('div.third').replaceWith($('.first'));结果如下:
<divclass="container"><divclass="innersecond">And</div><divclass="innerfirst">h**ello</div></div>从这个例子可以看出,用来替换的元素从老地方移到新位置,而不是复制。
.replaceWith()方法,和大部分其他jQuery方法一样,返回jQuery对象,所以可以和其他方法链接使用,但是需要注意的是:对于该方法而言,该对象指向已经从DOM中被移除的对象,而不是指向替换用的对象。
replaceWith:function(){var//SnapshottheDOMincase.domManipsweepssomethingrelevantintoitsfragmentargs=***.map(this,function(elem){return[elem.nextSibling,elem.parentNode];}),i=0;//M**ethechanges,replacingeachcontextelementwiththenewcontentthis.domManip(arguments,function(elem){varnext=args[i++],parent=args[i++];if(parent){//Don'tusethesnapshotnextifithasmoved(#13810)if(next&&next.parentNode!==parent){next=this.nextSibling;}jQuery(this).remove();parent.insertBefore(elem,next);}//Allownewcontenttoincludeelementsfromthecontextset},true);//Forceremovaliftherewasnonewcontent(e.g.,fromemptyarguments)returni?this:this.remove();},删除目标节点
jQuery(this).remove();
然后再插入一个新节点
parent.insertBefore(elem,next);
.domManip()是jQueryDOM操作的核心函数
对封装的节点操作做了参数上的校正支持,与对应处理的调用
append、prepend、before、after、replaceWith
appendTo、prependTo、insertBefore、insertAfter、replaceAll
为什么需要用这个domManip函数呢?
我们知道节点操作浏览器提供的接口无非就是那么几个
appendChild()
通过把一个节点增加到当前节点的childNodes[]组,给文档树增加节点。
cloneNode()
复制当前节点,或者复制当前节点以及它的所有子孙节点。
hasChildNodes()
如果当前节点拥有子节点,则将返回true。
insertBefore()
给文档树插入一个节点,位置在当前节点的指定子节点之前。如果该节点已经存在,则删除之再插入到它的位置。
removeChild()
从文档树中删除并返回指定的子节点。
replaceChild()
从文档树中删除并返回指定的子节点,用另一个节点替换它。
以上接口都有一个特性,传入的是一个节点元素
如果我们传递不是一个dom节点元素,如果是一个字符串,一个函数或者其他呢,所以针对所有接口的操作,jQuery会抽象出一种参数的处理方案
也就是domManip存在的意义了,针对很多类似接口的参数抽象jQuery**有很多这样的函数了,如之前属性操作中的jQuery.access
jQuery支持的参数传递
jquery对接点才操作封装出了一系列的接口,可以接受h**TML字符串,DOM元素,元素数组,或者jQuery对象,用来插在集合中每个匹配元素的不同位置
例如
h**TML结构
$('.inner').after('<p>Test</p>');$对象
$('.container').after($('h2'));回调函数
一个返回h**TML字符串,DOM元素,或者jQuery对象的函数,插在每个匹配元素的后面。接收元素在集合中的索引位置作为参数。在函数中this指向元素集合中的当前元素
$('p').after(function(){return'<div>'+this.className+'</div>';});domManip源码
针对节点的操作有几个重点的细节
保最终操作的永远是dom元素,浏览器的最终API只认识那么几个接口,所以如果传递是字符串或者其他的,当然需要转换
针对节点的大量操作,我们肯定是需要引入文档碎片做优化的,这个必不可少
domManip的结构
传递的参数,对应的处理回调,节点是否替换
domManip:function(args,callback,allowIntersection){参数初始化
iNoClone=l-1,是否为**节点,根据后面的大意,如果当前的jQuery对象是一个合集对象花
那么意味着通过文档碎片构件出来的dom,只能是副本**到每一个合集对象中
value是第一个元素,后边只针对args[0]进行检测,意味着args中的元素必须是统一类型;
WebKitchecked属性
如果是回调函数,或者跳过WebKitchecked属性
在WebKit中,不能**包含了已选中多选按钮的文档碎片,这有什么问题?之后在测
文档碎片
将html转化成dom
其实最终是通过jQuery.buildFragment方法构件出文档碎片
文档碎片的好处就不用多说了,多个绘制操作的时候合并必备
插入页面
这里就用了到iNoClone了
一看代码就很明显修正了node节点,为什么要修正?
因为通过文档碎片构建出来的只一样个dom,但是jQuery是一个合集对象,所以都需要用到这个碎片了,所以你把argsappend到第一个元素上了,jQuery实例的第二个元素他怎么办啊?他没有可以append的了?!所以,上来要判断一下实例的长度是不是大于1,大于1就需要cloneNode。
callback就是对应了所有api需要执行的操作方法了
jQuery2X是高级版本,所以不兼容IE6等低级浏览器了,自然在IE6中插入tr,如果漏掉tbody的问题也就不需要修复了
domManip其实就只做了2事件
第一个就是判断3种传递参数所映射的对应操作
第二个就是通过调用jQuery.buildFragment生成文档碎片