jQuery内核DOM操作详细教程
- 时间:2015年04月02日 15:17:40 来源:魔法猪系统重装大师官网 人气:14042
domManip是什么
dom即Dom元素,Manip是Manipulate的缩写,连在一起就是Dom操作的意思。
jQuery针对DOM操作的插入的方法有大概10种
append、prepend、before、after、replaceWith
appendTo、prependTo、insertBefore、insertAfter、replaceAll
分2组,上下对照,实现同样的功能。主要的不同是语法——特别是内容和目标的位置
依赖的domManip,buildFragment模块在之前就分析过了
在匹配元素集合中的每个元素后面插入参数所指定的内容,作为其兄弟节点
对于 .after(), 选择表达式在函数的前面,参数是将要插入的内容。
对于.insertAfter(), 刚好相反,内容在方法前面,它将被放在参数里元素的后面。
after
after: function() { return this.domManip( arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this.nextSibling ); } }); },
之前提过了所有的方法靠this.domManip合并参数处理,内部通过buildFragment模块构建文档碎片
然后把每一个方法的具体执行通过回调的方式提供出来处理
DOM操作并未提供一个直接可以在当前节点后插入一个兄弟节点的方法,但是提供了一个类似的方法
insertBefore() 方法:可在已有的子节点前插入一个新的子节点。语法 :insertBefore(newchild,refchild)
看看jQuery如何处理的
例如
inner.after('Test
');
内部就会把 '
Test
' 通过buildFragment构建出文档elem然后通过 this.parentNode.insertBefore( elem, this.nextSibling );
这里的this 就是对应着inner ,elem就是‘
Test
’看到这里就很好理解了after的实现了
用原生方法简单模拟
var inner = document.getElementsByClassName('inner')for(var i =0 ; iinsertAfter
jQuery代码的设计者很聪明的,都尽可能的合并相似功能的方法,代码更加精炼美观
jQuery.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith"}, function( name, original ) { jQuery.fn[ name ] = function( selector ) { }; });DEMO
$('Test
').insertAfter('.inner');通过$('
Test
')构建一个文档,对象通过insertAfter方法插入到所有class等于inner的节点后表达的意思与after是一样的,主要的不同是语法——特别是内容和目标的位置
jQuery.fn[ name ] = function( selector ) { var elems, 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() because core_push.apply(_, arraylike) throws core_push.apply( ret, elems.get() ); } return this.pushStack( ret ); };看具体的实现方法中.insertAfter('.inner');inner其实就被当作selector传入进来了
selector可能只是字符串选择器内部就需要转化,insert = jQuery( selector ),
$('
Test
')就是构建出来的文档碎片节点,那么如果赋给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() { return this.domManip( arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this ); } }); },类似after只是替换了第二个参数,改变插入的位置
append()
在每个匹配元素里面的末尾处插入参数内容
append: function() { return this.domManip( arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem );
target.appendChild( elem ); } }); },内部增加节点,直接可以调用appendChild方法
prepend()
将参数内容插入到每个匹配元素的前面(元素内部)
prepend: function() { return this.domManip( arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.insertBefore( elem, target.firstChild ); } }); },类似after只是替换了第二个参数,改变插入的位置
replaceWith()
用提供的内容替换集合中所有匹配的元素并且返回被删除元素的集合。
.replaceWith()可以从DOM中移除内容,然后在这个地方插入新的内容。请看下面的例子:
HelloAndGoodbye我们可以用指定的HTML替换第二个 inner
:jQuery,内核,DOM,操作,详细,教程,domMani$('div.second').replaceWith('New heading
');结果如下:
HelloNew heading
Goodbye或者我们可以选择一个元素把它当做替换的内容:
$('div.third').replaceWith($('.first'));结果如下:
AndHello从这个例子可以看出,用来替换的元素从老地方移到新位置,而不是复制。
.replaceWith()方法,和大部分其他jQuery方法一样,返回jQuery对象,所以可以和其他方法链接使用,但是需要注意的是:对于该方法而言,该对象指向已经从 DOM 中被移除的对象,而不是指向替换用的对象。
replaceWith: function() { var // Snapshot the DOM in case .domManip sweeps something relevant into its fragment args = jQuery.map( this, function( elem ) { return [ elem.nextSibling, elem.parentNode ]; }), i = 0; // Make the changes, replacing each context element with the new content this.domManip( arguments, function( elem ) { var next = args[ i++ ], parent = args[ i++ ]; if ( parent ) { // Don't use the snapshot next if it has moved (#13810) if ( next && next.parentNode !== parent ) { next = this.nextSibling; } jQuery( this ).remove(); parent.insertBefore( elem, next ); } // Allow new content to include elements from the context set }, true ); // Force removal if there was no new content (e.g., from empty arguments) return i ? this : this.remove(); },删除目标节点
jQuery( this ).remove();
然后再插入一个新节点
parent.insertBefore( elem, next );
.domManip()是jQuery DOM操作的核心函数
对封装的节点操作做了参数上的校正支持,与对应处理的调用
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对接点才操作封装出了一系列的接口,可以接受HTML字符串,DOM 元素,元素数组,或者jQuery对象,用来插在集合中每个匹配元素的不同位置
例如
HTML结构
$('.inner').after('Test
');$对象
$('.container').after($('h2'));回调函数
一个返回HTML字符串,DOM 元素, 或者 jQuery 对象的函数,插在每个匹配元素的后面。接收元素在集合中的索引位置作为参数。在函数中this指向元素集合中的当前元素
$('p').after(function() { return '' + this.className + ''; });domManip源码
针对节点的操作有几个重点的细节
保证最终操作的永远是dom元素,浏览器的最终API只认识那么几个接口,所以如果传递是字符串或者其他的,当然需要转换
针对节点的大量操作,我们肯定是需要引入文档碎片做优化的,这个必不可少
domManip的结构
传递的参数, 对应的处理回调,节点是否替换
domManip: function( args, callback, allowIntersection ) {参数初始化
iNoClone = l - 1, 是否为克隆节点,根据后面的大意,如果当前的jQuery对象是一个合集对象花
那么意味着通过文档碎片构件出来的dom,只能是副本克隆到每一个合集对象中
value 是第一个元素,后边只针对args[0]进行检测,意味着args中的元素必须是统一类型;
WebKit checked属性
如果是回调函数,或者跳过WebKit checked属性
在WebKit中,不能克隆包含了已选中多选按钮的文档碎片,这有什么问题?之后在测
文档碎片
将html转化成dom
其实最终是通过jQuery.buildFragment方法构件出文档碎片
文档碎片的好处就不用多说了,多个绘制操作的时候合并必备
插入页面
这里就用了到iNoClone了
一看代码就很明显 修正了node节点, 为什么要修正?
因为通过文档碎片构建出来的只一样个dom,但是jQuery是一个合集对象,所以都需要用到这个碎片了,所以你把args append到第一个元素上了,jQuery实例的第二个元素他怎么办啊?他没有可以append的了?!所以,上来要判断一下实例的长度是不是大于1,大于1就需要cloneNode。
callback就是对应了所有api需要执行的操作方法了
jQuery2X是高级版本,所以不兼容IE6等低级浏览器了,自然在IE6中插入tr,如果漏掉tbody的问题也就不需要修复了
domManip其实就只做了2事件
第一个就是判断3种传递参数所映射的对应操作
第二个就是通过调用jQuery.buildFragment生成文档碎片