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

jQuery源码历代记3

 
阅读更多

 

前面讲解过了jQuery构造器的each方法,并顺道将jQuery的工具方法jQuery.each()也讲解了一下。

下面继续讲解jQuery 1.01构造器中的index方法,先上代码: 

 

 

index : function( obj ) {
    var pos = -1;
    this.each( function( i ) {
              if ( this == obj ) pos = i;
    } );
    return pos;
}
 

 

 

 这段代码非常简单,就是通过each方法遍历当前jQuery实例对象属性并与obj进行判断若相同则返回该属性的索引值。

 请注意:在jQuery1.4之后对该方法进行了补充,具体补充内容在讲解到1.4时再介绍。 

这里还要说明的是在jQuery1.2版本之后,将这种遍历数组比较对象的方法重新抽象出一个工具方法:jQuery.inArray();

请看代码:

 

inArray: function( elem, array ) {
   for ( var i = 0, length = array.length; i < length; i++ )
   // Use === because on IE, window == document
   if ( array[ i ] === elem ){
         return i;
    }
     return -1;
}
 

 

 代码内容比较简单没啥可解释的了。

正因为在jQuery 1.2之后抽象出来这个工具方法所以,jQuery 1.2中构造器中的index方法改写成

 

index: function( elem ) {
   var ret = -1;
   // Locate the position of the desired element
    return jQuery.inArray(
          // If it receives a jQuery object, the first element is used
          elem && elem.jquery ? elem[0] : elem , this );
}
 

好了,index就介绍到这里,可以看出jQuery核心最重要的一个思想就是以类似于数组的方式存储jQuery的各个属性。

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

下一个要出场的是重量级方法:attr

对于attr方法有几个重载实现,我们将分别给与说明。根据以往讲解重要方法的经验先奉献上的是API中的描述:

attr的实现之一:.attr( attributeName )

 

Description: Get the value of an attribute for the first element in the set of matched elements. 
The .attr() method gets the attribute value for only the first element in the matched set. To get the value for each element individually, use a looping construct such as jQuery's .each() or .map() method. 
 

这里明确说明attr仅对匹配对象集合的第一个元素起作用,为了帮助理解我们看下下面的代码:

<p id="p1">element1</p><p id="p2">element2</p>

如果执行$("p").attr("id");则将返回p1。

 

attr的实现之二:.attr( attributeName, value ) 

attributeName  The name of the attribute to set.

value  A value to set for the attribute.

attr的实现之三:.attr( map )

map  A map of attribute-value pairs to set.

Description: Set one or more attributes for the set of matched elements. 

举例:

html内容:

<img id="greatphoto" src="brush-seller.jpg" alt="brush seller" />

JS内容:

$('#greatphoto').attr('alt', 'Beijing Brush Seller');

将修改img的属性alt为:Beijing Brush Seller

$('#greatphoto').attr('title', 'Photo by Kelly Clark');

将为img添加一个新的属性title

如果想同时做上述2个动作则:

$('#greatphoto').attr({

    alt: 'Beijing Brush Seller',

    title: 'photo by Kelly Clark'

});

请注意如果想修改class属性则只能使用最后一种方式。 

//****************************讲解代码的分割线**************************

我们看下attr方法的代码:

 

attr : function( key , value , type ) {
   // Check to see if we're setting style values
  return key.constructor != String || value != undefined ? this.each( function() {
         // See if we're setting a hash of styles
          if ( value == undefined ){
          // Set all the styles
                for ( var prop in key ){
                       jQuery.attr( type ? this.style : this , prop , key[prop] );
                 }
                // See if we're setting a single key/value style
           } else{
                  jQuery.attr( type ? this.style : this , key , value );
           }
    } ) :
    // Look for the case where we're accessing a style value
  jQuery[ type || "attr"]( this[0] , key );
}
 

 

 让我们分析一下这段代码:

首先在jQuery的构造器中attr方法允许传入3个参数,而根据API中的描述attr最多可以传入2个参数而第3个参数是做什么用的呢?

请看最后一行:jQuery[ type || "attr"]( this[0] , key ); 这里如果向该方法中传入第3个参数则会调用jQuery[type]方法,所以说第3个参数为jQuery内部使用的且只能传递字符串并且字符串只能是jQuery工具类的方法名。

这个地方为什么是this[0]呢? “Get the value of an attribute for the first element in the set of matched elements.”还记得吗~~

