一覽
今天是第四天啦!筆記的內容主要是跟著慕課網上的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);
}
這個方法可以用於刪除後又將會被新增進來的元素。