一览
今天是第四天啦!笔记的内容主要是跟着慕课网上的jQuery源码解析系列课程以及自己的理解+实践来写的(也比较偏向于自己的梳理,所以可能会有点乱),可能会有错误,欢迎指出。
jQuery对于DOM操作常用的方法大概有以下几种:
- append
- prepend
- before
- after
- replaceWith
- appendTo
- prependTo
- insertBefore
- insertAfter
- replaceAll
其中这里有些方法是对应的,但是它们的目标元素和被瞄准元素位置不一样。
对于DOM的移除有以下四种常用的方法:
- detach
- empty
- remove
- unwrap
插入篇
insertAfter()
比如after()和insertAfter()方法:
$('.inner').after('<p>Test</p>');
$('<p>Test</p>').insertAfter('.inner');
这部分课程讲的不是特别清楚,所以直接搬出源码来看:
jQuery.each(
//可以看到其实insertAfter和After其实本质是一样的
{
appendTo: "append",
prependTo: "prepend",
insertBefore: "before",
insertAfter: "after",
replaceAll: "replaceWith"
}, function( name, original ) {
jQuery.fn[ name ] = function( selector ) {
var elems,
ret = [],
//转为选择器
insert = jQuery( selector ),
last = insert.length - 1,
i = 0;
//给每个选择器去添加对应的元素
for ( ; i <= last; i++ ) {
//如果不是最后一次操作,就把元素进行克隆,因为是p调用了这个方法,所以这里的this是加入的p的jQuery对象
elems = i === last ? this : this.clone( true );
/* 再进行对应方法操作
* 举例:如果是insertAfter则调用了After()方法
*/
jQuery( insert[ i ] )[ original ]( elems );
//把元素添加到ret数组里
push.apply( ret, elems.get() );
}
//构建一个新jQuery对象,以便实现链式
return this.pushStack( ret );
};
} );
append()
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 );
}
});
}
这里用到了一个manipulationTarget函数,用于处理向table元素加入tr但没有tbody的情况,以下是它的源码:
function manipulationTarget( elem, content ) {
return jQuery.nodeName( elem, "table" ) &&
//如果加入内容文档碎片,就用它的孩子来判断是不是tr
jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
//是tr的话如果存在tbody直接返回,否则创建所属文档的tbody给elem(body)后返回
elem.getElementsByTagName( "tbody" )[ 0 ] ||
elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) :
elem;
}
替换篇
replaceWith
replaceWidth方法对元素进行替换,调用的是原生的replaceChild方法,因为涉及到“删除”这个元素,所以会调用cleanData这个方法:
replaceWith: function() {
var arg = arguments[0];
this.domManip(arguments, function(elem) {
arg = this.parentNode;
jQuery.cleanData(getAll(this));
if (arg) {
arg.replaceChild(elem, this);
}
});
return arg && (arg.length || arg.nodeType) ? this : this.remove();
}
最后返回的指向也从原来的元素换成了替换后的元素。
然后再来瞅瞅这个cleanData所做的事情(水很深,就先大概了解一下):
cleanData: function( elems ) {
var data, elem, type,
special = jQuery.event.special, //自定义事件
i = 0;
for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
if ( acceptData( elem ) ) {
if ( ( data = elem[ dataPriv.expando ] ) ) {
//如果data被绑定了事件,就一一移除
if ( data.events ) {
for ( type in data.events ) {
if ( special[ type ] ) {
//用于移除special中的事件
jQuery.event.remove( elem, type );
} else {
jQuery.removeEvent( elem, type, data.handle );
}
}
}
elem[ dataPriv.expando ] = undefined;
}
if ( elem[ dataUser.expando ] ) {
elem[ dataUser.expando ] = undefined;
}
}
}
}
jQuery.event.special中有load事件,focus事件,blur事件,click事件和beforeunload事件等等…里面做了一些处理去弥补原本的不足。
移除篇
innerText是常用的文本清理方法,火狐不兼容,但提供了类似的textContent方法。
两者的区别:
IE中的innerText是需要对innerHTML的值进行:
1.HTML转义;
2.HTML解释和CSS样式解释;
3.剔除格式信息;
4.留下的纯文本。而textContent没有2、3步,在经过了HTML转义之后直接剔除所有html标签后得到的纯文本。
.empty()
为了避免占有本该释放的资源,所以jQuery进行删除前必须要先移除子元素的数据和事件处理函数。。
empty: function() {
var elem,
i = 0;
for (;
(elem = this[i]) != null; i++) {
if (elem.nodeType === 1) {
//进行删前准备工作
jQuery.cleanData(getAll(elem, false));
//元素内容清空
elem.textContent = "";
}
}
return this;
}
jQuery.cleanData方法:
通过元素判断上绑定的expando的这个uuid在与之对应的cache中找到数据与事件句柄加以删除。
remove()
.remove() 将元素移出DOM,可以移除自身元素。
/*
* keepData仅供jQuery内部使用
*/
remove: function(selector, keepData ) {
var elem,
//如果存在选择器,就先过滤自己,不存在则就是自己
elems = selector ? jQuery.filter(selector, this) : this,
i = 0;
for (;
(elem = elems[i]) != null; i++) {
//如果不保留(keepData==false),则先进行事件移除
if (!keepData && elem.nodeType === 1) {
jQuery.cleanData(getAll(elem));
}
//如果元素存在父亲节点
if (elem.parentNode) {
//如果保留事件且元素所属文档存在这个元素
if (keepData && jQuery.contains(elem.ownerDocument, elem)) {
//在全局脚本中进行记录
setGlobalEval(getAll(elem, "script"));
}
//移除元素 elem.parentNode.removeChild(elem);
}
}
return this;
}
因为remove可能是移除自身,所以需要用父亲元素来进行移除。
附加getAll方法:
function getAll( context, tag ) {
//如果context下存在这个标签元素就获取,如果没有结果就用querySelectorAll去获取,再没结果就是空组
var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) :
context.querySelectorAll ? context.querySelectorAll( tag || "*" ) :
[];
return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
jQuery.merge( [ context ], ret ) :
ret;
}
detach()
detach()方法移除被选元素,包括所有文本和子节点。但它
保留jQuery对象中的匹配的元素,因而可以在将来再使用这些匹配的元素,它也
保留所有绑定的事件、附加的数据,这一点与remove() 不同。所以它和remove方法不一样的就是keepData这个属性,只要传入true就可以了:
detach: function(selector) {
return this.remove(selector, true);
}
这个方法可以用于删除后又将会被添加进来的元素。