前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >jQuery源码解析之after()/insertAfter()/before()/prepend()的实现

jQuery源码解析之after()/insertAfter()/before()/prepend()的实现

作者头像
进击的小进进
发布2022-03-28 14:49:00
9840
发布2022-03-28 14:49:00
举报

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

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

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


一、示例HTML

代码语言:javascript
复制
<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 元素

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

简单实现:

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

源码:

代码语言:javascript
复制
//在被选元素之后插入指定的内容(不是内部)
    //会移动已有节点到指定位置
    //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 元素

代码语言:javascript
复制
$("#divTwo").insertAfter($("#divOne"))
//等价于
$("#divOne").after($("#divTwo"))

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

源码很有意思:

代码语言:javascript
复制
  //源码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 和 待插入元素 调换位置

代码语言:javascript
复制
let insert = jQuery( selector )
//$(xxx).after(yyy)
 jQuery( insert[ i ] )[ original ]( elems )

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

代码语言:javascript
复制
   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对象,用于链式调用

代码语言:javascript
复制
return this.pushStack( ret );

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


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

源码:

代码语言:javascript
复制
    //在被选元素内部的开头插入指定内容
    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 );
        }
      } );
    },

解析:

代码语言:javascript
复制
//=========prepend()===========
  $("#divTwo").prepend($("#divOne"))
  //上面的等价于
  divTwo.insertBefore( divOne, divTwo.firstChild )

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


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

源码:

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

解析:

代码语言:javascript
复制
// =========before()===========
  $("#divTwo").before($("#divOne"))
// 上面的等价于  
  divTwo.parentNode.insertBefore( divOne, divTwo );

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 webchen 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档