prepend源码解析(domManip参见博客打开):
<p id="n1"><span id="n2">CodePlayer</span>
</p>
var $n1 = $("#n1");
//将一个strong标记追加到n1内部的起始位置
$n1.prepend( '<strong id="test">追加内容</strong>' );
alert($("#n1")[0].firstChild.id);//打印test,说明prepend是在第一个子元素前面插入的
prepend源码分析:
prepend: function() {
return this.domManip( arguments, function( elem ) {
//把参数传入domManip函数,callback.call( this[i], node, i );其中callback就是这里的第二个函数,给这个函数传入$n1[i]作为上下文this,
//其中node表示prepend函数传入的参数构造成的documentFragment对象,i表示$n1[i]的下标。
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
//其中this就是$("#n1")[i],底层调用$("#n1")[i]的insertBefore方法,该方法接受两个参数:要插入的对象,和作为参照的节点</span>,
//这里要插入的节点就是通过参数构造的documentFragment对象,参照的节点就是调用对象节点$("#n1")[i]的firstChild对象
var target = manipulationTarget( this, elem );
target.insertBefore( elem, target.firstChild );
}
});
}
//调用方式一:
var $n1 = $("#n1");
//将一个strong标记追加到n1内部的起始位置
$n1.prepend( '<strong>追加内容</strong>' );
//调用方式二:
var $n1 = $("#n1");
$n1.prepend( document.getElementsByTagName("label"), $("i") );
//这种调用方式会把传入的两个参数创建出来一个documentFragment对象,然后调用callback.call( this[i], node, i );其中callback就是
//调用domManip传入的参数函数,this[i]就是$n1[i]也就是domManip中的第二个函数参数上下文this,node就是两个prepend参数构造的documentFragment
//对象,i就是$n1[i]的下标
//调用方式三:
var $p = $("p");
$p.prepend( function(index, html){
return '<span>追加元素' + (index + 1) + '</span>';
} );
//会在所有的$p上面调用prepend传入的参数函数,把调用该函数的结果作为参数继续调用domManip,在该参数函数中的上下文是$p[i],
//第一个参数是DON元素下标,第二个参数是该DOM元素封装的jQuery对象调用html方法后得到的结果,也就是innerHTML。
//value.call( this, index, self.html() )。结束后调用domManip本身的回调函数,callback.call( this[i], node, i );
//还是和第一种情况一样,上下文是$p[i],第一个参数是根据节点构造的documentFragment,i是下标。如果参数元素是文档中存在的元素,
//那么这个元素将会从原来的位置上消失!append方法也是一样!
note:还是通过target.insertBefore( elem, target.firstChild ); 来完成的,给出一个例子表明insertBefore是在调用者的内部插入。也就是在调用者的DOM元素的内部插入我们创建的dodumentFragment,参考节点是内部第一个子元素!如果调用者是table,那么要创建tbody标签然后插入!
var returnedNode=someNode.insetBefore(newNode,someNode.firstChild);
console.log(returnedNode==newNode);//true
console.log(newNode==someNode.firstChild)//true
before源码分析:(结果是在调用对象的前面插入,因为是调用父元素的insertBefore,因此成为父元素的第一个子元素)
//(仔细理解domManip函数):
before: function() {
return this.domManip( arguments, function( elem ) {
if ( this.parentNode ) {
this.parentNode.insertBefore( elem, this );
}
});
}
//调用方式一:
$("#n4").before('<span id="n6">span#n6(new)</span>');
//callback.call( this[i], node, i );给domManip的第二个函数传给$("#n4")[i]作为this,node就是构造的documentFragment。
//如果该$("#n4")[0]具有父亲节点,用父亲节点调用insertBefore,参照节点是this,也就是当前节点!所以是在当前节点之前插入!
//调用方式二:
$("#n2").before( document.getElementById("n5") );//在domManip中的逻辑和prepend一样,但是参数的节点会在原来的位置上面消失!
//调用方式三:
$("span").before( function(index, innerHTML){
return '<strong>strong元素' + (index + 1) + '</strong>';
} );//和prepend方法一样
after源码分析:(是在调用者的后面添加,调用parentNode的insertBefore,参考节点是调用者的nextSibling)
after: function() {
return this.domManip( arguments, function( elem ) {
if ( this.parentNode ) {
this.parentNode.insertBefore( elem, this.nextSibling );
}
});
}
//用法和before一致,如果传入的参数是文档中的某些元素那么这些元素将会从原来的位置上消失!
replaceWith源码分析:
replaceWith: function() {
//$("span").replaceWith( '<em class="new">替代元素</em>' );
var arg = arguments[ 0 ];
//获取第0个参数
// Make the changes, replacing each context element with the new content
this.domManip( arguments, function( elem ) {
//this是调用对象的第i个元素是DOM元素,elem是根据参数生成的documentFragment对象
arg = this.parentNode;
//清除该元素上面的所有通过$.data保存的数据
jQuery.cleanData( getAll( this ) );
//如果该调用对象具有父元素,那么用父元素的replaceChild
//该方法接受两个参数:要插入的节点和要替换的节点
if ( arg ) {
//用产生的新节点替换this,this指的是调用对象的第i个元素
//arg.replaceChild(产生的documentFragment,$("xxx")[i])
arg.replaceChild( elem, this );
}
});
// Force removal if there was no new content (e.g., from empty arguments)
//如果父元素存在,同时有length,那么返回选择器对象,否则移除选择的jQuery对象
return arg && (arg.length || arg.nodeType) ? this : this.remove();
}
//如果传入元素,那么该元素就会从该位置移除!
首先给出一个replaceChild的例子(和insertBefore一样是通过父元素调用):
var returnedChild=someNode.replaceChild(newNode,someNode.firstChild);//替换第一个子元素
var returnedNode=someNode.replaceChild(newNode,someNode.lastChild);//替换最后一个子元素
这里使用了this.parentNode.replaceChild(elem,this);也就是说用创建好的documentFragment对象替换掉调用对象!
总结:
(1)清除该调用者对象和子元素的所有的数据和事件
(2)上面几个方法都是调用了domManip函数,该函数里面调用了document.buildFragment,可以仔细阅读该博客
下面再给出一个removeChild的例子(也是父元素调用):
var formerChild=someNode.removeChild(someNode.firstChild);//移除第一个节点
var formerLastChild=someNode.removeChild(someNode.lastChild);//移除最后一个节点
注意:replaceChild和removeChild一样,移除的节点仍然为当前文档所有,只是在文档中已经没有自己的位置!