这一节重点讲jQuery对样式的处理,虽然IE同时拥有style,currentStyle与runtimeStyle,但没有一个能获取used value,这是原罪。直接导致的结果是处理样式,就是处理IE的非精确值问题,有时能否获得值也是个大问题。jQuery与其他类库一样,在这方面下了很大工夫,最终在这方面打败其他类库。

001.  //这里的代码写得很垃圾啊,不过这样写肯定有它的道理,既然版本号已经发展1.32,那当然是那么兼容以前的代码设计的
002.  className: {
003.    //顺便一提className与arguments一样是个类数组
004.    add: function( elem, classNames ) {
005.      jQuery.each((classNames || "").split(/\s+/), function(i, className){
006.        if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
007.          elem.className += (elem.className ? " " : "") + className;
008.      });
009.    },
010.    // internal only, use removeClass("class")
011.    remove: function( elem, classNames ) {
012.      //觉得什么都用自定义函数解决效率太低了,更何况jQuery.grep的逻辑如此复杂
013.      if (elem.nodeType == 1)
014.        elem.className = classNames !== undefined ?
015.        jQuery.grep(elem.className.split(/\s+/), function(className){
016.        return !jQuery.className.has( classNames, className );
017.      }).join(" ") :
018.        "";
019.    },
020.    // internal only, use hasClass("class")
021.    has: function( elem, className ) {
022.      return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
023.    }
024.  },
025.  //这是一个非常重要的内部函数,用于精确获取样式值
026.  // A method for quickly swapping in/out CSS properties to get correct calculations
027.  swap: function( elem, options, callback ) {
028.    var old = {};//备份用
029.    // Remember the old values, and insert the new ones
030.    for ( var name in options ) {
031.      old[ name ] = elem.style[ name ];
032.      elem.style[ name ] = options[ name ];
033.    }
034.    //交换之后调用测试函数
035.    callback.call( elem );
036.    //测试完后还原
037.    // Revert the old values
038.    for ( var name in options )
039.      elem.style[ name ] = old[ name ];
040.  },
041.  //jQuery对象也有一个与它同名的方法,但这不是简单的代理
042.  //不过实际路线图为原型的css→原型的attr→静态的attr→静态的css
043.  //最后是curCSS,这才是真身
044.  css: function( elem, name, force, extra ) {
045.    //处理宽与高,因为IE不能正确返回以px为单位的精确值
046.    if ( name == "width" || name == "height" ) {
047.      //props用于swap,一个聪明的手段,值得学习
048.      var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
049.      function getWH() {
050.        //Ext与Prototypet等类库也是这样实现
051.        //在标准模式中,offsetWidth是包含padding,borderWidth与width
052.        //在怪癖模式下,offsetWidth等于width,而width是包含padding与borderWidth
053.        //offsetHeight同理
054.        val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
055.        if ( extra === "border" )
056.          return;
057.        jQuery.each( which, function() {
058.          if ( !extra )
059.          //求出paddingLeft与paddingRight之和,或paddingTop与paddingBottom之和,
060.          //然后作为减数,去减offsetWidth或offsetHeight
061.            val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
062.          if ( extra === "margin" )
063.          val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
064.        else
065.          val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
066.      });
067.    }
068.    if ( elem.offsetWidth !== 0 )
069.      getWH();
070.    else
071.    //如果display:none就求不出offsetWidht与offsetHeight,swap一下
072.      jQuery.swap( elem, props, getWH );
073.    return Math.max(0, Math.round(val));
074.  }
075.  //再调用jQuery.curCSS进行深加工
076.  return jQuery.curCSS( elem, name, force );
077.},
078.curCSS: function( elem, name, force ) {
079.  var ret, style = elem.style;
080.  // We need to handle opacity special in IE
081.  if ( name == "opacity" && !jQuery.support.opacity ) {
082.    ret = jQuery.attr( style, "opacity" );
083.    return ret == "" ?
084.      "1" :
085.      ret;
086.  }
087.  // Make sure we're using the right name for getting the float value
088.  if ( name.match( /float/i ) )
089.    name = styleFloat;
090.  if ( !force && style && style[ name ] )
091.    ret = style[ name ];//缓存结果
092.  else if ( defaultView.getComputedStyle ) {
093.    //标准浏览器
094.    // Only "float" is needed here
095.    if ( name.match( /float/i ) )
096.      name = "float";//把cssFloat转换为float
097.    //把驼峰风格转换为连字符风格
098.    name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
099.    var computedStyle = defaultView.getComputedStyle( elem, null );
100.    if ( computedStyle )
101.      ret = computedStyle.getPropertyValue( name );
102.    // We should always get a number back from opacity
103.    if ( name == "opacity" && ret == "" )
104.      ret = "1";//把opacity设置成1
105.  } else if ( elem.currentStyle ) {
106.    //IE浏览器部分
107.    var camelCase = name.replace(/\-(\w)/g, function(all, letter){
108.      return letter.toUpperCase();
109.    });
110.    //把连字符风格转换为驼峰风格
111.    ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
112.    // From the awesome hack by Dean Edwards
114.    // If we're not dealing with a regular pixel number
115.    // but a number that has a weird ending, we need to convert it to pixels
116.    //将不是以px为单位的计算值全部转换为以px为单位,用到 Dean Edwards(Base2类库的作者)的hack
117.    //网上有文章讲解这hach,这里不重复
118.    if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
119.      // Remember the original values
120.      var left = style.left, rsLeft = elem.runtimeStyle.left;
121.      // Put in the new values to get a computed value out
122.      elem.runtimeStyle.left = elem.currentStyle.left;
123.      style.left = ret || 0;
124.      ret = style.pixelLeft + "px";
125.      // Revert the changed values
126.      style.left = left;
127.      elem.runtimeStyle.left = rsLeft;
128.    }
129.  }
130.  return ret;
131.},
132.attr: function( elem, name, value ) {
133.  // 文本,注释节点不处理
134.  if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
135.    return undefined;
136.  //不处理xml文档的
137.  var notxml = !jQuery.isXMLDoc( elem ),
138.  //是读方法还是写方法
139.  set = value !== undefined;
140.  // Try to normalize/fix the name
141.  //兼容处理,
142.  //jQuery.props = {
143.  //"for": "htmlFor",
144.  //"class": "className",
145.  //"float": styleFloat,
146.  //cssFloat: styleFloat,
147.  //styleFloat: styleFloat,
148.  //readonly: "readOnly",
149.  //maxlength: "maxLength",
150.  //cellspacing: "cellSpacing",
151.  //rowspan: "rowSpan",
152.  //tabindex: "tabIndex"
153.  //};
154.  name = notxml && jQuery.props[ name ] || name;
155.  // Only do all the following if this is a node (faster for style)
156.  // IE elem.getAttribute passes even for style
157.  if ( elem.tagName ) {
158.    // These attributes require special treatment
159.    var special = /href|src|style/.test( name );
160.    // Safari mis-reports the default selected property of a hidden option
161.    // Accessing the parent's selectedIndex property fixes it
162.    //修正无法取得selected正确值的bug
163.    if ( name == "selected" && elem.parentNode )
164.      elem.parentNode.selectedIndex;
165.    // If applicable, access the attribute via the DOM 0 way
166.    if ( name in elem && notxml && !special ) {
167.      if ( set ){
168.        //不允许改写type的值
169.        // We can't allow the type property to be changed (since it causes problems in IE)
170.        if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
171.          throw "type property can't be changed";
172.        elem[ name ] = value;
173.      }
174.      // browsers index elements by id/name on forms, give priority to attributes.
175.      if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
176.      //getAttributeNode() 方法的作用是:通过指定的名称获取当前元素中的属性节点。
177.        return elem.getAttributeNode( name ).nodeValue;
178.      // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
180.      //IE只能tabIndex
181.      //标准浏览器用tabindex
182.      if ( name == "tabIndex" ) {
183.        var attributeNode = elem.getAttributeNode( "tabIndex" );
184.        return attributeNode && attributeNode.specified
185.          ? attributeNode.value
186.        : elem.nodeName.match(/(button|input|object|select|textarea)/i)
187.          ? 0
188.        : elem.nodeName.match(/^(a|area)$/i) && elem.href
189.          ? 0
190.        : undefined;
191.      }
192.      return elem[ name ];
193.    }
194.    if ( !jQuery.support.style && notxml &&  name == "style" )
195.      return jQuery.attr( elem.style, "cssText", value );
196.    if ( set )
197.    // convert the value to a string (all browsers do this but IE) see #1070
198.      elem.setAttribute( name, "" + value );
199.    //IE的getAttribute支持第二个参数,可以为 0,1,2
200.    //0 是默认;1 区分属性的大小写;2取出源代码中的原字符串值。
201.    //IE 在取 href 的时候默认拿出来的是绝对路径,加参数2得到我们所需要的相对路径。
202.    var attr = !jQuery.support.hrefNormalized && notxml && special
203.    // Some attributes require a special call on IE
204.      ? elem.getAttribute( name, 2 )
205.    : elem.getAttribute( name );
206.    // Non-existent attributes return null, we normalize to undefined
207.    return attr === null ? undefined : attr;
208.  }
209.  // elem is actually elem.style ... set the style
210.  // IE uses filters for opacity
211.  if ( !jQuery.support.opacity && name == "opacity" ) {
212.    if ( set ) {
213.      // IE has trouble with opacity if it does not have layout
214.      // Force it by setting the zoom level
215.      //IE7中滤镜(filter)必须获得hasLayout才能生效,我们用zoom这个IE私有属性让其获得hasLayout
216.      elem.zoom = 1;
217.      // Set the alpha filter to set the opacity
218.      elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
219.        (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
220.    }
221.    return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
222.      (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
223.      "";
224.  }
225.  //获得其他属性,直接用DOM 0方法读写
226.  name = name.replace(/-([a-z])/ig, function(all, letter){
227.    return letter.toUpperCase();
228.  });
229.  if ( set )
230.    elem[ name ] = value;
231.  return elem[ name ];
232.},

其实在curCss与attr方法中还夹着一个clean方法,总觉得clean职责太多,里面分支繁缛,看得我头晕眼花……太凌乱,这方法应该分割成几个方法条理更清晰,效率更高。

001.//把字符串转换为DOM元素的纯数组
002.//这里的elems为字符串数组,将用文档碎片做转换
003.clean: function( elems, context, fragment ) {
004.  context = context || document;
005.  // !context.createElement fails in IE with an error but returns typeof 'object'
006.  if ( typeof context.createElement === "undefined" )
007.    context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
008.  // If a single string is passed in and it's a single tag
009.  // just do a createElement and skip the rest
010.  if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
011.    var match = /^<(\w+)\s*\/?>$/.exec(elems[0]);
012.    if ( match )
013.      return [ context.createElement( match[1] ) ];
014.  }
015.  //div是用于把字符串转换为DOM的
016.  var ret = [], scripts = [], div = context.createElement("div");
017.  jQuery.each(elems, function(i, elem){
018.    if ( typeof elem === "number" )
019.      elem += '';//转换为字符串
020.    if ( !elem )
021.      return;
022.    // Convert html string into DOM nodes
023.    if ( typeof elem === "string" ) {
024.      // Fix "XHTML"-style tags in all browsers
025.      //生成闭合的标签对,亦即把在XHTML中不合法的写法强制转换过来
026.      elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
027.        //但对于abbr|br|col|img|input|link|meta|param|hr|area|embed等元素不修改
028.        return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
029.          all :
030.          front + "></" + tag + ">";
031.      });
032.      // Trim whitespace, otherwise indexOf won't work as expected
033.      //将“ <div> ”去掉两边的空白“<div>”,用于下面的indexOf
034.      var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();
035.      var wrap =
036.        // option or optgroup
037.      //option与optgroup的直接父元素一定是select
038.        !tags.indexOf("<opt") &&
039.        [ 1, "<select multiple='multiple'>", "</select>" ] ||
040.        //legend的直接父元素一定是fieldset
041.        !tags.indexOf("<leg") &&
042.        [ 1, "<fieldset>", "</fieldset>" ] ||
043.        //thead,tbody,tfoot,colgroup,caption的直接父元素一定是table
044.      tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
045.        [ 1, "<table>", "</table>" ] ||
046.        //tr的直接父元素一定是tbody,
047.        !tags.indexOf("<tr") &&
048.        [ 2, "<table><tbody>", "</tbody></table>" ] ||
049.        //<thead> matched above
050.      //td与th的直接父元素一定是tr
051.      (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
052.        [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
053.        //col一定是colgroup
054.        !tags.indexOf("<col") &&
055.        [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
056.        // IE can't serialize <link> and <script> tags normally
057.        !jQuery.support.htmlSerialize &&
058.        [ 1, "div<div>", "</div>" ] ||
059.        [ 0, "", "" ];
060.      // Go to html and back, then peel off extra wrappers
061.      div.innerHTML = wrap[1] + elem + wrap[2];
062.      // Move to the right depth
063.      while ( wrap[0]-- )
064.        div = div.lastChild;
065.      //IE会自动添加tbody,要特殊处理
066.      // Remove IE's autoinserted <tbody> from table fragments
067.      if ( !jQuery.support.tbody ) {
068.        // String was a <table>, *may* have spurious <tbody>
069.        var hasBody = /<tbody/i.test(elem),
070.        tbody = !tags.indexOf("<table") && !hasBody ?
071.          div.firstChild && div.firstChild.childNodes :
072.          // String was a bare <thead> or <tfoot>
073.        wrap[1] == "<table>" && !hasBody ?
074.          div.childNodes :
075.          [];
076.        for ( var j = tbody.length - 1; j >= 0 ; --j )
077.          if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
078.            tbody[ j ].parentNode.removeChild( tbody[ j ] );
079.      }
080.      // IE completely kills leading whitespace when innerHTML is used
081.      if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
082.        div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
083.      //div中的所有节点都转换为数组
084.      elem = jQuery.makeArray( div.childNodes );
085.    }
086.    if ( elem.nodeType )
087.    //过滤非元素节点的节点
088.      ret.push( elem );
089.    else
090.    //把符合要求的节点加入ret中
091.      ret = jQuery.merge( ret, elem );
092.  });
093.  if ( fragment ) {
094.    for ( var i = 0; ret[i]; i++ ) {
095.      //处理script元素
096.      if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
097.        scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
098.      } else {
099.        if ( ret[i].nodeType === 1 )
100.          ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
101.        fragment.appendChild( ret[i] );
102.      }
103.    }
104.    return scripts;
105.  }
106.  return ret;
107.},

转自:http://www.cnblogs.com/rubylouvre/

 posted on 2009-11-22 16:10  dongpo  阅读(382)  评论(0编辑  收藏  举报