美文网首页
jQuery源码解析之after()/insertAfter()

jQuery源码解析之after()/insertAfter()

作者: 小进进不将就 | 来源:发表于2019-04-05 19:13 被阅读0次

前言:当我调用了$().append()后,jQuery内部发生了什么? 一样,after() / insertAfteer() / before() / prepend(),都会经过 domManip()buildFragment() 的洗礼,最后调用原生JS的方法来实现。

所以,本文只讲述 jQuery 中最后对 after() / insertAfter() / before() / prepend() 处理的相关代码。

想了解domManip()buildFragment()的,请看 当我调用了$().append()后,jQuery内部发生了什么?


一、示例HTML

<script src="jQuery.js"></script>
<div id="divTwo">
  <p>这是divTwo</p>
</div>
<div id="divOne">
  <p>这是divOne</p>
</div>
<script>
  let divOne = document.querySelector("#divOne")
  let divTwo = document.querySelector("#divTwo")
</script>

二、after()
作用:在被选元素(外部)后 插入 HTML 元素

注意:会移动已有节点到指定位置

简单实现:

let divOne = document.querySelector("#divOne")
let divTwo = document.querySelector("#divTwo")
//在divOne的第一个child之前插入divTwo
divOne.parentNode.insertBefore(divTwo, divOne.nextSibling)
//上面的等价于
$("#divOne").after($("#divTwo"))

源码:

//在被选元素之后插入指定的内容(不是内部)
    //会移动已有节点到指定位置
    //http://www.runoob.com/jquery/html-after.html
    //源码6225行
    after: function() {
      return domManip( this, arguments, function( elem ) {
        if ( this.parentNode ) {
          //a.insertBefore(elem, a.firstElementChild )
          //在a的第一个child之前插入elem
          //由父节点调用insertBefore,在目标节点的后一节点 的前面插入新节点
          this.parentNode.insertBefore( elem, this.nextSibling );
        }
      } );
    },

解析:
可以看到,在经历了 domManip 的洗礼后,返回符合规范的 elem 即待插入元素,
然后 this 表示 selector ,
this 的父节点存在的情况下调用 this.parentNode.insertBefore( elem, this.nextSibling ) 完成 after() 操作。

三、insertAfter()
作用:在被选元素(外部)后 插入 HTML 元素

$("#divTwo").insertAfter($("#divOne"))
//等价于
$("#divOne").after($("#divTwo"))

注意:和 after() 作用一样,只是调用的元素顺序相反

源码很有意思:

  //源码6340行
  jQuery.each( {
    // 在被选元素(内部)的结尾插入 HTML 元素
    appendTo: "append",
    // 在被选元素(内部)的开头插入 HTML 元素
    prependTo: "prepend",
    // 在被选元素(外部)前插入 HTML 元素
    insertBefore: "before",
    // 在被选元素(外部)后插入 HTML 元素
    insertAfter: "after",
    // 把被选元素(整个)替换为新的 HTML 元素
    replaceAll: "replaceWith"
  },
    //key,value
    function( name, original ) {
    //insertAfter
    jQuery.fn[ name ] = function( selector ) {

      var elems,
        ret = [],
        insert = jQuery( selector ),
        last = insert.length - 1,
        i = 0;
      //根据selector的个数来循环
      for ( ; i <= last; i++ ) {
        //如果有多个选择器,就将待插入的节点深复制
        elems = i === last ? this : this.clone( true );
        //$().after(elem)
        jQuery( insert[ i ] )[ original ]( elems );

        // Support: Android <=4.0 only, PhantomJS 1 only
        // .get() because push.apply(_, arraylike) throws on ancient WebKit
        //push:array.push()
        //ret.push(elems.get()) 作用就是将待插入的DOM节点依次放进ret数组中
        //$().get():获取selector的DOM元素
        push.apply( ret, elems.get() );
      }

      //$().pushStack将一个DOM集合压入jQuery栈,并返回DOM集合的jQuery对象,用于链式调用
      return this.pushStack( ret );
    };
  } );

解析:insertAfter() 是如何偷懒,调用 after()的?

(1)调用 $.each( { key: xxx, value: yyy } , function( key, value ){ } ) 依次定义 appendTo() / prependTo() / insertBefore() / insertAfter() / replaceAll()

(2)将 selector 和 待插入元素 调换位置

let insert = jQuery( selector )
//$(xxx).after(yyy)
 jQuery( insert[ i ] )[ original ]( elems )

(3)根据 selector 的个数作循环,深复制待插入的节点,并依次调用 after() 方法

   for ( ; i <= last; i++ ) {
        //如果有多个选择器,就将待插入的节点深复制
        elems = i === last ? this : this.clone( true );
        //$().after(elem)
        jQuery( insert[ i ] )[ original ]( elems );
        push.apply( ret, elems.get() );
      }

(4)最后,调用 $().pushStack 依次返回处理后 新的 jQuery对象,用于链式调用

return this.pushStack( ret );

注意:appendTo() / prependTo() / insertBefore() / replaceAll() 实现方法类似,就不一一解释了。


四、prepend()
作用:在被选元素(内部)的开头插入 HTML 元素

源码:

    //在被选元素内部的开头插入指定内容
    prepend: function() {
      return domManip( this, arguments, function( elem ) {
        if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
          var target = manipulationTarget( this, elem );
          target.insertBefore( elem, target.firstChild );
        }
      } );
    },

解析:

//=========prepend()===========
  $("#divTwo").prepend($("#divOne"))
  //上面的等价于
  divTwo.insertBefore( divOne, divTwo.firstChild )

prepend() 其实是调用了 原生 insertBefore() 方法,也就是在 divTwo 内部的第一个子节点前插入 divOne


五、before()
作用:在被选元素(外部)之前插入 HTML 元素

源码:

    //在被选元素之前插入指定的内容
    //源码6218行
    before: function() {
      return domManip( this, arguments, function( elem ) {
        if ( this.parentNode ) {
          this.parentNode.insertBefore( elem, this );
        }
      } );
    },

解析:

// =========before()===========
  $("#divTwo").before($("#divOne"))
// 上面的等价于  
  divTwo.parentNode.insertBefore( divOne, divTwo );

before() 其实是调用了 原生 insertBefore() 方法,也就是在 selector 的父节点的内部,在 divTwo 前插入 divOne


(完)

相关文章

网友评论

      本文标题:jQuery源码解析之after()/insertAfter()

      本文链接:https://www.haomeiwen.com/subject/kaqobqtx.html