讲解完参数我们看下实现的代码。整个代码就只有一句,一个return就搞定了,但在return中进行了一些判断:

key.constructor != String || value != undefined 说明如果传递的第一个参数不是字符串(对应attr( map )这种情况)或者传递的第2个参数不为空则进入this.each()部分,否则直接执行jQuery[ type || "attr"]( this[0] , key );

我们用实例分析:

$('#greatphoto').attr({

    alt: 'Beijing Brush Seller',

    title: 'photo by Kelly Clark'

});

在执行该方法时首先$('#greatphoto')得到了一个jQuery的结果集,当然上面的例子因为是使用了id选择器,所以结果集中只有一个元素。

拿到结果集后在结果集上执行attr方法,所以需要对结果集进行遍历,使用的就是前面刚介绍过的each方法。

  if ( value == undefined ){

           for ( var prop in key ){

                 jQuery.attr( type ? this.style : this , prop , key[prop] );

            }

   }

这句是在遍历每一个对象时首先判断是否在传递了key的同时传递了value。

如果是:$("p").attr({title:'ptitle',class:'pClass'});

这种形式的调用时遍历{title:'ptitle',class:'pClass'}为每一个P设置title和class属性。

如果是:如果是:$("p").attr("title","abc");

则调用    jQuery.attr( type ? this.style : this , key , value );

如果key为字符串,value还为空则属于$("p").attr("id");这种调用方式。

则执行: jQuery[ type || "attr"]( this[0] , key );

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

现在的重点就是看下jQuery的工具方法:attr的定义:

 

attr : function( elem , name , value ) {
  var fix = {
     "for" : "htmlFor",
     "class" : "className",
      "float" : "cssFloat",
       innerHTML : "innerHTML",
        className : "className",
       value : "value",
        disabled : "disabled"
  };

   if ( fix[ name ] ) {
       if ( value != undefined ) elem[ fix [ name ] ] = value;
          return elem[ fix [ name ] ];
    } else if ( elem.getAttribute ) {
          if ( value != undefined ) elem.setAttribute( name , value );
           return elem.getAttribute( name , 2 );
    } else {
          name = name.replace( /-([a-z])/ig , function( z , b ) {
                           return b.toUpperCase();
                        });
        if ( value != undefined ) elem[ name ] = value;
              return elem[ name ];
    }
}
 

 一开始有一个fix的对象,里面将一些值转换成了字符串。将class转换为js调用class时需要使用的className。为什么要这样呢?

jQuery的API中有一个很重要的提示:

Note: Attribute values are strings with the exception of a few attributes such as value and tabindex. 

好吧,英文看不懂的童鞋请google翻译吧。

jQuery的attr工具方法不仅仅用来对DOM属性进行复制也肩负着修改DOM css属性的作用。

看这个代码其实很简单,首先判断待修改的属性名是否属于“需修正的属性”如果是则使用修正后的属性名进而对该属性进行赋值。

如果不属于待修正的属性则直接调用getAttrbute进行修改(这里的第2个参数那个数值2没用任何作用,怀疑是bug)

最下面的else就是用来匹配css的了。(哭了,css一直是我的死穴,看不懂直接飘过吧。。。。)

综上我们得出结论就是jQuery的attr工具方法的作用就是在elem上找到name属性并值赋值为value。

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

attr方法在后面的讲解中还会重复出现哦,并且由于这是jQuery体系中非常非常重要的一个基础方法,所以在jQuery升级的过程中几乎每一个版本都会对其进行重构哦。

好了,下面我们讲解下一个方法:css

 

css : function( key , value ) {
        return this.attr( key , value , "curCSS" );
}
 

从代码就可以看出构造器的css方法其实就是attr方法的style属性简化版。

其中用到了attr的第3属性哦,还记得此属性的作用吧:

直接调用jQuery.curCSS();

 

curCSS : function( elem , prop , force ) {
   var ret;
   if ( !force && elem.style[prop] ) {
         ret = elem.style[prop];
    } else if ( elem.currentStyle ) {
        var newProp = prop.replace( /\-(\w)/g ,  function( m , c ) {
                             return c.toUpperCase()
                         });
          ret = elem.currentStyle[prop] || elem.currentStyle[newProp];
   } else if ( document.defaultView && document.defaultView.getComputedStyle ) {
          prop = prop.replace( /([A-Z])/g , "-$1" ).toLowerCase();
          var cur = document.defaultView.getComputedStyle( elem , null );
          if ( cur )
              ret = cur.getPropertyValue( prop );
         else if ( prop == 'display' )
               ret = 'none';
         else
                jQuery.swap( elem ,{display : 'block'} ,function() {
                       ret = document.defaultView.getComputedStyle( this , null )
                                   .getPropertyValue( prop );
           });
    }
  return ret;
}
 

