`
adamed
  • 浏览: 183819 次
社区版块
存档分类
最新评论

jQuery源码历代记4

 
阅读更多

之前文章传送门:http://adamed.iteye.com/category/207898

 

最近工作忙,居然好久没写博客了。好容易写了一点,就先传上来免得自己懈怠了。

 

 

 

 

下面要讲解的是jQuery1.01构造器中的wrap方法,顾名思义就是将选择器选中的结果集中每一个元素都用某一个HTML元素包装起来。

不多说先上官方API介绍:

.wrap( wrappingElement )  

wrappingElement : An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the matched elements.

 

 

Description: Wrap an HTML structure around each element in the set of matched elements.
 

看下实际使用时例子:

HTML代码如下:

<div class="container">

    <div class="inner">Hello</div>

    <div class="inner">Goodbye</div>

</div>

执行JS代码:

$('.inner').wrap('<div class="new" />');

结果如下:

<div class="container">

    <div class="new">

        <div class="inner">Hello</div>

    </div>

    <div class="new">

        <div class="inner">Goodbye</div>

    </div>

</div>

好了下面上源码: 

 

 

wrap : function() {
    // The elements to wrap the target around
    var a = jQuery.clean( arguments );
    // Wrap each of the matched elements individually
    return this.each( function() {
        // Clone the structure that we're using to wrap
        var b = a[0].cloneNode( true );
        // Insert it before the element to be wrapped
        this.parentNode.insertBefore( b , this );
        // Find he deepest point in the wrap structure
        while ( b.firstChild )
            b = b.firstChild;
        // Move the matched element to within the wrap structure
        b.appendChild( this );
     } );
}
 

 

代码中首先运行了jQuery.clean这是很多地方都使用的方法: 

该方法的作用是将参数转换成DOM元素,这个方法可以说是每次jQuery升级变动最大的一个方法。

基本上每次都会增加很多很多内容,当前我们介绍的还是jQuery最最初级的1.01版所以看起来功能单一了一些。

 

 

clean : function( a ) {
    var r = [];
    for ( var i = 0; i < a.length; i++ ) {
        if ( a[i].constructor == String ) {
            var table = "";
            if ( !a[i].indexOf( "<thead" ) || !a[i].indexOf( "<tbody" ) ) {
                table = "thead";
                a[i] = "<table>" + a[i] + "</table>";
            } else if ( !a[i].indexOf( "<tr" ) ) {
                table = "tr";
                a[i] = "<table>" + a[i] + "</table>";
            } else if ( !a[i].indexOf( "<td" ) || !a[i].indexOf( "<th" ) ) {
                table = "td";
                a[i] = "<table><tbody><tr>" + a[i] + "</tr></tbody></table>";
            }
            var div = document.createElement( "div" );
            div.innerHTML = a[i];
            if ( table ) {
                div = div.firstChild;
                if ( table != "thead" ) div = div.firstChild;
                if ( table == "td" ) div = div.firstChild;
            }
            for ( var j = 0; j < div.childNodes.length; j++ )
                r.push( div.childNodes[j] );
        } else if ( a[i].jquery || a[i].length && !a[i].nodeType )
            for ( var k = 0; k < a[i].length; k++ )
                r.push( a[i][k] );
        else if ( a[i] !== null )
            r.push( a[i].nodeType ? a[i] :
                  document.createTextNode( a[i].toString() ) );
    }
    return r;
}
 

 

 我们看下!a[i].indexOf( "<thead" ) 这一句是什么意思呢?

表面看是判断字符串a[i]是否包含"<thead"还有一个取反的操作。

这里的indexOf如果包含则返回字符串出现的位置,假如a[i]字符串为"<thead>"则a[i].indexOf( "<thead" )返回0,取反则返回true。

这里要注意的是如果不包含(例如"<div>")或者包含但不是起始位置(例如:“<div><thead”)则返回-1或大于0的数字取反之后均返回false。所以上面那句的意思就是类似于 a[i].beginWith("<thead") (beginWith是我自己瞎编的方法不是JS的标准方法哦。)

这里还可以看出1.01还是有一些问题的,比如如果带空格什么的也会造成问题。(例如:"   <thead>")

源码中的很多if。。。else if都是类似的这里解释一段:

if ( !a[i].indexOf( "<thead" ) || !a[i].indexOf( "<tbody" ) ) {

    table = "thead";

    a[i] = "<table>" + a[i] + "</table>";

}

意思就是判断a[i]是否以"<thead"或者"<tbody"开头("    <thead></thead>"这种写法将导致判断失败)如果是则推断:该字符串是thead标签字符串将其放入<table>中最后使用innerHTML生成DOM。

这里看该判断还是有些武断比如如果传入的是"<thead></thead><div />"将导致生成的DOM出现问题。

var div = document.createElement( "div" );

div.innerHTML = a[i];

这2句创建一个新的div将刚才整理出来的字符串使用innerHTML生成DOM。

例如传入:"<thead></thead>"则返回"<table><thead></thead></table>"

生成的DOM就是:

<div>

    <table>

        <thead></thead>

    </table>

</div>

下面的代码:

  if ( table ) {

      div = div.firstChild;

      if ( table != "thead" ) div = div.firstChild;

      if ( table == "td" ) div = div.firstChild;

  }

我们需要知道的是if("")返回是false 但是if("a")返回是true。知道这些就不难理解上面的代码了

 else if ( a[i].jquery || a[i].length && !a[i].nodeType )是判断传入的参数是否为jQuery对象,如果是则迭代放入返回的数组中。

如果传入的参数既不是字符串又不是jQuery的对象则判断该对象是否包含nodeType属性如果包含则认为是DOM否则就调用toString方法生成文本节点(好生猛的处理方式啊。。。。)

 a[i].nodeType ? a[i] : document.createTextNode( a[i].toString() )

由于clean方法每个版本变化都非常大,所以其他版本的jQuery的clean方法在降到该版本时再介绍吧。

 //***************************这是重要的分界线**********************

 我们再回过头来看wrap的代码,在clean处理之后返回数组a之后:

 

 

return this.each( function() {
     // Clone the structure that we're using to wrap
     var b = a[0].cloneNode( true );
     // Insert it before the element to be wrapped
     this.parentNode.insertBefore( b , this );
     // Find he deepest point in the wrap structure
     while ( b.firstChild ){
          b = b.firstChild;
    }
     // Move the matched element to within the wrap structure
     b.appendChild( this );
} );
 

wrap方法返回的是each方法执行的结果,也就是this。

根据wrap的api,该方法仅传一个字符串类型的参数。处理时我们也是只取a[0]:var b = a[0].cloneNode( true );

如果wrap方法调用时传入的是多个参数那么a的length将大于1,我们忽略除第一个参数以外的其余参数。

变量b指向的是将a[0]克隆(包括子节点)的新节点。

而后将新创建的b节点插入当前节点之前并遍历b节点找到最深层次的首节点而后将当前节点作为最深子节点的子节点。

这里请注意appendChild的这个用法。(W3CSchool里的描述:You can also use the appendChild method to move an element from one element to another.)

 

  //***************************这是重要的分界线**********************

下面将一起讲解4个实现代码类似的方法:

 

append : function() {
    return this.domManip( arguments , true , 1 , function( a ) {
        this.appendChild( a );
    } );
},
prepend : function() {
    return this.domManip( arguments , true , -1 , function( a ) {
        this.insertBefore( a , this.firstChild );
    } );
},
before : function() {
    return this.domManip( arguments , false , 1 , function( a ) {
        this.parentNode.insertBefore( a , this );
    } );
},
after : function() {
    return this.domManip( arguments , false , -1 , function( a ) {
        this.parentNode.insertBefore( a , this.nextSibling );
    } );
}
 

 

这几个方法作用都很简单通过名字就可以很轻松的判断出来,他们的实现也很类似主要是通过domManip方法来实现的功能。

因为这4个构造器中的方法实现基本类似所以就讲解append和before剩余2个不讲了。

 //***************************这是重要的分界线**********************

先上append的API说明: 

 

Description: Insert content, specified by the parameter, to the end of each element in the set of matched elements.
 

 

.append( content [, content] )

content : DOM element, HTML string, or jQuery object to insert at the end of each element in the set of matched elements.

content : One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the end of each element in the set of matched elements.

样例说明--HTML代码:

<h2>Greetings</h2>

    <div class="container">

    <div class="inner">Hello</div>

    <div class="inner">Goodbye</div>

</div>

样例说明---JS代码:

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

样例说明---结果HTML:

<h2>Greetings</h2>

<div class="container">

    <div class="inner">

        Hello

        <p>Test</p>

    </div>

    <div class="inner">

        Goodbye

        <p>Test</p>

    </div>

</div>

上面的原始HTML若执行:

$('.container').append($('h2'));

则结果HTML为:

<div class="container">

    <div class="inner">Hello</div>

    <div class="inner">Goodbye</div>

    <h2>Greetings</h2>

</div>

注意:append可以接收任意数量的参数。例如:

var $newdiv1 = $('<div id="object1"/>'),

newdiv2 = document.createElement('div'),

existingdiv1 = document.getElementById('foo');

$('body').append($newdiv1, [newdiv2, existingdiv1]);

等价于:$('body').append($newdiv1, newdiv2, existingdiv1);或者:$('body').append([$newdiv1,newdiv2, existingdiv1]);

 //***************************这是重要的分界线**********************

由于append实现的主要代码就是一句domManip,故我们先分析一下这个构造器方法的代码:

 

domManip : function( args , table , dir , fn ) {
    var clone = this.size() > 1;
    var a = jQuery.clean( args );
    return this.each( function() {
        var obj = this;
        if ( table && this.nodeName == "TABLE" && a[0].nodeName != "THEAD" ) {
            var tbody = this.getElementsByTagName( "tbody" );
            if ( !tbody.length ) {
                obj = document.createElement( "tbody" );
                this.appendChild( obj );
            } else
                obj = tbody[0];
        }
        for ( var i = ( dir < 0 ? a.length - 1 : 0 ); i != ( dir < 0 ? dir: a.length ); i += dir ) {
            fn.apply( obj , [ clone ? a[i].cloneNode( true ) : a[i]] );
        }
    } );
}
 

 

 此方法的实现和后面的jQuery版本比较起来还是比较简单的。

我们分析一下:

var clone = this.size() > 1;这一句是判断调用domManip的对象是否是一个数组或类数据。

var a = jQuery.clean( args );这一句就更明显了,之前已经讲过clean的用法了,这句的意思就是将args的字符串转换成DOM对象。

遍历this,对于每一个元素执行return的那个方法。

return方法中前面部分判断如果当前this遍历的元素是table则添加一个tbody的子元素而后迭代自身。

主要起作用的方法就是下面的那个for循环。

 //***************************这是重要的分界线**********************

现在使用下面的代码来逐句解释源码:

样例说明--HTML代码:

<h2>Greetings</h2>

    <div class="container">

    <div class="inner">Hello</div>

    <div class="inner">Goodbye</div>

</div>

样例说明---JS代码:

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

解读:

$('.inner')返回jQuery的集合对象里面是[div,div](类似的结构)

调用append:

append : function() {

     //这里的this就是那2个div

    //arguments就是字符串'<p>Test</p>'

    return this.domManip( arguments , true , 1 , function( a ) {

        this.appendChild( a );

    } );

},

我们在对应一下domManip,由于里面没有table所以就把table部分删掉了:

 

domManip : function( args , table , dir , fn ) {
    //args的值为字符串'<p>Test</p>'
    //table值为true
    //dir的值为1
    //fn为匿名函数
    //this就是那2个div,所以clone为true
    var clone = this.size() > 1;
    //a的值为DOM对象<p>Test</p>
    var a = jQuery.clean( args );
    //返回的是2个div对象,但是每个对象都执行了匿名方法。
    //这里用第一个div举例
    return this.each( function() {
        //obj的值为jQuery对象:<div class="inner">Hello</div>
        var obj = this;
        //这里dir=1 ,a.length=1
        //相当于:for(var i=0; i!=1; i+=1)
        for ( var i = ( dir < 0 ? a.length - 1 : 0 ); i != ( dir < 0 ? dir: a.length ); i += dir ) {
            //clone为true所以相当于:
            //fn.apply(obj,a[i].cloneNode(true));
            fn.apply( obj , [ clone ? a[i].cloneNode( true ) : a[i]] );
      /*
      也就是相当于调用了:
      var a=function( a ) {
              this.appendChild( a );
      }
      $('<div class="inner">Hello</div>').a();
       里面的this为<div class="inner">Hello</div>所以执行完就是
        <div class="inner">Hello<p>Test</p></div>
      */
        }
    } );
}
 

如果调用的方法是:

 

prepend : function() {
    return this.domManip( arguments , true , -1 , function( a ) {
        this.insertBefore( a , this.firstChild );
    } );
}
 

则最后的那个for循环有一些区别:

 for ( var i = ( dir < 0 ? a.length - 1 : 0 ); i != ( dir < 0 ? dir: a.length ); i += dir ) {.....}

  //这里dir=-1 ,a.length=1

 for ( var i = 0i !=-1; i += -1) {.....}

分享到:
评论

相关推荐

    jQuery源码 jQuery源码 jQuery源码

    jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码...

    JQuery源码详细中文注释_Jquery源码分析_

    《jQuery源码深度解析》 jQuery,作为一款广泛使用的JavaScript库,因其简洁的API和强大的功能,深受开发者喜爱。本文将深入探讨jQuery 1.2.6版本的源码,结合其中的中文注释,帮助读者理解其内部机制,提升...

    Jquery源码分析 源码

    《jQuery源码分析》 jQuery,作为一款广泛使用的JavaScript库,极大地简化了DOM操作、事件处理、动画制作以及Ajax交互。深入理解其源码对于提升JavaScript编程技能和优化前端性能至关重要。本文将从核心概念、设计...

    jQuery源码分析系列.pdf

    ### jQuery源码分析系列知识点概览 #### 一、总体架构与核心概念 - **前言开光**:介绍分析jQuery源码的目的和价值,强调通过深入研究源码,可以学习到先进的设计理念和实践技巧。 - **总体架构**:解析jQuery的...

    jquery源码,3.7.0版本

    jquery源码,3.7.0版本

    jquery源码分析

    jquery源码分析,包括入口技术,选择器入口,以及在选择器使用的时候需要注意的优化思路

    Head First jquery源码

    《Head First jQuery源码》是一本深入解析jQuery库的书籍,其内容主要涵盖了jQuery的核心功能、设计理念以及实现机制。jQuery是JavaScript的一个库,它极大地简化了网页中的DOM操作、事件处理、动画效果以及Ajax交互...

    jquery源码框架解读

    《jQuery源码框架解读》 在前端开发领域,jQuery是一个不可或缺的库,它极大地简化了JavaScript操作DOM、处理事件、创建动画以及实现Ajax交互。这份资料深入解析了jQuery的源码,旨在帮助开发者理解其内部机制,...

    Head First jQuery源码

    4. **事件处理**:jQuery封装了事件处理,如`click()`, `mouseover()`, `keydown()`等,使得绑定和解绑事件变得简单。源码里,我们可以看到这些事件是如何被注册和触发的。 5. **动画效果**:jQuery的`animate()`...

    jQuery源码+中文详细注解

    ### jQuery源码+中文详细注解 #### 一、引言 本文档是对jQuery源码进行中文注解的详细介绍,旨在帮助广大前端开发者更深入地理解jQuery的核心逻辑和技术要点。通过对核心部分的逐行注解,可以更好地掌握jQuery的...

    jquery源码(1.4)

    jquery最新源码jquery最新源码jquery最新源码

    jQuery源码分析-插件

    jQuery源码分析-插件

    jQuery源码分析视频教程

    jQuery源码分析视频教程总计116课,按照jquery每个区段实现的内容,精确地讲解源码中是怎么实现我们开发中用到的jquery得方法的

    jQuery源码分析系列中文PDF版

    资源名称:jQuery源码分析系列 中文PDF版内容简介:jQuery凭借简洁的语法和跨平台的兼容性,极大地简化了Javascript开发开发人员遍历HTML文档、操作DOM、处理事件、执行动画和开发Ajax的操作。其独特而又优雅...

    jquery api, jquery ui api, jquery源码分析

    **jQuery API** jQuery 是一款广泛使用的 JavaScript 库,它的出现极大地简化了网页的 ...通过学习jQuery API、jQuery UI API以及源码分析,开发者不仅可以提升开发效率,还能编写出更高效、更优雅的JavaScript代码。

    jQuery 源码+实例+注释

    **jQuery 源码分析与实例详解** jQuery 是一个广泛使用的 JavaScript 库,它极大地简化了 JavaScript 的 DOM 操作、事件处理、动画制作以及 AJAX 交互。本教程旨在通过源码解析、实例演示和详细注释,帮助新手快速...

    jQuery源码解读

    在源码解读中,我们可以看到jQuery的设计思路和实现机制。 首先,jQuery的核心设计是基于JavaScript的闭包特性,以避免命名冲突。整个jQuery库被包裹在一个立即执行的匿名函数中,这样可以确保其中定义的所有函数和...

    jquery 源码分析

    《jQuery源码分析详解》 jQuery,作为一款广泛使用的JavaScript库,极大地简化了网页的DOM操作、事件处理、动画制作以及Ajax交互。深入理解jQuery的源码,不仅可以提升我们的编程技巧,更能帮助我们优化代码,提高...

Global site tag (gtag.js) - Google Analytics