jQuery.
本題に入る前に少々豆知識です。jQueryは軽量なライブラリだと言われていますが、
それでは、
swap()
0770: // A method for quickly swapping in/out CSS properties to get correct calculations
0771: swap: function( elem, options, callback ) {
0772: var old = {};
0773: // Remember the old values, and insert the new ones
0774: for ( var name in options ) {
0775: old[ name ] = elem.style[ name ];
0776: elem.style[ name ] = options[ name ];
0777: }
0778:
0779: callback.call( elem );
0780:
0781: // Revert the old values
0782: for ( var name in options )
0783: elem.style[ name ] = old[ name ];
0784: },
0785:
jQuery.
css()
0786: css: function( elem, name, force ) {
0787: if ( name == "width" || name == "height" ) {
0788: var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
0789:
0790: function getWH() {
0791: val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
0792: var padding = 0, border = 0;
0793: jQuery.each( which, function() {
0794: padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
0795: border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
0796: });
0797: val -= Math.round(padding + border);
0798: }
0799:
0800: if ( jQuery(elem).is(":visible") )
0801: getWH();
0802: else
0803: jQuery.swap( elem, props, getWH );
0804:
0805: return Math.max(0, val);
0806: }
0807:
0808: return jQuery.curCSS( elem, name, force );
0809: },
0810:
jQuery.
また、
curCSS()
jQuery.
0811: curCSS: function( elem, name, force ) {
0812: var ret;
0813:
0814: // A helper method for determining if an element's values are broken
0815: function color( elem ) {
0816: if ( !jQuery.browser.safari )
0817: return false;
0818:
0819: var ret = document.defaultView.getComputedStyle( elem, null );
0820: return !ret || ret.getPropertyValue("color") == "";
0821: }
0822:
0823: // We need to handle opacity special in IE
0824: if ( name == "opacity" && jQuery.browser.msie ) {
0825: ret = jQuery.attr( elem.style, "opacity" );
0826:
0827: return ret == "" ?
0828: "1" :
0829: ret;
0830: }
0831: // Opera sometimes will give the wrong display answer, this fixes it, see #2037
0832: if ( jQuery.browser.opera && name == "display" ) {
0833: var save = elem.style.display;
0834: elem.style.display = "block";
0835: elem.style.display = save;
0836: }
0837:
0838: // Make sure we're using the right name for getting the float value
0839: if ( name.match( /float/i ) )
0840: name = styleFloat;
0841:
815行目は、
824行目は、
832行目は、
838行目は、
0842: if ( !force && elem.style && elem.style[ name ] )
0843: ret = elem.style[ name ];
0844:
forceがfalseか指定されていない場合は、
0845: else if ( document.defaultView && document.defaultView.getComputedStyle ) {
0846:
0847: // Only "float" is needed here
0848: if ( name.match( /float/i ) )
0849: name = "float";
0850:
0851: name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
0852:
0853: var getComputedStyle = document.defaultView.getComputedStyle( elem, null );
0854:
0855: if ( getComputedStyle && !color( elem ) )
0856: ret = getComputedStyle.getPropertyValue( name );
0857:
0858: // If the element isn't reporting its values properly in Safari
0859: // then some display: none elements are involved
0860: else {
0861: var swap = [], stack = [];
0862:
0863: // Locate all of the parent display: none elements
0864: for ( var a = elem; a && color(a); a = a.parentNode )
0865: stack.unshift(a);
0866:
0867: // Go through and make them visible, but in reverse
0868: // (It would be better if we knew the exact display type that they had)
0869: for ( var i = 0; i 0870: if ( color( stack[ i ] ) ) {
0871: swap[ i ] = stack[ i ].style.display;
0872: stack[ i ].style.display = "block";
0873: }
0874:
0875: // Since we flip the display style, we have to handle that
0876: // one special, otherwise get the value
0877: ret = name == "display" && swap[ stack.length - 1 ] != null ?
0878: "none" :
0879: ( getComputedStyle && getComputedStyle.getPropertyValue( name ) ) || "";
0880:
0881: // Finally, revert the display styles back
0882: for ( var i = 0; i 0883: if ( swap[ i ] != null )
0884: stack[ i ].style.display = swap[ i ];
0885: }
0886:
0887: // We should always get a number back from opacity
0888: if ( name == "opacity" && ret == "" )
0889: ret = "1";
0890:
IEにはdocument.
860行目以降はSafariが適切な値を返さない場合の処理で、
0891: } else if ( elem.currentStyle ) {
0892: var camelCase = name.replace(/\-(\w)/g, function(all, letter){
0893: return letter.toUpperCase();
0894: });
0895:
0896: ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
0897:
891行目からはIEの場合の処理です。
892行目は先ほどの属性名の変更とは逆で、
0898: // From the awesome hack by Dean Edwards
0899: // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
0900:
0901: // If we're not dealing with a regular pixel number
0902: // but a number that has a weird ending, we need to convert it to pixels
0903: if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
0904: // Remember the original values
0905: var style = elem.style.left, runtimeStyle = elem.runtimeStyle.left;
0906:
0907: // Put in the new values to get a computed value out
0908: elem.runtimeStyle.left = elem.currentStyle.left;
0909: elem.style.left = ret || 0;
0910: ret = elem.style.pixelLeft + "px";
0911:
0912: // Revert the changed values
0913: elem.style.left = style;
0914: elem.runtimeStyle.left = runtimeStyle;
0915: }
0916: }
0917:
0918: return ret;
0919: },
0920:
903行目以降は、
clean()
0921: clean: function( elems, context ) {
0922: var ret = [];
0923: context = context || document;
0924: // !context.createElement fails in IE with an error but returns typeof 'object'
0925: if (typeof context.createElement == 'undefined')
0926: context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
0927:
0928: jQuery.each(elems, function(i, elem){
0929: if ( !elem )
0930: return;
0931:
0932: if ( elem.constructor == Number )
0933: elem = elem.toString();
0934:
0935: // Convert html string into DOM nodes
0936: if ( typeof elem == "string" ) {
0937: // Fix "XHTML"-style tags in all browsers
0938: elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
0939: return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
0940: all :
0941: front + "></" + tag + ">";
0942: });
0943:
0944: // Trim whitespace, otherwise indexOf won't work as expected
0945: var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div");
0946:
0947: var wrap =
0948: // option or optgroup
0949: !tags.indexOf("<opt") &&
0950: [ 1, "<select multiple='multiple'>", "</select>" ] ||
0951:
0952: !tags.indexOf("<leg") &&
0953: [ 1, "<fieldset>", "</fieldset>" ] ||
0954:
0955: tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
0956: [ 1, "<table>", "</table>" ] ||
0957:
0958: !tags.indexOf("<tr") &&
0959: [ 2, "<table><tbody>", "</tbody></table>" ] ||
0960:
0961: // <thead> matched above
0962: (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
0963: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
0964:
0965: !tags.indexOf("<col") &&
0966: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
0967:
0968: // IE can't serialize <link> and <script> tags normally
0969: jQuery.browser.msie &&
0970: [ 1, "div<div>", "</div>" ] ||
0971:
0972: [ 0, "", "" ];
0973:
0974: // Go to html and back, then peel off extra wrappers
0975: div.innerHTML = wrap[1] + elem + wrap[2];
0976:
0977: // Move to the right depth
0978: while ( wrap[0]-- )
0979: div = div.lastChild;
0980:
0981: // Remove IE's autoinserted <tbody> from table fragments
0982: if ( jQuery.browser.msie ) {
0983:
0984: // String was a <table>, *may* have spurious
0985: var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
0986: div.firstChild && div.firstChild.childNodes :
0987:
0988: // String was a bare <thead> or <tfoot>
0989: wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
0990: div.childNodes :
0991: [];
0992:
0993: for ( var j = tbody.length - 1; j >= 0 ; --j )
0994: if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
0995: tbody[ j ].parentNode.removeChild( tbody[ j ] );
0996:
0997: // IE completely kills leading whitespace when innerHTML is used
0998: if ( /^\s/.test( elem ) )
0999: div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
1000:
1001: }
1002:
1003: elem = jQuery.makeArray( div.childNodes );
1004: }
1005:
1006: if ( elem.length === 0 && (!jQuery.nodeName( elem, "form" ) && !jQuery.nodeName( elem, "select" )) )
1007: return;
1008:
1009: if ( elem[0] == undefined || jQuery.nodeName( elem, "form" ) || elem.options )
1010: ret.push( elem );
1011:
1012: else
1013: ret = jQuery.merge( ret, elem );
1014:
1015: });
1016:
1017: return ret;
1018: },
1019:
jQuery.cleanメソッドは、内部でHTMLを利用しやすいように加工するためのメソッドです。
925行目では、contextにdocument以外が渡された場合、Internet ExplorerではcreateElementが失敗するため、contextの値を再設定しています。そして、928行目以降で渡されたすべての要素に対して、以下の処理を適用していきます。
- 937~942行目:空要素タグを分割
- 944~979行目:特定のタグの場合に適切なタグで囲む
- 981~1004行目:Internet Exploerで自動的に挿入されるtbodyタグを取り除く、
innerHTMLで先頭の空白文字列を保存
- 1006~1013行目:formまたはselectがリターン用配列への格納
attr()
1020: attr: function( elem, name, value ) {
1021: // don't set attributes on text and comment nodes
1022: if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
1023: return undefined;
1024:
1025: var fix = jQuery.isXMLDoc( elem ) ?
1026: {} :
1027: jQuery.props;
1028:
1029: // Safari mis-reports the default selected property of a hidden option
1030: // Accessing the parent's selectedIndex property fixes it
1031: if ( name == "selected" && jQuery.browser.safari )
1032: elem.parentNode.selectedIndex;
1033:
1034: // Certain attributes only work when accessed via the old DOM 0 way
1035: if ( fix[ name ] ) {
1036: if ( value != undefined )
1037: elem[ fix[ name ] ] = value;
1038:
1039: return elem[ fix[ name ] ];
1040:
1041: } else if ( jQuery.browser.msie && name == "style" )
1042: return jQuery.attr( elem.style, "cssText", value );
1043:
1044: else if ( value == undefined && jQuery.browser.msie && jQuery.nodeName( elem, "form" ) && (name == "action" || name == "method") )
1045: return elem.getAttributeNode( name ).nodeValue;
1046:
1047: // IE elem.getAttribute passes even for style
1048: else if ( elem.tagName ) {
1049:
1050: if ( value != undefined ) {
1051: // We can't allow the type property to be changed (since it causes problems in IE)
1052: if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
1053: throw "type property can't be changed";
1054:
1055: // convert the value to a string (all browsers do this but IE) see #1070
1056: elem.setAttribute( name, "" + value );
1057: }
1058:
1059: if ( jQuery.browser.msie && /href|src/.test( name ) && !jQuery.isXMLDoc( elem ) )
1060: return elem.getAttribute( name, 2 );
1061:
1062: return elem.getAttribute( name );
1063:
1064: // elem is actually elem.style ... set the style
1065: } else {
1066: // IE actually uses filters for opacity
1067: if ( name == "opacity" && jQuery.browser.msie ) {
1068: if ( value != undefined ) {
1069: // IE has trouble with opacity if it does not have layout
1070: // Force it by setting the zoom level
1071: elem.zoom = 1;
1072:
1073: // Set the alpha filter to set the opacity
1074: elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
1075: (parseFloat( value ).toString() == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
1076: }
1077:
1078: return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
1079: (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100).toString() :
1080: "";
1081: }
1082:
1083: name = name.replace(/-([a-z])/ig, function(all, letter){
1084: return letter.toUpperCase();
1085: });
1086:
1087: if ( value != undefined )
1088: elem[ name ] = value;
1089:
1090: return elem[ name ];
1091: }
1092: },
1093:
jQuery.attrメソッドは、属性値を扱うためのメソッドです。
1021~1023行目は、nodeTypeがテキストまたはコメントの場合は属性値は存在しないためundefinedを返します。
1029~1032行目は、Safariでselected値を取得しようとした時に発生する不具合を回避するため、親要素のselectedIndex値にアクセスしています。
1034~1040行目は、jQuery.propsで定義されている属性(1214行目)についてはDOM0に従ってアクセスします。
1041~1042行目は、IEからstyle属性にアクセスしようとしている場合は、jQuery.attr( elem.style, "cssText", value )として自分自身を呼び出します。
1044~1045行目は、IEからformのactionまたはmethodを取得しようとしている場合の処理です。
1047~1062行目は、上記以外の場合で、値を設定したり取得する場合の処理。input要素のtypeを変更しようとした場合には例外を発生させます。
1065~1081行目は、IEでopacityを扱う場合の処理です。
1083~1090行目は、属性名をキャメライズして、value引数が指定されていれば値を設定し、属性名値を返します。
以上でjQueryのコード全体の三分の一が終了しました。だいぶ全体像が見えてきたのではないかと思います。今回はクロスブラウザのためのバッドノウハウ的な部分が多く、泥臭い印象があったかもしれませんが、また次回をお楽しみに。