解密jQuery內核 DOM操做

jQuery針對DOM操做的插入的方法有大概10種html

append、prepend、before、after、replaceWithnode

appendTo、prependTo、insertBefore、insertAfter、replaceAllapp

分2組,上下對照,實現一樣的功能。主要的不一樣是語法——特別是內容和目標的位置dom

依賴的domManipbuildFragment模塊在以前就分析過了函數

 


在匹配元素集合中的每一個元素後面插入參數所指定的內容,做爲其兄弟節點ui

對於 .after(), 選擇表達式在函數的前面,參數是將要插入的內容。this

對於.insertAfter(), 恰好相反,內容在方法前面,它將被放在參數裏元素的後面。spa

after

after: function() {
    return this.domManip( arguments, function( elem ) {
        if ( this.parentNode ) {
            this.parentNode.insertBefore( elem, this.nextSibling );
        }
    });
},

以前提過了全部的方法靠this.domManip合併參數處理,內部經過buildFragment模塊構建文檔碎片設計

而後把每個方法的具體執行經過回調的方式提供出來處理code

DOM操做並未提供一個直接能夠在當前節點後插入一個兄弟節點的方法,可是提供了一個相似的方法

insertBefore() 方法:可在已有的子節點前插入一個新的子節點。語法 :insertBefore(newchild,refchild)

看看jQuery如何處理的

例如

inner.after('<p>Test</p>');

內部就會把  '<p>Test</p>' 經過buildFragment構建出文檔elem

而後經過  this.parentNode.insertBefore( elem, this.nextSibling );

這裏的this 就是對應着inner ,elem就是‘<p>Test</p>’

看到這裏就很好理解了after的實現了

用原生方法簡單模擬

var  inner = document.getElementsByClassName('inner')
for(var i =0 ; i<inner.length;i++){
    var elem = inner[i]
    var div = document.createElement('div')
    div.innerHTML = 'aaaa'
    elem.parentNode.insertBefore(div,elem.nextSibling)
}

 


insertAfter

jQuery代碼的設計者很聰明的,都儘量的合併類似功能的方法,代碼更加精煉美觀

jQuery.each({
    appendTo: "append",
    prependTo: "prepend",
    insertBefore: "before",
    insertAfter: "after",
    replaceAll: "replaceWith"
}, function( name, original ) {
    jQuery.fn[ name ] = function( selector ) {
                           
    };
});

DEMO

$('<p>Test</p>').insertAfter('.inner');

經過$('<p>Test</p>')構建一個文檔,對象經過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 ),

 

$('<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() {
        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中移除內容,而後在這個地方插入新的內容。請看下面的例子:

<div class="container">
  <div class="inner first">Hello</div>
  <div class="inner second">And</div>
  <div class="inner third">Goodbye</div>
</div>

咱們能夠用指定的HTML替換第二個 inner <div>

$('div.second').replaceWith('<h2>New heading</h2>');

結果以下:

<div class="container">
  <div class="inner first">Hello</div>
  <h2>New heading</h2>
  <div class="inner third">Goodbye</div>
</div>

或者咱們能夠選擇一個元素把它當作替換的內容:

$('div.third').replaceWith($('.first'));

結果以下:

 

<div class="container">
  <div class="inner second">And</div>
  <div class="inner first">Hello</div>
</div>

從這個例子能夠看出,用來替換的元素從老地方移到新位置,而不是複製。

.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 );

相關文章
相關標籤/搜索