这个方法呢~~就是把css属性换成js写法background-color准换成backgroundColor。

css我是不熟悉的,等待高人补充吧。诶。。。后台转前台的人伤不起啊。。。。

//*********************************再次出现一个重要的分割线***************************

下面是我非常常用的一个方法text

照例先看下API中对于此方法的描述

Description: Get the combined text contents of each element in the set of matched elements, including their descendants. 

 text()方法可以处理HTML和XML文档,例如:

HTML文档:

<div class="demo-container">

<div class="demo-box">Demonstration Box</div>

<ul>

<li>list item 1</li>

<li>list <strong>item</strong> 2</li>

</ul>

</div>

JS文档执行:$('div.demo-container').text()

得到的结果为:

Demonstration Box list item 1 list item 2

 //*********************************一个重要的分割线***************************

text另一个重载方法为:

.text( textString )

 

Description: Set the content of each element in the set of matched elements to the specified text.
 

 调用:$('div.demo-container').text('<p>This is a test.</p>');时页面效果为:<div class="demo-container">

&lt;p&gt;This is a test.&lt;/p&gt;

</div>

jQuery会自动将<等特殊字符进行转码。

 //*******************************讲解代码的分割线**************************

让我们看下代码:

 

text : function( e ) {
  e = e || this;
   var t = "";
   for ( var j = 0; j < e.length; j++ ) {
        var r = e[ j ].childNodes;
       for ( var i = 0; i < r.length; i++ ){
              if ( r[ i ].nodeType != 8 ){
                     t += r[ i ].nodeType != 1 ? r[ i ].nodeValue : jQuery.fn.text( [ r [ i ] ] );
                  }
        }
  }
  return t;
}
 

 

一个功能比较简单的代码,代码中的t就是返回的字符串信息。由于text会将DOM元素的子元素文本也包含进返回的信息中。所以代码中有一个递归的调用。

其中DOM的nodeType包含如下信息:

元素类型节点类型

元素element 1
属性attr 2
文本text 3
注释comments 8
文档document 9

  t += r[ i ].nodeType != 1 ? r[ i ].nodeValue : jQuery.fn.text( [ r [ i ] ] );的含义是:

首先判断当前节点是否为元素节点,如果是则进行递归。如果不是则取出nodeValue添加进t字符串中。

请大家注意一点,从上面的代码我们可以看出我们根本无法实现$('div.demo-container').text('<p>This is a test.</p>');这种方式的调用。我们看下jQuery 1.1版本时text代码的变化:

 

text : function( e ) {
    if ( typeof e == "string" ){
        return this.empty().append( document.createTextNode( e ) );
    }
    var t = "";
    jQuery.each( e || this ,function() {
        jQuery.each( this.childNodes ,  function() {
            if ( this.nodeType != 8 )
                t += this.nodeType != 1 ? this.nodeValue : jQuery.fn.text( [this] );
        });
    });
    return t;
}
 

 

从代码中我们看出变化的内容下半部分主要是将迭代变更为使用jQuery.each来实现增加复用性。

上半部分中添加了如果参数为字符串则先清空当前节点而后在当前节点下创建文本节点的代码。当然这段代码还是有一些局限性的。

 还有一个bug是如果我们这样设置:

var str = new String("<p>");

console.log(typeof str == "string");

我们可以看到结果是false,所以新的方法一般会用如下代码进行验证:

var str = new String("<p>");

var str1 = "a";

console.log(str.constructor == String);

console.log(str1.constructor == String);

执行后我们可以看出2个验证均返回true。

到了jQuery 1.2以后这句有些问题的代码变更为:

 

if ( typeof text != "object" && text != null )
       return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
 

含义将来再说。嘿嘿。。

 

//**************************传送门的分割线******************

我们回到jQuery 1.0是的代码:jQuery.fn.text( [ r [ i ] ] );

这一句中的jQuery.fn.text为什么是调用自身呢?

我们在最开始的代码中可以看到:

jQuery.fn = jQuery.prototype = {

jquery : "$Rev: 509 $",

... ..... ....

所以调用jQuery.fn.text就是调用jQuery.prototype.text所以就是调用本身即迭代调用。

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics