jquery.jsを読み解く

第13回jQueryライブラリ(2834行目~3107行目)

前回から今回までの間に、jQueryの最新版 1.2.6がリリースされました。どうやら再びパッチの適用に不備があったようで、1.2.4および1.2.5は使用しないようにとのことですのでご注意ください。変更点としては前回説明した通りなのですが、リリースノートを見るとかなりの高速化が図られていることが分かります。

  • イベントバンドリングが103%高速化
  • CSSセレクタの13%高速化
  • offset() 21%高速化
  • css() 25%高速化

それでは、2834行目からのソースを見ていきましょう。

jQuery.param()

2834:   // Serialize an array of form elements or a set of
2835:   // key/values into a query string
2836:   param: function( a ) {
2837:     var s = [];
2838: 
2839:     // If an array was passed in, assume that it is an array
2840:     // of form elements
2841:     if ( a.constructor == Array || a.jquery )
2842:       // Serialize the form elements
2843:       jQuery.each( a, function(){
2844:         s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );
2845:       });
2846: 
2847:     // Otherwise, assume that it's an object of key/value pairs
2848:     else
2849:       // Serialize the key/values
2850:       for ( var j in a )
2851:         // If the value is an array then the key names need to be repeated
2852:         if ( a[j] && a[j].constructor == Array )
2853:           jQuery.each( a[j], function(){
2854:             s.push( encodeURIComponent(j) + "=" + encodeURIComponent( this ) );
2855:           });
2856:         else
2857:           s.push( encodeURIComponent(j) + "=" + encodeURIComponent( a[j] ) );
2858: 
2859:     // Return the resulting serialization
2860:     return s.join("&").replace(/%20/g, "+");
2861:   }
2862: 
2863: });

2834行目からは、jQuery.param()メソッドの定義で、フォーム要素の配列やキー/値形式のオブジェクトをクエリー文字列形式にシリアライズするメソッドになります。

2837行目は、データ格納用に空の配列を用意します。

2841~2845行目は、フォーム要素の配列などのArrayオブジェクトが渡されてきた場合の処理でjQuery.each()メソッドを使って、name=value形式にして配列sに格納していきます。またデータは、encodeURIComponent()によりエスケープされます。

2848~2857行目は、キーと値のペアが渡されてきた場合の処理です。もし、この値が配列だった場合には、jQuery.each()メソッドを使って同じキー名を繰り返し設定するようにします。配列でなければ、key=value形式でそのまま配列sに格納します。

最後に2860行目で、配列sを"&"で結合して、スペースを"+"に変換して完了です。

jQuery.fn.show()

2864: jQuery.fn.extend({
2865:   show: function(speed,callback){
2866:     return speed ?
2867:       this.animate({
2868:         height: "show", width: "show", opacity: "show"
2869:       }, speed, callback) :
2870:       
2871:       this.filter(":hidden").each(function(){
2872:         this.style.display = this.oldblock || "";
2873:         if ( jQuery.css(this,"display") == "none" ) {
2874:           var elem = jQuery(" + this.tagName + " />").appendTo("body");
2875:           this.style.display = elem.css("display");
2876:           // handle an edge condition where css is - div { display:none; } or similar
2877:           if (this.style.display == "none")
2878:             this.style.display = "block";
2879:           elem.remove();
2880:         }
2881:       }).end();
2882:   },
2883:   

jQuery.fn.show()メソッドは、選択された要素が非表示になっている場合に表示状態にするメソッドです。

第1引数speedが指定されていれば、2867~2868行目にあるようにanimate()メソッドをheight:"show",width:"show",opacity:"show"というパラメータとともに呼び出します。

speed引数が指定されていない場合は、2871行目のfilter(":hidden")によって非表示の要素のみを抽出し、2872行目でthis.oldblockに以前の値が保持されていれば、style.displayプロパティを元に戻します。ここで、this.oldblockとは元々のdisplayプロパティの値なのですが、もしこの値が"none"だった場合には、そのままでは表示されません。そこで、2874行目で空のタグをbody内に挿入し、そのタグのデフォルトのdisplay値を取得しています。しかし、divタグなどのようにそれでもdisplay値に"none"が返ってくるものに関しては、2878行目で強制的に"block"を設定します。

jQuery.fn.hide()

2884:   hide: function(speed,callback){
2885:     return speed ?
2886:       this.animate({
2887:         height: "hide", width: "hide", opacity: "hide"
2888:       }, speed, callback) :
2889:       
2890:       this.filter(":visible").each(function(){
2891:         this.oldblock = this.oldblock || jQuery.css(this,"display");
2892:         this.style.display = "none";
2893:       }).end();
2894:   },
2895: 

jQuery.fn.hide()メソッドは、選択された要素が表示されている場合に非表示状態にするメソッドです。

第1引数speedが指定されていれば、2886~2888行目にあるようにanimate()メソッドをheight: "hide", width: "hide", opacity: "hide"というパラメータとともに呼び出します。

speed引数が指定されていない場合は、2890行目のfilter(":visible")によって表示されている要素のみを抽出し、2892行目でstyle.displayをnoneに設定することで非表示にします。また、2891行目で非表示にする前にdisplayプロパティの値をthis.oldblockに保存しておきます。

jQuery.fn.toggle()

2896:   // Save the old toggle function
2897:   _toggle: jQuery.fn.toggle,
2898:   
2899:   toggle: function( fn, fn2 ){
2900:     return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
2901:       this._toggle( fn, fn2 ) :
2902:       fn ?
2903:         this.animate({
2904:           height: "toggle", width: "toggle", opacity: "toggle"
2905:         }, fn, fn2) :
2906:         this.each(function(){
2907:           jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();
2908:         });
2909:   },
2910:   

jQuery.fn.toggle()メソッドは、選択された要素が表示されている場合には非表示に、非表示の場合には表示するメソッドです。

まず、2897行目ですが、jQuery.fn.toggle()メソッドは2217行目で既に定義されています。このメソッドを_toggleに退避しておきます。そして、もし2つの引数が関数の場合には、この_toggle()メソッドを呼び出すようにします。それ以外の場合で第1引数fnが指定されていれば、animate()メソッドをheight: "toggle", width: "toggle", opacity: "toggle"というパラメータとともに呼び出します。

fn引数が指定されていない場合には、2907行目にあるようにis(":hidden")メソッドを使って現在の状態を調べて、非表示状態であればshow()、表示状態であればhide()メソッドを実行します。

jQuery.fn.slideDown()

2911:   slideDown: function(speed,callback){
2912:     return this.animate({height: "show"}, speed, callback);
2913:   },
2914:   

jQuery.fn.slideDown()メソッドは、選択された要素をスライドダウンしながら表示します。第1引数speedにはアニメーションの速度を"slow","normal","fast"いずれかもしくは、ミリ秒単位で指定します。第2引数callbackは、アニメーションが終了した後に実行されるコールバック関数です。

実際の処理としては、引数として{height: "show"}とspeed,callbackを渡して、animate()メソッドが実行されます。

jQuery.fn.slideUp()

2915:   slideUp: function(speed,callback){
2916:     return this.animate({height: "hide"}, speed, callback);
2917:   },
2918: 

jQuery.fn.slideUp()メソッドは、選択された要素をスライドアップしながら非表示にします。slideDownと同様に、第1引数speedにはアニメーションの速度を"slow","normal","fast"いずれかもしくは、ミリ秒単位で指定します。第2引数callbackは、アニメーションが終了した後に実行されるコールバック関数です。

実際の処理としては、引数として{height: "hide"}とspeed,callbackを渡して、animate()メソッドが実行されます。

jQuery.fn.slideToggle()

2919:   slideToggle: function(speed, callback){
2920:     return this.animate({height: "toggle"}, speed, callback);
2921:   },
2922:   

jQuery.fn.slideToggle()メソッドは、選択された要素をスライドしながら、表示されている場合は非表示に、非表示の場合は表示します。つまり、先ほどのslideDownとslideUpが交互に繰り返されるようなかたちになります。第1引数speedにはアニメーションの速度を"slow","normal","fast"いずれかもしくは、ミリ秒単位で指定します。第2引数callbackは、アニメーションが終了した後に実行されるコールバック関数です。

実際の処理としては、引数として{height: "toggle"}とspeed,callbackを渡して、animate()メソッドが実行されます。

jQuery.fn.fadeIn()

2923:   fadeIn: function(speed, callback){
2924:     return this.animate({opacity: "show"}, speed, callback);
2925:   },
2926:   

jQuery.fn.fadeIn()メソッドは、選択された要素をフェードインしながら表示します。第1引数speedにはアニメーションの速度を"slow","normal","fast"いずれかもしくは、ミリ秒単位で指定します。第2引数callbackは、アニメーションが終了した後に実行されるコールバック関数です。

実際の処理としては、引数として{opacity: "show"}とspeed,callbackを渡して、animate()メソッドが実行されます。

jQuery.fn.fadeOut()

2927:   fadeOut: function(speed, callback){
2928:     return this.animate({opacity: "hide"}, speed, callback);
2929:   },
2930:   

jQuery.fn.fadeOut()メソッドは、選択された要素をフェードアウトしながら非表示にします。第1引数speedにはアニメーションの速度を"slow","normal","fast"いずれかもしくは、ミリ秒単位で指定します。第2引数callbackは、アニメーションが終了した後に実行されるコールバック関数です。

実際の処理としては、引数として{opacity: "hide"}とspeed,callbackを渡して、animate()メソッドが実行されます。

jQuery.fn.fadeTo()

2931:   fadeTo: function(speed,to,callback){
2932:     return this.animate({opacity: to}, speed, callback);
2933:   },
2934:   

jQuery.fn.fadeTo()メソッドは、選択された要素を指定された不透明度までフェード表示を行います。第1引数speedにはアニメーションの速度を"slow","normal","fast"いずれかもしくは、ミリ秒単位で指定します。第2引数toは、不透明度で0~1の間の数値で指定します。第3引数callbackは、アニメーションが終了した後に実行されるコールバック関数です。

実際の処理としては、引数として{opacity: to}とspeed,callbackを渡して、animate()メソッドが実行されます。

jQuery.fn.animate()

2935:   animate: function( prop, speed, easing, callback ) {
2936:     var optall = jQuery.speed(speed, easing, callback);
2937: 
2938:     return this[ optall.queue === false ? "each" : "queue" ](function(){
2939:       if ( this.nodeType != 1)
2940:         return false;
2941: 
2942:       var opt = jQuery.extend({}, optall);
2943:       var hidden = jQuery(this).is(":hidden"), self = this;
2944:       
2945:       for ( var p in prop ) {
2946:         if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
2947:           return jQuery.isFunction(opt.complete) && opt.complete.apply(this);
2948: 
2949:         if ( p == "height" || p == "width" ) {
2950:           // Store display property
2951:           opt.display = jQuery.css(this, "display");
2952: 
2953:           // Make sure that nothing sneaks out
2954:           opt.overflow = this.style.overflow;
2955:         }
2956:       }
2957: 
2958:       if ( opt.overflow != null )
2959:         this.style.overflow = "hidden";
2960: 
2961:       opt.curAnim = jQuery.extend({}, prop);
2962:       
2963:       jQuery.each( prop, function(name, val){
2964:         var e = new jQuery.fx( self, opt, name );
2965: 
2966:         if ( /toggle|show|hide/.test(val) )
2967:           e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
2968:         else {
2969:           var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
2970:             start = e.cur(true) || 0;
2971: 
2972:           if ( parts ) {
2973:             var end = parseFloat(parts[2]),
2974:               unit = parts[3] || "px";
2975: 
2976:             // We need to compute starting value
2977:             if ( unit != "px" ) {
2978:               self.style[ name ] = (end || 1) + unit;
2979:               start = ((end || 1) / e.cur(true)) * start;
2980:               self.style[ name ] = start + unit;
2981:             }
2982: 
2983:             // If a +=/-= token was provided, we're doing a relative animation
2984:             if ( parts[1] )
2985:               end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
2986: 
2987:             e.custom( start, end, unit );
2988:           } else
2989:             e.custom( start, val, "" );
2990:         }
2991:       });
2992: 
2993:       // For JS strict compliance
2994:       return true;
2995:     });
2996:   },
2997:   

jQuery.fn.animate()は、カスタムアニメーションを作成するためのメソッドです。また、あらかじめ定義されているslideDownやfadeOutなどのメソッドも実際にはこのanimate()メソッドを呼び出して処理を実行しています。2935行目にあるように、prop, speed, easing, callbackの引数をとります。2936行目にて、3073行目で定義されているjQuery.speed()メソッドを呼び出してパラメータを利用しやすいように処理して変数optallに格納します。

2938行目は、queueに追加するか即時実行するかの分岐用の条件式が定義されています。通常は、queueに追加して1つのアニメーションが終わってから次のアニメーションが実行されるのですが、animateの第2引数に{queue:false}が渡された場合にはeach()メソッドを使って2939行目以降の処理がすぐに実行されます。これにより、2種類のエフェクトを同時に実行することが可能になり、表現力が向上します。

2939行目は、nodeTypeのチェックをして、もしelementノード以外の場合には何もせずに処理を終了します。

2942行目は、jQueryオブジェクトにoptallのプロパティを追加して、opt変数に代入しています。

2943行目は、選択された要素が現在非表示状態であるかを変数hiddenに格納し、同じく変数selfに自身を格納しています。

2945~2956行目で、第1引数propに渡されたアニメーションさせる属性について、それぞれ処理していきます。2946行目のif文により、指定された属性が"hide"で既に非表示、もしくは"show"で既に表示中の場合には、何もせずにopt.completeにcallback関数が定義されていればそれを実行します。2949行目のif文により、属性が"height"か"width"の場合には、現在のdisplayとoverflowの値を後々のために取得しておきます。

2958行目は、もしopt.overflowの値がnullの場合にhiddenを設定しておきます。

2961行目は、opt.curAnimにjQueryオブジェクトに引数propの値を追加して格納します。

2963行目からは、第1引数で渡されたアニメーション後の属性値propについて処理を行っていきます。

2964行目では、jQuery.fxをnewしてeに格納しています。これは、3122~3266行目で定義されているshow,hide,update,stepなどのエフェクト処理に関するメソッドと、3111行目で設定されるoptions,elem,propをプロパティとしてもつオブジェクトになります。

2966行目は、値がtoggle,show,hideのいずれかの場合の処理で、e.hide()もしくはe.show()が呼び出されます。toggleの場合は、状態によりいずれか適切な方を呼び出します。

2969行目は、何らかの数値、もしくは'+= 10'などの相対値が指定されている場合を判定するための正規表現です。また、2970行目で開始値として現在値を取得し、もし取得できなければ0に初期化しています。

2972行目のif文により、数値を含む形式であれば、変数endに終了値を設定、変数unitに単位が指定されていればその値を、なければ'px'を設定します。

2977行目は、もし単位が'px'でない場合に、startの値を再計算しています。

2984行目は、'+='もしくは'-='のように相対値で指定された場合に、終了値を計算し直しています。

ここまでで計算した開始値と終了値を使って、jQuery.fn.custom()メソッドを実行するのが2987行目です。指定された単位が'px'の場合には、2989行目にあるように第3引数を空にしてcustom()メソッドを実行します。custom()内でタイマーの設定などが行われます。

return文に値を返すために、最後に2994行目でtrueを返してanimate()メソッドは終了です。

jQuery.fn.queue()

2998:   queue: function(type, fn){
2999:     if ( jQuery.isFunction(type) || ( type && type.constructor == Array )) {
3000:       fn = type;
3001:       type = "fx";
3002:     }
3003: 
3004:     if ( !type || (typeof type == "string" && !fn) )
3005:       return queue( this[0], type );
3006: 
3007:     return this.each(function(){
3008:       if ( fn.constructor == Array )
3009:         queue(this, type, fn);
3010:       else {
3011:         queue(this, type).push( fn );
3012:       
3013:         if ( queue(this, type).length == 1 )
3014:           fn.apply(this);
3015:       }
3016:     });
3017:   },
3018: 

jQuery.fn.queue()メソッドは、選択されている要素に格納されているqueueを返します。queueは、アニメーションのエフェクトなどを格納した関数の配列になります。2999行目にて、第1引数typeに関数または配列が渡された場合は、引数fnにtypeの値を、引数typeに'fx'を設定します。

3004行目は、第1引数typeがないか、文字列が渡され第2引数fnがない場合の処理で、その場合は3045行目で定義されているqueue()メソッドを使って、現在の1番目の選択要素からtypeをキーとして値を取得し返します。3007行目以降は、queueに値を設定する処理で、queue()メソッドの第3引数を指定して関数を格納します。ここで、もしqueueに一つしか関数が格納されていない場合は、3013行目にてその関数を即時実行します。

jQuery.fn.stop()

3019:   stop: function(clearQueue, gotoEnd){
3020:     var timers = jQuery.timers;
3021: 
3022:     if (clearQueue)
3023:       this.queue([]);
3024: 
3025:     this.each(function(){
3026:       // go in reverse order so anything added to the queue during the loop is ignored
3027:       for ( var i = timers.length - 1; i >= 0; i-- )
3028:         if ( timers[i].elem == this ) {
3029:           if (gotoEnd)
3030:             // force the next step to be the last
3031:             timers[i](true);
3032:           timers.splice(i, 1);
3033:         }
3034:     });
3035: 
3036:     // start the next in the queue if the last step wasn't forced
3037:     if (!gotoEnd)
3038:       this.dequeue();
3039: 
3040:     return this;
3041:   }
3042: 
3043: });
3044: 

jQuery.fn.stop()は、指定した要素において、動作中のすべてのアニメーションを停止するためのメソッドです。もし、他のアニメーションがqueueに登録されていれば、実行中のアニメーションを停止した後、すぐにそれを実行します。

引数clearQueueとgotoEndは、ドキュメントに記載のない内部処理用のものです。3020行目でアニメーション処理用の関数が登録されているjQuery.timersを取得します。jQuery.timersに登録されている関数の実体は、3209行目で定義されているstep()になるのですが、こちらについては後ほど説明します。

3022行目は、第1引数clearQueueがtrueの場合にすべてのqueueをクリアします。

3025~3034行目のthis.eachが本メソッドの根幹をなす部分です。timers変数を逆順に処理することで、ループ処理中に追加されたアニメーションは無視されます。3028行目のif文により、現在選択されている要素に関するアニメーションのみを抽出します。そして、3032行目のspliceメソッドにより、実際にtimersの登録を解除します。また、もし第2引数gotoEndがtrueであれば、登録を解除する前にアニメーションの最終フレームを実行します。

3026行目にて、gotoEndがfalseであれば、dequeueメソッドを呼び出して次の処理を実行します。

最後に3040行目にて、メソッドチェーンのために自身を返して終了です。

queue()

3045: var queue = function( elem, type, array ) {
3046:   if ( !elem )
3047:     return undefined;
3048: 
3049:   type = type || "fx";
3050: 
3051:   var q = jQuery.data( elem, type + "queue" );
3052: 
3053:   if ( !q || array )
3054:     q = jQuery.data( elem, type + "queue", 
3055:       array ? jQuery.makeArray(array) : [] );
3056: 
3057:   return q;
3058: };
3059: 

queue()は内部的に利用される関数で、jQuery.fn.queue()メソッドから呼び出され、実際にqueueへの格納/取得を行います。

引数elemが指定されていない場合は、3046行目にてundefinedを返します。また、引数typeが指定されていなければ、デフォルト値として'fx'を定義します。3051行目は、jQuery.data()メソッドを利用して、実際にqueueデータを取得する処理です。

第3引数arrayが渡された場合は、3054行目にて値をqueueに設定します。また、queueにまだ何も設定されていない場合は、空の配列を登録しておきます。

最後に3057行目にて、取得したqueueを返します。

jQuery.fn.dequeue()

3060: jQuery.fn.dequeue = function(type){
3061:   type = type || "fx";
3062: 
3063:   return this.each(function(){
3064:     var q = queue(this, type);
3065: 
3066:     q.shift();
3067: 
3068:     if ( q.length )
3069:       q[0].apply( this );
3070:   });
3071: };
3072: 

jQuery.fn.dequeue()メソッドは、queueの先頭から登録されている関数を削除し、実行します。ここで、3061行目にあるように引数typeが指定されていない場合のデフォルト値は'fx'です。

まず3064行目にて、現在選択されている要素に格納されているqueueを取り出します。次に3066行目で、queueの最初の要素を削除します。そして、まだqueueに処理対象の関数が残っていれば、3069行目にて実際にその関数を実行します。

jQuery.speed()

3073: jQuery.extend({
3074:   
3075:   speed: function(speed, easing, fn) {
3076:     var opt = speed && speed.constructor == Object ? speed : {
3077:       complete: fn || !fn && easing || 
3078:         jQuery.isFunction( speed ) && speed,
3079:       duration: speed,
3080:       easing: fn && easing || easing && easing.constructor != Function && easing
3081:     };
3082: 
3083:     opt.duration = (opt.duration && opt.duration.constructor == Number ? 
3084:       opt.duration : 
3085:       { slow: 600, fast: 200 }[opt.duration]) || 400;
3086:   
3087:     // Queueing
3088:     opt.old = opt.complete;
3089:     opt.complete = function(){
3090:       if ( opt.queue !== false )
3091:         jQuery(this).dequeue();
3092:       if ( jQuery.isFunction( opt.old ) )
3093:         opt.old.apply( this );
3094:     };
3095:   
3096:     return opt;
3097:   },
3098:

jQuery.speed()は、jQuery.fn.animate()メソッドに渡されたパラメータを解釈するための内部用メソッドです。

3076行目により、第1引数speedが渡されかつそれがObject型の場合は、変数optにそのままspeedを設定します。それ以外の場合には、completeにcallback関数を設定します。3077行目と3078行目の論理演算子によって、何番目の引数にcallback関数が渡されても取得できるようになっています。durationには、第1引数speedをそのまま設定します。同様に、easingには、第3引数が指定されているかもしくは指定されていなくても第2引数easingが関数ではない場合に、第2引数easingを設定します。

3083行目は、opt.durationつまりアニメーションの実行時間を正規化します。数値が設定されている場合は、そのまま実行時間(ミリ秒)として扱います。もし数値ではなく、'slow'が指定されていたら600ミリ秒、'fast'が指定されていたら200ミリ秒、それ以外の場合は400ミリ秒を設定します。

3088~3094行目は、アニメーション終了時のcallback関数にqueueに格納されている次の処理を実行するように設定しています。

jQuery.easing()

3099:   easing: {
3100:     linear: function( p, n, firstNum, diff ) {
3101:       return firstNum + diff * p;
3102:     },
3103:     swing: function( p, n, firstNum, diff ) {
3104:       return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
3105:     }
3106:   },
3107:   

easingは、アニメーション処理時の動き方を定義する関数です。easingに定義する関数は、p(ステップ回数⁠⁠、n(経過時間⁠⁠、firstNum(初期値⁠⁠、diff(変化量)のパラメータをとり、現在のところfirstNumとdiffの値はそれぞれ0および1で固定のようです。'linear'は3101行目の式により線形の動きを、'swing'は3104行目の式によりCos曲線を描くような動きを定義しています。

デフォルトでは"linear"と"swing"の2つしか定義されていませんが、jQuery Easing pluginを使うといろいろな動きを追加することができます。

おすすめ記事

記事・ニュース一覧