09 | 2017/10 |  11

  1. 無料サーバー

User forum-FC2BLOG-Info-Edit Template-Post-Edit-Upload-LogOut

CSSやJavascript自習の苦闘史を綴っていきたい。恐縮ですがJavascriptを有効にしてご覧ください。
2005年12月から社会問題も掲載!


スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

jQuery を使って画像も動画も Popup する

popup メソッドは2年半前に一度作成していた

2007年12月。PopupTips と題して、jquery.js を最小限度使用してポップアップを作りました。お陰様で 6 件の拍手を戴きましたが、パソコンの高速化がますます定着し、加速されている今、jquery.js をフル活用したコードでも、描画に大きな支障は出ない程度にまでパソコン性能が向上しました。
だから、というわけでもありませんが、今回改めて jquery.js をフル活用して popup メソッドを作り直しました。

それは上の「関連エントリイ」に掲載した jQuery()活用(2) Popup Tips を自作する──jQuery解読(22) に詳述したので、関心のある方はそちらをご覧戴ければ幸いです。

jquery.js をフル活用して作成した popup メソッドの概要

ここでは自作の setPopup プラグインメソッドの概要を紹介させて戴きます。なお、以下のアンダーライン部に popup を仕込みましたので、どんな具合に作動するのか、このエントリイで確認して戴くことが出来ます。

  1. 改めて作成した popup は それを引き起こすタグ要素の title 属性値をそのコンテンツとするように設計しました。つまり、何らかの class 指定を行わなくても、jQuery('*[title]').setPopup(args) で popup 励起要素が指定出来るようにしました。
  2. 勿論、特定の要素に class 指定を施して、それらの要素に対してだけ popup を表示させることも可能です。つまり、popup 励起要素はいかようにも自由に指定できるようにしました。
  3. popup 励起要素内で mouseenter/mouseleave イベントが発生すると、popup 要素が表示/隠蔽されることは勿論ですが、更に mousemove イベントが起きると、popup 要素はマウスカーソルに追随して移動するように設計しました。なお、その際には popup 要素が画面からはみ出さないようにもしました。
  4. popup する内容はテキスト文字列だけではなく、任意の HTML タグ要素を指定出来るようにしました。つまり、title 属性値に記述した HTML 文が HTML 文として解釈され、popup 要素内に描画されるようにしました。例えば、番号付き箇条書きや画像を popup させることも可能です。(ここではパノラマ写真を popup します)
  5. 次に動画を popup させてみます。なお動画表示の場合には HTML 文に若干の工夫が必要です。popup 要素内にはマウスカーソルを移動出来ないので、自動再生を指定しておく必要がある、ということです。
    なお、youtube 動画の自動再生指定方法は、 youtubeの動画を自動再生する方法 に大変分かりやすく記されていました。

▲ToTop

どのエントリイでも、どのブログでも使えるように plug-in 化

作成した Javascript コードの全容は こちら に掲載しておきましたが、このコードをプラグイン化したことを強調しておこうと思います。

プラグイン化により、このブログのどのエントリイでも、否、このブログだけではなく、また Fc2 ブログでなくても、任意のブログで使える仕様になっているはずです。

jquery.js をフル活用しつつ、汎用的に利用できる───今後作成するjquery.js を利用したコードは、可能な限りこのような仕様にするつもりです。それこそが jquery.js を真の意味で活用することになるはずですし、そうなればこその「やり甲斐」でもあります。

jQuery の挙動を解読する(35):Traversing メソッドについて(1) ──jQuery解読(52)

何回かに分けて Traversing に分類されているインスタンスメソッドの解読を行います。

全体の項目とこのエントリイの項目
  • eq()、slice()、is()、filter()、closest()、not()、add()
  • children()、contents()、find()、next()、nextAll()
  • offsetParent()、parent()、parents()、prev()、prevAll()、siblings()、andSelf()、end()

このエントリイでは、本家サイトにおいて Traversing に分類されているインスタンスメソッドの内、次のメソッドを解読対象とします。

eq()、slice()、is()、filter()、closest()、not()、add()

eq()、slice() メソッド解読

eq() メソッドはインスタンスに登録されている要素ノードから、i+1 番目の 1 つのノードを取り出します。

そのために使用される slice() メソッドはインスタンス内に登録されている要素ノードから、連続した一部を取り出すためのメソッドです。このメソッドを適用すると、元のインスタンスの一部のプロパティを保持する新たなインスタンスが得られます。このとき、pushStack() メソッドにより、新たなインスタンスの selector プロパティには抽出経過を記録する文字列が代入されます。

■ eq() メソッド……インスタンスの配列 like なプロパティから i 番目の要素を取り出す
495: eq: function( i ) {
496:  return this.slice( i, +i + 1 );
497: }, 

■ slice() メソッド …… インスタンスの配列 like なプロパティからその連続した部分を取り出す
499: slice: function() {
500:  return this.pushStack( Array.prototype.slice.apply( this, arguments ),
501:   "slice", Array.prototype.slice.call(arguments).join(",") );
502: },
このエントリイ上で eq()、slice() を試してみる

左のボタンをクリックすると、最初の pre タグの背景色が teal になり、もう一度このボタンをクリックすると元に戻ります。

左のボタンをクリックすると 6 番目から 9 番目迄の 4 つの p タグ背景色が teal に変わり、もう一度クリックすると元に戻ります。

尚、ボタンをクリックした際には、mousedown 時にそのボタンの背景色が変わり、mouseup すると元に戻るように設定しました。

is()、filter() メソッド解読

is() メソッドは或るノードがインスタンス内に登録されているかどうかを調べます。filter() メソッドはその名の通りインスタンス内に登録されているノードを対象として、一定の条件でフィルタリングを行います。

■ jQuery(a).is(s) …… a にマッチする要素ノード内に s にマッチする要素があるかどうか調査する。
401: is: function( selector ) {
    // 引数 selector が存在し、かつ jQuery インスタンスの中に selector に
    // マッチするノードが 1 つ以上あれば true を、なければ false を返す。
402:  return !!selector && jQuery.multiFilter( selector, this ).length > 0;
403: },

■ jQuery(s,c).filter(selector) メソッド
349: filter: function( selector ) {
    // selector が関数の場合とそうでない場合とに分けて pushStack() メソッ
    // ドの結果を受け取る。
350:  return this.pushStack(
351:   jQuery.isFunction( selector ) && // selector が関数ならば
     // インスタンスの各プロパティに登録されている要素ノードに対して
352:   jQuery.grep(this, function(elem, i){
353:    return selector.call( elem, i ); // 順番に関数を適用し結果を返す。
354:   }) || // あるいは
355:   // 関数でない場合には、multiFilter メソッドを使用して、インスタンス
     // に登録されている要素ノードから selector にマッチする要素を取り出す。
356:   jQuery.multiFilter( selector, jQuery.grep(this, function(elem){
357:    return elem.nodeType === 1;
358:   }) ), "filter", selector );
359: },
このエントリイ上で is()、filter() を試してみる

左のボタンをクリックすると、この p タグの contents に「左のボタン」という文字列があるかどうか調べ、結果を animate 表示 します。当然それは存在しているのでtrueであることを示す文字列がアニメーションポップアップで表示されます。

尚、このボタンは単純なクリックボタンで toggle にはなっていません。表示された popup を消去するにはその popup 内にある×をクリックします。またボタンが複数回クリックされた場合には、popup を即ぐに消し去ってから新たなポップアニメーションが起動します。

また、このボタンだけは IE8 では作動しません。おそらくは IE8 の Animation に関するバグのためだと思われますが、正確な原因は不明です。

左のボタンをクリックすると「 左のボタン 」という文字列を含む全ての p タグ背景色が teal に変わり、もう一度クリックすると元に戻ります。

尚、ボタンをクリックした際には、mousedown 時にそのボタンの背景色が変わり、mouseup すると元に戻るように設定しました。

closest() メソッド解読

これは 1.3.x から追加された新しいメソッドです。jQuery(s,c)にマッチする要素( A )を対象として、closest(selector) を適用すると、A 及びその親ノードの中から、selector フィルタにマッチするノードをを取得します。

要点はインスタンスが参照しているノードからだけではなく、その親ノードからも条件に合致するノードを探す点にあります。イベントオブジェクトがバブリングして親要素にも達するように、このメソッドは親要素をも対象としている点が注目されます。「 closest(近親者)」と名付けられた所以です。

■ jQuery(s,c).closest(selector) メソッド
361: closest: function( selector ) {
    // jQuery.expr.match.POS は /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/
    // である( #1663 から )。そしてこの正規表現は次のような文字列となる。
    //「 コロンの後に nth から odd までのいずれかの文字列があり、その後に(数字)が 
    // 1 回以下続き、その後に - ではない 1 文字があるか行末まで何もない 」
    // ── selector がこのような文字列の場合には、 jQuery(selector) の
    // 結果を変数 pos に代入し、そうでない場合には null 値を代入する。
362:  var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
363:   closer = 0;
364:
365:  return this.map(function(){ // インスタンスに map メソッド処理を行って返す。
     // この結果、インスタンスの各要素及びその親ノードの中から、selector 条件に
     // 合致するノードが抽出され、それらをプロパティとする jQuery インスタンス
     // が返される。
366:   var cur = this; // この this は map メソッド内にあるので、
             // インスタンスに登録されている個々の要素ノードを指す。
367:   while ( cur && cur.ownerDocument ) { // cur 及びその document ノードが
      // ある限り、変数 pos があれば cur が pos の何番目かにあるかどうか
      // チェックし、pos がなければ cur にマッチする要素の中に selector
      //  にマッチするものがあるかどうかをチェックし、いずれかが true であれば、
368:    if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
       // cur 要素に closest と言う名前と closer 値を関連づける。
369:     jQuery.data(cur, "closest", closer);
370:     return cur; // curを返す。
371:    }
372:    cur = cur.parentNode; // cur をその親ノードとし、closer 値を増加する。
373:    closer++;
374:   }
375:  });
376: },

左のボタンをクリックすると「 メソッド 」という文字列を含む全ての p タグ背景色が teal に変わり、もう一度クリックすると元に戻ります。

尚、ボタンをクリックした際には、mousedown 時にそのボタンの背景色が変わり、mouseup すると元に戻るように設定しました。

not()、add() メソッド解読

not メソッドは名前からその機能が推測しにくいメソッドです。is() メソッドは「あるかないか」の真偽値を返しますが、notメソッドの返値は jQuery オブジェクトです。

このメソッドは jQuery インスタンスから、selector で指定された条件を満たさないノードを抽出します。

他方、add() メソッドはその名の通り、jQuery インスタンスに selector 条件に合致するノードを「追加」します。

■ jQuery(s,c).not(selector) メソッド
378: not: function( selector ) {
379:  if ( typeof selector === "string" ) // selector が文字列の時
380:   // test special case where just one selector is passed in
     // isSimple は #33 から /^.[^:#\[\.,]*$/。つまり最初に何か 1 文字があり、
     // 次に : でも、# でも、 [ でも、.でも、 ,でもない文字がゼロ個以上続く
     // 文字列を意味する。つまり、何らかのフィルターでもなく、クラス名でもなく、
     // id でもなく、複数のセレクタでもない単純な文字列を意味する。
381:   if ( isSimple.test( selector ) ) // selector が上のような条件の文字列ならば
      // インスタンスから selector 条件を満足するノードを削除し、その結果を
      // jQuery インスタンスを返す。
382:    return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector );
383:   else // 単純ではない文字列だったら、インスタンスから selector 条件に
      // 合致するノードを抽出して selector に代入する。
384:    selector = jQuery.multiFilter( selector, this );
385:  // selector に length 値があって、最後の selector 値が定義されていて、
    // selector がノードではなければ true を返す。
386:  var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
387:  return this.filter(function() { // インスタンスに登録されている各ノード
     // に対してfilter メソッドにより引数の関数を適用する。filter メソッド
     // 内では grep メソッドが条件に合致するノードだけを抽出する。
     // これにより各ノードが selector に合致しない場合には true が返され、
     // 合致した場合には false が返される。この結果 selctor 条件に合致しない
     // ノードだけが残され、これをプロパティとする jQuery インスタンスが返される。
388:   return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
389:  });
390: },

■ jQuery(s,c).add(selector) メソッド
392: add: function( selector ) {
393:  return this.pushStack( jQuery.unique( jQuery.merge(
394:   this.get(), // merge メソッドの第 1 引数は配列でなければならない。
395:   typeof selector === "string" ?
      // merge メソッドは第 2 引数が配列 like なプロパティ値を持つオブ
      // ジェクトであっても、配列そのものでも同様に扱える。
396:    jQuery( selector ) :
397:    jQuery.makeArray( selector )
398:  )));
399: },

jQuery の挙動を解読する(34):要素属性に係るメソッドの解読 ── jQuery解読(51)

このエントリイでは、本家サイトにおいて Attributes に分類されているインスタンスメソッドを対象にしてそのコード解読を行います。なお、Attri() メソッドはこちらのエントリイ( jQuery() の挙動を解読する(30) jQuery を使ってタグ属性や css 値を設定/取得する upon ver1.3.2──jQuery解読(45) )において、また text() と html() メソッドは、こちらのエントリイ( jQuery の挙動を解読する(33):DOM Manipulate メソッド解読──jQuery解読(50) )で、それぞれ解読済みなので、ここで対象とするメソッドは次の通りです。

addClass()、hasClass()、removeClass()、toggleClass()、val()

addClass()、hasClass()、removeClass()、toggleClass() が行う処理

これらのインスタンスメソッドは、順に、class 属性の「追加」、「有無調査」、「削除」及び「追加/削除の交互作用」を行うためのもので、hasClass() メソッド以外は 1 つのインスタンスメソッドの中に盛り込まれているので、解読するコードブロック数は 2 つになります。

まずは hasClass() から。

これは class 属性が存在するかしないかを調査するためのメソッドで返値は真偽値です。

■ hasClass( selector ) メソッド
405: hasClass: function( selector ) {
    // 引数 selector が存在し、かつ「 .selector 」が存在すれば true を、
    // 存在しなければ false を返す。
406:  return !!selector && this.is( "." + selector );
407: },
■ 関連メソッド(1) jQuery(a).is(s) …… a 内に s が在るかどうか調査する。
401: is: function( selector ) {
    // 引数 selector が存在し、かつ jQuery インスタンスの中に selector に
    // マッチするノードが 1 つ以上あれば true を、なければ false を返す。
402:  return !!selector && jQuery.multiFilter( selector, this ).length > 0;
403: },
■ 関連メソッド(2) jQuery.multiFilter()……elems 内から expr 該当/非該当要素を抽出する。
2383: jQuery.multiFilter = function( expr, elems, not ) {
2384:  if ( not ) { // not が true ならば
2385:  expr = ":not(" + expr + ")"; // expr 文字列を加工する。
2386: }
2387:  // 対象ノード elems から expr にマッチする要素を抽出する。
    // 返値は Sizzle.matches メソッドの定義から配列となる。
2388:  return Sizzle.matches(expr, elems);
2389: };

▲ToTop

次に、addClass()、removeClass()、toggleClass() について。

これらのメソッドは下に見るように jQuery.each( { name:function(){} },fn ) 形式のコードで定義されており、この構造のメソッドについては,別のエントリイ( anything from here jQuery の挙動を解読する(31):jQuery.each({ nameN:fnN(){}},function(){}) によるメソッド登録──jQuery解読(48) )でその解読を済ませています。

ここでは 3 つのクラス属性に関するメソッドが共通して利用している className クラスメソッドについて解読します。

■ addClass()、removeClass()、toggleClass()
1218:jQuery.each({
1219: removeAttr: function( name ) {
1220:  jQuery.attr( this, name, "" );
1221:  if (this.nodeType == 1)
1222:   this.removeAttribute( name );
1223: },
1224:
1225: addClass: function( classNames ) {
1226:  jQuery.className.add( this, classNames );
1227: },
1228:
1229: removeClass: function( classNames ) {
1230:  jQuery.className.remove( this, classNames );
1231: },
1232:
1233: toggleClass: function( classNames, state ) {
1234:  if( typeof state !== "boolean" )
1235:   state = !jQuery.className.has( this, classNames );
1236:  jQuery.className[ state ? "add" : "remove" ]( this, classNames );
1237: },
1238:
1239: remove: function( selector ) {
1240:  if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
1241:   // Prevent memory leaks
1242:   jQuery( "*", this ).add([this]).each(function(){
1243:    jQuery.event.remove(this);
1244:    jQuery.removeData(this);
1245:   });
1246:   if (this.parentNode)
1247:    this.parentNode.removeChild( this );
1248:  }
1249: },
1250:
1251: empty: function() {
1252:  // Remove element nodes and prevent memory leaks
1253:  jQuery(this).children().remove();
1254:
1255:  // Remove any remaining nodes
1256:  while ( this.firstChild )
1257:   this.removeChild( this.firstChild );
1258: }
1259:}, function(name, fn){
1260: jQuery.fn[ name ] = function(){
1261:  return this.each( fn, arguments );
1262: };
1263:});
■ jQuery.className()
709: className: {
710:  // internal only, use addClass("class")
    // このメソッドは jquery.js 内部でのみ使用するメソッドであり、
    // addClass("class") と記述して使用する。
711:  add: function( elem, classNames ) {
     // classNames をスペースで区切って Name 毎に配列の要素にして、
     // Name 毎に巡回処理する。
712:   jQuery.each((classNames || "").split(/\s+/), function(i, className){
      // elem が要素ノードでそれが className と一致するクラス名を持っていなければ
713:    if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
       // elem のクラス名があれば半角スペースを付け、なければ
       // 何も付けずに、className を elem のクラス名として追加登録する。
714:     elem.className += (elem.className ? " " : "") + className;
715:   });
716:  },
717:
718:  // internal only, use removeClass("class")
719:  remove: function( elem, classNames ) {
720:   if (elem.nodeType == 1) // elem が要素ノードならば
721:    elem.className = classNames !== undefined ? // elem にクラス名があれば
       // 要素 elem のクラス名を要素とする配列を作り、配列要素に対して
       // grep 処理を行う。
722:     jQuery.grep(elem.className.split(/\s+/), function(className){
        // classNames 内に className があるかないか調査し、
        // なければ true を、あれば false を返して、grep メソッド処理を行う。
        // ここに grep メソッドの第 3 引数が指定されていないので、渡された
        // 真偽値が true の場合はその要素を残し、false の場合には削除する。
        // この結果、grep から返される配列の要素から classNames 文字列に含
        // まれるクラス名は消えることになる。
723:      return !jQuery.className.has( classNames, className );
724:     }).join(" ") : // 返された配列の要素を半角スペースで結合して文字列にする。
       // こうして elem.className には classNames 内に含まれているクラス名が
       // 削除されたクラス名が代入される。
725:     ""; // クラス名がなければ空文字を返す。
726:  },
727:
728:  // internal only, use hasClass("class")
    // elem ノードが className というクラス名を持つかどうかを調べるメソッド
729:  has: function( elem, className ) {
     // 要素ノード elem が存在すれば、elem のクラス名か elem を要素とする
     // 配列の要素に、className があるかどうかを調査し、あれば true を、
     // 要素でなければ false を返す。
730:   return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
731:  }
732: },

jQuery(s,c).val(v) が行う処理

val(v) インスタンスメソッドは、v がない場合には jQuery(s,c) で抽出されるエレメント内の value 属性値を取得し、v がある場合には当該エレメントの value 属性値を設定します。様々な form 部品( input、button、select 等 )の value 属性を操作するためのメソッドなので、form 部品を余り使用しない私にとっては縁遠いものです。

なお、このメソッドが対象とする要素エレメントは、value 属性値を取得する場合には jQuery インスタンスに登録された最初のノードだけですが、設定する場合にはそのような限定はありません。

■ val(value) メソッド
409: val: function( value ) {
    // ケースA: value が未定義の場合、つまり 対象とするform 部品要素から
    // value を取得する場合の処理
410:  if ( value === undefined ) {
411:   var elem = this[0]; //インスタンスの最初のノードを elem に代入する。
412: 
413:   if ( elem ) { // elem があれば
414:    if( jQuery.nodeName( elem, 'option' ) ) elem が option タグならば
       // 属性値 value が明記されていればその値を返し、なければ text 値
       // を返す。なお、空オブジェクトとの or 条件としたのは value が未
       // 定義の時に undefined を返さないための対策か?
415:     return (elem.attributes.value || {}).specified ? elem.value : elem.text;
416:    
417:    // We need to handle select boxes special
418:    if ( jQuery.nodeName( elem, "select" ) ) { // elem が select タグならば
419:     var index = elem.selectedIndex,
420:      values = [],
421:      options = elem.options,
422:      one = elem.type == "select-one"; // type 値が "select-one"かどうか
423: 
424:     // Nothing was selected
425:     if ( index < 0 )
426:      return null; // 何も選択されていないならば null 値を返す。
427: 
428:     // Loop through all the selected options
       // one が true ならば i を indexとし、 max を index +1 とする、
       // false ならば i=0、max は options の要素数とする。
       // i < max を loop 条件とし、増分値は i++ とする。
429:     for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
430:      var option = options[ i ]; // i 番目の options ノードを変数 option に代入
431: 
432:      if ( option.selected ) { // option に selected 属性があれば
433:       // Get the specifc value for the option
         // val() メソッドを再帰呼び出しして結果を value に返す。
434:       value = jQuery(option).val();
435: 
436:       // We don't need an array for one selects
437:       if ( one ) // type 属性が select-one となっていた場合
438:        return value;
439: 
440:       // Multi-Selects return an array
441:       values.push( value );
442:      } // End of if (#432)
443:     } // End of for loop (#429)
444: 
445:     return values;
446:    }
447: 
448:    // Everything else, we just grab the value
      // キャリッジリターンを削除する。
449:    return (elem.value || "").replace(/\r/g, "");
450: 
451:   }
452: 
453:   return undefined; // 未定義値を返す。
454:  }
455: 
    //ケースB: value が数値の時。このときには value を設定することになる。
456:  if ( typeof value === "number" )
457:   value += ''; // 文字列に変換する
458: 
459:  return this.each(function(){ // インスタンス値を更新する。
460:   if ( this.nodeType != 1 ) // インスタンスが要素ノードではないとき
461:    return; // 何もしないで val() メソッドの適用を終える。
462:   // 従って以下のコードはインスタンスが要素ノードの時だけ進行される。
     // value が配列で、type 属性にラジオボタン又はチェックボックスがある場合
463:   if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )
      // this.value 値または this.name 値が value 配列内の要素と
      // 一致すれば checked 値を true とする。つまり当該ノードを「選択」
      // 状態とする。
464:    this.checked = (jQuery.inArray(this.value, value) >= 0 ||
465:     jQuery.inArray(this.name, value) >= 0);
466: 
     // インスタンスに登録されているノードが select タグの場合
467:   else if ( jQuery.nodeName( this, "select" ) ) {
      // value を配列 values に代入する。
468:    var values = jQuery.makeArray(value);
469: 
      // インスタンス内の各 option ノードを走査して
470:    jQuery( "option", this ).each(function(){
       // this.value または this.text 値が values 配列内にあれば
       // その要素の selected 値を true とする。
471:     this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
472:      jQuery.inArray( this.text, values ) >= 0);
473:    });
474: 
475:    if ( !values.length ) // 配列要素が空ならば
476:     this.selectedIndex = -1; // 無選択状態とする。
477: 
478:   } else // 属性がラジオボタンでも、チェックボックスでも、select でもなければ
479:    this.value = value; // インスタンスの value 属性値を value とする。
480:  });
481: },

▲ToTop

jQuery を使ってDOM Manipulate──例題で動きを見る

jQuery で DOM Manipulate

このエントリイでは、各種 DOM Manipulate のための jQuery インスタンスメソッドの例を試します。

それぞれのメソッドの働き方や差異は、jQuery本家サイトの Document でも見ることが出来ますが、相互に比較しながら一覧出来た方がより分かりやすいので、そのような例題を作ってみました。

なおここであつかったメソッドの解読は関連エントリイリスト内に表示した「・・・解読(50)」で行っています。

適用例の説明
  • 下のボタンは各種 DOM Manipulate メソッドを実行するためのもの。15 のいずれかをクリックすると、ボタンの下にある薄緑色のボックス内で各種メソッドの効果を表示します。
  • メソッドの適用効果を消去するにはもう一度同じボタンをクリックします。
  • メソッドの適用対象は、薄緑色のボックス内に配置した 3 つの p タグです。これをボタン上では $(this) と表示しました。3 つの pタグはピンクの枠で示していますが、それらを対象として各種 DOM Manipulate メソッドを適用します。
  • 他方、働き方を逆転させるメソッドの場合には、メソッド起動要素がサイト内に存在していなければ成りませんので、そのための要素として DIV タグ( class="targetDiv704" )を用意しました。

メソッドの引数として次の html 文字列を準備しました。

  • "<div style='background-color:maroon;border:4px dotted gray;margin:2px;padding:2px;'></div>"
  • "<p style='background-color:maroon;border:4px dotted gray;margin:2px;padding:2px;'>これはボタンをクリックしたことにより追加されたノードです。</p>"
  
   
   
   

(第1 pタグ)これは各種 DOM Manipulate メソッドの働き方を視覚的に見やすくしたサンプルです。

(第2 pタグ)このブロックを包含する div タグがあり、その中に複数の 3 つの p タグと 2 つの div タグを配置し、3 つめのパラグラフには画像を配置しました。

(第1 divタグ)3 つの p タグを操作対象として各種のメソッドを適用します。

(第3 pタグ) これは画像を含むパラグラフです。

(第2 divタグ)
逆転メソッドで使用する div タグ

jQuery の挙動を解読する(33):DOM Manipulate メソッド解読──jQuery解読(50)

Contents
  • domManip() メソッド解読
  • 各種 DOM Manipulate メソッドの性質と分類
  • 各種 DOM Manipulate メソッドの例を試す
  • 各種 DOM Manipulate メソッドを解読する───親ノード追加系
  • 各種 DOM Manipulate メソッドを解読する───子ノード追加系
  • 各種 DOM Manipulate メソッドを解読する───兄弟ノード追加系
  • 各種 DOM Manipulate メソッドを解読する───置換系

このエントリイでは DOM 要素操作 に係るインスタンスメソッドに焦点を当て、それらのメソッドを解読します。

対象とするメソッドは、domManip()、wrapAll()、wrapInner()、wrap()、append()、prepend()、before()、after() 、appendTo()、prependTo()、insertBefore()、insertAfter()、replaceWith()、replaceAll()、text()、html() です。

domManip()

まず、DOM 走査の基本的な役割を担う domManip() メソッドから解読します。

このメソッドは args 内の html 文字列によってインスタンスにノードを追加する際に、(1) clean メソッドを適用して html 文字列の記述ミスを正し、(2) またブラウザバグを補正すると共に、(3) DocumentFragment を使用することにより script タグをインスタンスに追加する、以上のことを行います。

■ domManip() インスタンスメソッド解読
  // 引数は順に、1:走査対象となるノードを要素とする配列、2:table 有無の指定、
  // 及び 3:適用するメソッドで、返値は jQuery インスタンスである。
514:domManip: function( args, table, callback ) {
515: if ( this[0] ) { // インスタンスに最初の要素があれば
    // 3 つの変数を定義する。まず文書断片( DocumentFragment )を作る。ここに、
    // this[0] が document ノードの時には owwnerDocument 値が null となる
    // ため < || this[0] > が必要となる。いずれにしても document ノードに
    // 文書断片を作成し、それへの参照を fragment 変数に代入する。
516:  var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
     // 引数 args を clean メソッドでチェックし return 値を変数 scripts に代入
     // する。ここに clean メソッド #961 から返される値は配列であり、その要素は
     // args 内に存在していた script タグである。
517:   scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
     // 上の clean メソッドにより args 配列要素の script タグ以外のタグ内にあった
     // script タグが削除された後に fragmentノードの chileNodes となっている。
     // ( #957 )。その中の第一子ノードを 変数 first に参照させる。
518:   first = fragment.firstChild;
519:  // このブロックは args 内に記述されていた script タグを、インスタンスの各要素
    // ノードから呼び出される実行される callback 関数の引数にするためのもの。
520:  if ( first ) // 第一子ノードが取得できれば
     // インスタンスに格納されている要素ノードの数だけ巡回処理する。
521:   for ( var i = 0, l = this.length; i < l; i++ )
      // root 関数で table ノードの場合の処理を行い、call の第 1 引数とし、
      // this に格納されている要素ノード数が 2 以上か イテレート変数が
      // 1 以上ならば fragment ノードの cloneNode をイベント付きで作成して
522:    callback.call( root(this[i], first), this.length > 1 || i > 0 ?
        // 第 2 引数とし、要素ノード数が 1 以下か i = 0 の時には
        // fragment ノード自体を第 2 引数とする。
523:      fragment.cloneNode(true) : fragment );
524: 
525:  if ( scripts ) // scripts が取得できたらば
     // その 1 つ 1 つを evalScript メソッドで評価する。
526:   jQuery.each( scripts, evalScript );
527: }
528: // 以上の処理を終えた this インスタンスを呼び出し元に返す。
529: return this; // また this[0] がない場合には this をそのまま返す。
530: 
531: function root( elem, cur ) {
    // 引数 table が true で、要素ノード=this[i] が table ノードで
    // cur が tr ノードならば
532:  return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ?
     // 取得できた最初の tbody ノードを返すか、取得できなければ tbody 要素ノードを
     // elem ノードに追加して return する。
533:   (elem.getElementsByTagName("tbody")[0] ||
534:   elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
     // その他の場合には elem 自身=this[0] を return する。
535:   elem;
536: }
537:}

▲ToTop

各種 DOM Manipulate メソッドの性質と分類

DOM 操作を行う jQuery インスタンスメソッドは次のように沢山あります。─── $(s,c).wrapAll(html)、$(s,c).wrapInner(html)、$(s,c).wrap(html)、$(s,c).append(elem)、$(s,c).prepend(elem)、$(s,c).before(elem)、$(s,c).after(elem)、$(s,c).appendTo(elem)、$(s,c).prependTo(elem)、$(s,c).insertBefore(elem)、$(s,c).insertAfter(elem)、$(s,c).replaceWith(elem)、$(s,c).replaceAll(elem)、$(s,c).text(str)、$(s,c).html(html) 等々。

これらの 15 個のメソッドの共通点は、jQuery インスタンス内に参照が取得されている要素ノードと、各のメソッドの引数である html や elem 等によって定まるノードとの、二者の組み合わせ方を定めることです。これらのインスタンスメソッドは、2 つの(あるいは 2 つの群の) DOM 要素間の前後関係(つまり兄弟関係)、包含/被包含関係(親子関係)、あるいは置換/被置換関係を定義づけます。

まず、組み合わせの対象となる 2 つ( 2 群 )の位置関係を整理しておくべきでしょう。分かりやすく表示するために、インスタンス内で参照付けられているノード群をA、引数によって参照付けられるノードをBとします。

親ノード追加系
  • $(s,c).wrapAll(html)……A[0]~A[last] までの全体を B で包含する。A[i] = B.childNodes[i]
  • $(s,c).wrapInner(html)……A の子ノード C を B で囲む。B.childNodes =A[i].childNodesとなる。
  • $(s,c).wrap(html)……B を個々の A の親ノードとする。A[i].parentNode = B
子ノード追加系
  • $(s,c).append(elem)……B を個々の Aの最後の子ノードとする。A[i].lastChild = B
  • $(s,c).prepend(elem)……B を個々の A の最初の子ノードとする。A[i].firstChild = B
  • $(s,c).appendTo(elem)……A を B の最後の子ノードとする。B.lastChild = A
  • $(s,c).prependTo(elem)……A を B の最初の子ノードとする。B.firstChild = A
兄弟ノード追加系
  • $(s,c).before(elem)……B を個々の A の直前の兄弟ノードとする。A[i].previousSibling = B
  • $(s,c).after(elem)……B を個々の A の直後の兄弟ノードとする。A[i].nextSibling = B
  • $(s,c).insertBefore(elem)……A を B の直前の兄弟ノードとする。A = B.previousSibling
  • $(s,c).insertAfter(elem)……A を B の直後の兄弟ノードとする。A = B.lastSibling
置換系
  • $(s,c).replaceWith(elem)……A を B で置換する。A[i] → B
  • $(s,c).replaceAll(elem)……B を A で置換する。A[i] ← B
  • $(s,c).text(str)……A の 子要素を B で置換する。A[i].childNodes = B
  • $(s,c).html(html)……A の 子要素を B で置換する。A[i].childNodes = B

▲ToTop

各種 DOM Manipulate メソッドの例を試す

解読の前に各種 DOM Manipulate メソッドの適用例をこのページ上で見てみます。それぞれのメソッドの働き方や差異を見るにはそれが手っ取り早いからです。

適用例の説明
  • 下のボタンは各種 DOM Manipulate メソッドを実行するためのもの。15 のいずれかをクリックすると、ボタンの下にある薄緑色のボックス内で各種メソッドの効果を表示します。
  • メソッドの適用効果を消去するにはもう一度同じボタンをクリックします。
  • メソッドの適用対象は、薄緑色のボックス内に配置した 3 つの p タグです。これをボタン上では $(this) と表示しました。3 つの pタグはピンクの枠で示していますが、それらを対象として各種 DOM Manipulate メソッドを適用します。
  • 他方、働き方を逆転させるメソッドの場合には、メソッド起動要素がサイト内に存在していなければ成りませんので、そのための要素として DIV タグ( class="targetDiv" )を用意しました。

メソッドの引数として次の html 文字列を準備しました。

  • "<div style='background-color:maroon;border:4px dotted gray;margin:2px;padding:2px;'></div>"
  • "<p style='background-color:maroon;border:4px dotted gray;margin:2px;padding:2px;'>これはボタンをクリックしたことにより追加されたノードです。</p>"
  
   
   
   

(第1 pタグ)これは各種 DOM Manipulate メソッドの働き方を視覚的に見やすくしたサンプルです。

(第2 pタグ)このブロックを包含する div タグがあり、その中に複数の 3 つの p タグと 2 つの div タグを配置し、3 つめのパラグラフには画像を配置しました。

(第1 divタグ)3 つの p タグを操作対象として各種のメソッドを適用します。

(第3 pタグ) これは画像を含むパラグラフです。

(第2 divタグ)
逆転メソッドで使用する div タグ

各種 DOM Manipulate メソッドを解読する───親ノード追加系

$(s,c).wrapAll(html) メソッド解読

このメソッドは、jQueryインスタンスが参照するノード全体を、 html 文字列によってサイトに追加されるノードの子ノードにします。

■ $(s,c).wrapAll(html) メソッド
219: wrapAll: function( html ) {
220:  if ( this[0] ) {
221:   // The elements to wrap the target around
     // html 文字列で指定されたノード群を document 内で検索し、マッチ
     // したノードの clone ノードを作り、変数 wrap に代入する。
222:   var wrap = jQuery( html, this[0].ownerDocument ).clone();
223:   // インスタンスの最初のノードに親ノードがあれば、
224:   if ( this[0].parentNode )
      // clone ノード ( wrap ) をインスタンスの、最初の配列もどきプロ
      // パティが参照するノード ( this[0] ) の前に配置する。
225:    wrap.insertBefore( this[0] );
226:
227:   wrap.map(function(){
      // この this は mapメソッド内にあり、map メソッドが wrap から
228:    var elem = this; // 呼び出されているので wrap を参照する。
229:
230:    while ( elem.firstChild ) // 最も深い階層にある最初の
231:     elem = elem.firstChild; // 子ノードを elem に代入し、
232:
233:    return elem; // その最後の子ノードを返す。
     // 返された最深層の最初の子ノードの中に、this を最後の子ノードとして追加する。
     // なおここの this はインスタンスから呼び出される wrapAll メソッド内にある
234:   }).append(this); // ので、インスタンスを参照する。
235:  }
236:
237:  return this; // インスタンスを返す。
238: },
$(s,c).wrapInner(html) 解読

このメソッドはインスタンスの各々の子ノードを html 文字列による追加ノードで包含します。

■ $(s,c).wrapInner(html) メソッド
240: wrapInner: function( html ) {
241:  return this.each(function(){ // インスタンスの各々のノードに対して
     // 当該ノードの子ノード jQuery( this ).contents() を html により
     // 挿入されるノードで包含する。なお、contents() は #1186 で定義
     // されているインスタンスメソッドである。
242:   jQuery( this ).contents().wrapAll( html );
243:  });
244: },
$(s,c).wrap(html) 解読

このメソッドはインスタンスが参照する各々のノードを html 文字列による追加ノードで包含します。

■ $(s,c).wrap(html) メソッド
246: wrap: function( html ) {
    // インスタンスの各々のノードを巡回処理し結果を返す。
247:  return this.each(function(){
     // 各々のノードを html 文字列による追加ノードの子ノードとする。
248:   jQuery( this ).wrapAll( html );
249:  });
250: },

▲ToTop

各種の DOM Manipulate メソッドを解読する───子ノード追加系

子ノードを追加する 最初の 2 つのメソッドでは共通して domManip メソッドが使われています。また、appendTo メソッドと prependTo メソッドはそれぞれ append と prepend メソッドを利用して対象を逆転させているだけですから、結局 4 つのメソッドで domManip メソッドが使われていることになります。

これは、誤記や table に係る IE のバグ対策を施さないと追加により、インスタンスが指し示すノードが文法的におかしくなってしまうことがあるためです。

■ 子ノード追加系 メソッド
▼ append() メソッド
252: append: function() {
253:  return this.domManip(arguments, true, function(elem){
254:   if (this.nodeType == 1)
      // この this は domManip メソッドの定義から、インスタンス内で指し
      // 示されている個々のノードを指す。( #522 )その個々のノードの
      // 最後の子ノードとして elem により定義されるノードを挿入する。
255:    this.appendChild( elem );
256:  });
257: },
258:

▼ prepend() メソッド
259: prepend: function() {
260:  return this.domManip(arguments, true, function(elem){
261:   if (this.nodeType == 1)
      // この this は domManip メソッドの定義から、インスタンス内で指し
      // 示されている個々のノードを指す。( #522 )その個々のノードの
      // 最初の子ノードの前に、elem により定義されるノードを挿入する。
262:    this.insertBefore( elem, this.firstChild );
263:  });
264: },

▼insertBefore() 及び insertAfter() メソッド
// 以下のブロックの解読は別途こちらのエントリイ ( jQuery の挙動を解読する(31):
jQuery.each({ nameN:fnN(){}},function(){}) によるメソッド登録──jQuery解読(48) )
で行いました。
1198:jQuery.each({
1199: appendTo: "append",
1200: prependTo: "prepend",
1201: insertBefore: "before",
1202: insertAfter: "after",
1203: replaceAll: "replaceWith"
1204:}, function(name, original){
1205: jQuery.fn[ name ] = function( selector ) {
1206:  var ret = [], insert = jQuery( selector );
1207:
1208:  for ( var i = 0, l = insert.length; i < l; i++ ) {
1209:   var elems = (i > 0 ? this.clone(true) : this).get();
1210:   jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
1211:   ret = ret.concat( elems );
1212:  }
1213:
1214:  return this.pushStack( ret, name, selector );
1215: };
1216:});

▲ToTop

各種の DOM Manipulate メソッドを解読する───兄弟ノード追加系

兄弟ノードを追加する最初の 2 つのメソッドでも共通して domManip メソッドが使われ、その中では clean メソッドが呼び出されます。また、insertBefore メソッドと insertAfter メソッドはそれぞれ before と after メソッドを利用して対象を逆転させているだけですから、結局 4 つのメソッドで domManip メソッドが使われていることになります。

このような回り道をするのは、clean メソッドを使って誤記や table に係る IE のバグ対策を施さないと、ノード追加によって、インスタンスが指し示すノードが文法的におかしくなってしまうことがあるためです。

■ 兄弟ノード追加系 メソッド
▼ before() メソッド
266: before: function() {
267:  return this.domManip(arguments, false, function(elem){
     // この this は domManip メソッドの定義から、インスタンス内で指し
     // 示されている個々のノードを指す。( #522 )その個々のノードの
     // 前に、elem により定義されるノードを兄弟要素として挿入する。
268:   this.parentNode.insertBefore( elem, this );
269:  });
270: },
271:

▼ after() メソッド
272: after: function() {
273:  return this.domManip(arguments, false, function(elem){
     // この this は domManip メソッドの定義から、インスタンス内で指し
     // 示されている個々のノードを指す。( #522 )その個々のノードの
     // 次の兄弟ノードの前に、elem により定義されるノードを挿入する。
     // つまり個々のノードの後ろに elem を兄弟ノードとして挿入する。
274:   this.parentNode.insertBefore( elem, this.nextSibling );
275:  });
276: },
// insertBefore と insertAfter の 2 つのメソッドに関しては、直上の
// 「子ノード追加系」でリストアップした #1198-1216 を参照のこと。

▲ToTop

各種の DOM Manipulate メソッドを解読する───置換系

replaceWith(elem) メソッドは極めて簡単なコードで定義されています。インスタンスが指し示す個々の要素ノードの直後に兄弟要素 elem を追加してから、当該要素ノードをを削除します。

■ 置換系 メソッド
▼ replaceWith() メソッド
491: replaceWith: function( value ) {
492:  return this.after( value ).remove();
493: },

▼ replaceAll() メソッド
// replaceAll メソッドに関しては、こちらを参照のこと。

▼ text() メソッド
201: text: function( text ) {
    // 引数がオブジェクトでもなく空でもなければ
202:  if ( typeof text !== "object" && text != null )
     // インスタンスが指し示す個々の要素の子ノードを削除してから、
     // 0 プロパティが存在したら document ノードにテキストノードを作り、
     // それを個々の要素の子ノードとして追加する。
203:   return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
204:  // 引数がオブジェクトか、空文字のとき
205:  var ret = ""; // 空文字を変数 ret に代入
206:
207:  jQuery.each( text || this, function(){ // 
208:   jQuery.each( this.childNodes, function(){ // 子ノードを巡回処理
209:    if ( this.nodeType != 8 ) // 子ノードがコメントノードでない場合
210:     ret += this.nodeType != 1 ? // 子ノードが要素ノードでなければ
211:      this.nodeValue : // その値、つまりテキスト文字列を ret に代入
        // 子ノードが要素ノードの場合 this を配列の要素に入れて
212:      jQuery.fn.text( [ this ] ); //text メソッドを再帰呼び出しする。
213:   });
214:  });
215:
216:  return ret; // 文字列 ret を返す。
217: },

▼ html() メソッド
483: html: function( value ) {
484:  return value === undefined ? // value が未定義ならば
485:   (this[0] ? // 最初の配列もどきプロパティ 0 が存在すれば
      // プロパティ 0 の innerHTML 文字列内に変数 expando による
      // 「半角スペース + "jQuery" + 数字 = 数字」文字列があれば
      // それを削除してから、innerHTML 文字列を返値とする。
      // プロパティ 0 が存在しなければ null を返す。
486:    this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
487:    null) :
     // value が存在すれば個々の要素の子ノードを空にしてから、
     // value を子ノードとして追加する。
488:   this.empty().append( value );
489: },



・・・・・

jQuery の挙動を解読する(32):jQuery.clean() メソッド解読──jQuery解読(49)

jQuery.clean() メソッドを徹底解明!

以前にこちらのエントリイ ( jQuery()の挙動を解読する(6) jQuery.clean()メソッド解読──jQuery解読(10) )で clean メソッドを解読しました。

しかし、jquery.js ver1.3.2 でこのメソッドは一部仕様が変わり拡張されました。

そこで改めてこのメソッドに正面から取り組み、ほぼ全容を解明しました。

特にver 1.3.x 系から追加された Document Fragment を利用して DOM にスクリプトを追加することなく実行する機能はなかなか興味深いものがあります。

そこで、jQuery()の挙動を解読する(6) jQuery.clean()メソッド解読──jQuery解読(10) を抜本改定しましたので、よろしければご覧下さい。

上のエントリイでは、ver 1.3.x 系の新たな仕様であるスクリプト実行機能のサンプルも用意しました。

非常に興味深いのでこのエントリイでも抜粋しておきます。

▲ToTop

cleanメソッドが 第 3 引数 fragment を取る場合の興味深い例示

clean() メソッドの第 3 引数 「 fragment 」 は ver 1.3.x で初めて登場しました。1.2.6 まではこのメソッドは 2 つの引数しか取りませんでした。この fragment 引数は一体何をするために設けられたのか?───それがなかなか解明できませんでした。今でも完全に解明しきったとは思えません。まが霧が完全には晴れないからです。

それでも、コードを睨み続け、サンプルを幾つか試してみて分かったことがあります。

スクリプトを使って Web ページに新たにノードを追加する場合において、追加する html 文字列内にスクリプトを埋め込んで( いわば「 二重スクリプト 」になります。 )、fragment 引数つきで clean() メソッドを実行すると、大変興味深いことが起こるのです。

くだくだ説明するよりも、実例を示すのが手っ取り早いでしょう。

下のボタンをクリックするとこの文字列付近で 2 つの変化が起こります。或る文章がボタンの前に挿入され(文字列を内包する p タグです)、同時に画面中央に alert 表示が出ます。

ここで行ったことは以下の通りです。

上のボタンがクリックされると、このページに埋め込まれた或る Javascript コードが起動します。そのコードでは jQuery().before() メソッドを使って、このボタンの前に兄弟要素 p タグとスクリプトタグを挿入します。

このとき、jQuery().before() メソッドはその定義により、domManip メソッドを呼び出し、その中では clean メソッドが fragment 引き数付きで起動されます。この結果、挿入する html 文字列に記述されているスクリプトタグは、ページ内に挿入された直後に削除され、かつ実行されるのです。こうして alert 表示が実行されます。

スクリプトによって或る DOM エレメントを追加する行為において、そのノード内にスクリプトタグを含める必要性が果たしてどれほどあるのか判然とはしません。挿入する html 文字列内に敢えてスクリプトタグを埋め込まなくても、挿入する DOM エレメントとは別にスクリプトから直接同様の効果を得ることも可能でしょう。

それでも、二重スクリプトはなかなかおもしろい効果をWebページに与えるのではないか、と感じています。

なお、以上を実現する Javascript コードでは name エンティティに悩まされました。フゥ~(;´_`;)

■ 上のことを実現している Javascript コード
(function(){
 $("#fragmentTest702").css("background-color","pink")
  .toggle(function(){
   $(this).text("clean(e,c,fragment)メソッドテスト取り消し")
    .css("background-color","aquamarine")
    .before("<p id='before702' class='ta_l accentuate2'>このブロックは下のボタンをク
    リックした結果表示されました。それは jQuery().before('<p id=\"before702\">
    ・・・</p><script type=\"text/javascript\">alert(\"実験成功・・・\")
    </script>) メソッドテストであり、追加する html 文字列内に上に見るようにスク
    リプトコードを含む場合の実験です。</p><script type='text/javascript'>alert('実験
    成功!\\nこの alert 表示がスクリプト実行結果です')<\/script>");
  },function(){
   $("#before702").remove();
   $(this).text("clean(e,c,fragment)メソッドテスト実行")
    .css("background-color","pink");
  });
})();

▲ToTop

jQuery の挙動を解読する(31):jQuery.each({ nameN:fnN(){}},function(){}) によるメソッド登録──jQuery解読(48)

$.each( { nameN,funcN }, fn ) 形式によるメソッドの一括登録

jquery.js #1177-1263 では $.each クラスメソッドを利用して、 fn の内容別に 3 つに分けて各種メソッドの一括登録が行われています。

この方法によるメソッド登録は他にも、2 箇所 ( #2888-2892、 #3954-3964 )ありますが、今回対象とする 3 ブロックのメソッド一括登録は、jQuery prototype オブジェクト、すなわちインスタンスメソッドの登録である点において、他の 2 箇所での使用方法と一線を画しています。

さて、#1177-1263 では obj オブジェクトプロパティとして、複数のメソッド名とメソッドを定義し、それらを巡回処理により順に jQuery インスタンスメソッドとして登録しています。まず #1177-1196 では トラバース系の DOM メソッドを、続く #1198-1216 では 挿入置換系の DOM メソッドを、#1218-1263 では属性処理 DOM メソッドを、それぞれ定義しています。

ここに、これらは共通して $.each( obj,fn ) メソッド実行コードなので、jquery.js 読み込み時に実行され、こうして obj オブジェクト内にリストアップされている各種メソッドは、プロトタイプオブジェクトに登録され jQuery インスタンスメソッドとなります。

以下に、これらの 3 つの $.each({nameN,funcN},fn) 形式によるインスタンスメソッド一括登録コードを解読します。

DOM トラバース系のインスタンスメソッド登録

次のコードは、親子、兄弟姉妹、前後などの要素ノードを扱うインスタンスメソッドを一括してプロトタイプオブジェクトに登録するものです。

   // #1177-1187 は「登録するメソッドリスト」をプロパティとするオブジェクト
1177:jQuery.each({ // $.dir、.nth、.sibling は Sizzle 内で定義されている。
1178: parent: function(elem){return elem.parentNode;},
1179: parents: function(elem){return jQuery.dir(elem,"parentNode");},
1180: next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
1181: prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
1182: nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
1183: prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
1184: siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
1185: children: function(elem){return jQuery.sibling(elem.firstChild);},
1186: contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
1187:}, function(name, fn){// name はリスト内のメソッド名称、fn はリスト内の関数を指す
    // プロトタイプオブジェクトに function name(selector){・・・ } を登録する。 
1188: jQuery.fn[ name ] = function( selector ) { // selector は name メソッドの引数
     // インスタンス this に fn メソッドを map する。
     // 例えばイテレート処理の最初には、parent プロパティが対象となるが、
     // map メソッドの定義から ret は this.length 個の this[i].parentNode 
     // を要素とする配列となる。
1189:  var ret = jQuery.map( this, fn );
1190:  // もし selector が文字型の場合
1191:  if ( selector && typeof selector == "string" )
      // ノードを要素とする配列 ret から selector 条件に
      // 合致するノードを抽出する。
1192:   ret = jQuery.multiFilter( selector, ret );
1193:  // 重複チェックを掛けて jQuery オブジェクトとして return
     // する。このとき this インスタンスは return オブジェクトの
     // prevObject プロパティに保持する。
1194:  return this.pushStack( jQuery.unique( ret ), name, selector );
1195: };
1196:});

以下には、上の each メソッド第 1 引数オブジェクトのプロパティ内で、繰り返し登場する Sizzle ユーティリティ内にある jQuery クラスメソッドを簡単に解読しました。

なお、これらの 3 つのコードは全て jQuery クラスメソッドの登録ですが、興味深いことに extend メソッドを使わずメソッドを直接 jQuery オブジェクトに登録する「 素朴な 」方法を採用しています。

■ jQuery.multiFilter
2383: jQuery.multiFilter = function( expr, elems, not ) {
2384:  if ( not ) {
2385:   expr = ":not(" + expr + ")"; // "not(expr)"文字列を生成して代入
2386:  }
2387:  // 要素ノード elems から expr に合致する要素を抽出し return 値とする。
2388:  return Sizzle.matches(expr, elems);
2389: };
■ jQuery.dir
2391: jQuery.dir = function( elem, dir ){
2392:  var matched = [], cur = elem[dir];
2393:  while ( cur && cur != document ) { // elem の dir プロパティを
2394:   if ( cur.nodeType == 1 ) // 走査し、それが要素ノードであれば
2395:    matched.push( cur );  // matched 配列に格納する。
     // cur の dir プロパティを cur に代入してloopを繰り返す。
     // これにより cur ノードの子ノードがあれば、それが走査対象となる
2396:   cur = cur[dir];
2397:  }
2398:  return matched; // 該当要素ノードを格納した配列を返す。
2399: };
■ jQuery.nth
2401: jQuery.nth = function(cur, result, dir, elem){
2402:  result = result || 1;
2403:  var num = 0;
2404:  // cur がある限りその dir プロパティを走査する
2405:  for ( ; cur; cur = cur[dir] ) // cur が要素ノードで num 値が
2406:   if ( cur.nodeType == 1 && ++num == result ) // result に等しくなるまで
2407:    break; // loopする。
2408: 
2409:  return cur;  // 該当した要素ノードを返す。
2410:
 };

▲ToTop

挿入置換系の DOM インスタンスメソッド

2 つめは、登録済みの jQuery インスタンスメソッドを使って、その機能を逆転させるためのメソッド群を一括登録するコードです。

1198: jQuery.each({
1199:  appendTo: "append",
1200:  prependTo: "prepend",
1201:  insertBefore: "before",
1202:  insertAfter: "after",
1203:  replaceAll: "replaceWith"
    // name はプロパティ名、original はプロパティ値
1204: }, function(name, original){
1205:  jQuery.fn[ name ] = function( selector ) { // selector は name メソッドの引数
      // selector に合致するノードを insert jQuery オブジェクトに代入
1206:   var ret = [], insert = jQuery( selector );
1207:    // 配列のようなプロパティを走査
1208:   for ( var i = 0, l = insert.length; i < l; i++ ) {
       // 要素があれば clone をイベント付きで作ってその、なければ this の
       // 要素を配列として取り出す。
1209:    var elems = (i > 0 ? this.clone(true) : this).get();
       // jQuery(insert[i]).original( elems ) メソッドを起動。
       // これにより insert[i] ノードに elems が追加される。
1210:    jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
1211:    ret = ret.concat( elems );  // elems を ret 配列に線形化して結合する。
1212:   }
1213:    // ret jQuery オブジェクトを返し、その prevObject プロパティに this を保持する。
1214:   return this.pushStack( ret, name, selector );
1215:  };
1216: });

▲ToTop

属性処理系の DOM インスタンスメソッド

最後に、タグ属性を操作するメソッド群の登録です。

1218:jQuery.each({
1219: removeAttr: function( name ) {
1220:  jQuery.attr( this, name, "" );
1221:  if (this.nodeType == 1)
1222:   this.removeAttribute( name );
1223: },
1224:
1225: addClass: function( classNames ) {
1226:  jQuery.className.add( this, classNames );
1227: },
1228:
1229: removeClass: function( classNames ) {
1230:  jQuery.className.remove( this, classNames );
1231: },
1232:
1233: toggleClass: function( classNames, state ) {
1234:  if( typeof state !== "boolean" )
1235:   state = !jQuery.className.has( this, classNames );
1236:  jQuery.className[ state ? "add" : "remove" ]( this, classNames );
1237: },
1238:
1239: remove: function( selector ) {
1240:  if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
1241:   // Prevent memory leaks
1242:   jQuery( "*", this ).add([this]).each(function(){
1243:    jQuery.event.remove(this);
1244:    jQuery.removeData(this);
1245:   });
1246:   if (this.parentNode)
1247:    this.parentNode.removeChild( this );
1248:  }
1249: },
1250:
1251: empty: function() {
1252:  // Remove element nodes and prevent memory leaks
1253:  jQuery(this).children().remove();
1254:
1255:  // Remove any remaining nodes
1256:  while ( this.firstChild )
1257:   this.removeChild( this.firstChild );
1258: }
1259:}, function(name, fn){
1260: jQuery.fn[ name ] = function(){ // prototype オブジェクトに登録する。
     // each メソッドの定義から次のように読み解けます。
     // jQuery インスタンス(this) の各プロパティに fn(arguments) メソッド
     // を登録し、処理後の this インスタンスを返値とする。例えば、
     // name が remove の時には、remove.apply(this,arguments) となる。
1261:  return this.each( fn, arguments );
1262: };
1263:});
1264:

jQueryに学ぶ Javascript の基礎(9) concat() メソッド──jQuery解読(47)

 過去のエントリイで行った jQuery.map() メソッド解読を抜本的に改め、かつ 配列のメソッドである concat を徹底的に解明しました。

 map() メソッドの一部における concat() メソッドの使い方を jquery.js ver 1.2.6 と最新の ver 1.3.2 とで比較し、高速化のために後者においてコードが改訂されていることも確認しました。

 よろしければこちら 【 jQuery() の挙動を解読する(9) jQuery.map()メソッド解読 upon ver1.3.2──jQuery解読(13)】 をご覧下さい。

jQueryに学ぶ Javascript の基礎(8) String.replace() メソッド──jQuery解読(46)

string.replace(/・・・/,function(hit,b1,b2,・・・){}) メソッド

string.replace() は比較馴染みやすい、また分かりやすいメソッドです。何故ならば「置換」は、ワードやエクセル等のメジャーアプリケーションは元より、エディターでも必須機能ですし、言葉の意味からも機能が類推しやすいからです。

事実、多くのサイトで string.replace( beforeString, afterString ) のように引数付きで紹介され、例題の多くも 引数は置換前文字列、置換語文字列の2 つの「定数文字列」として説明されています。

しかし、第 2 引数に関数を取る場合に触れたサイトは限られています。ところが、その機能を知ってみると関数による置換は極めて有用で便利です。どうしてもっと紹介されないのだろうか、と訝しく思います。置換前文字列内の部分文字列を、置換後文字列に部分的に利用することは決して少なくないからです。

jquery.js での使われ方

この関数を第 2 引数とする replace() メソッドは、jQuery.curCSS() クラスメソッドに登場します。#1056-1057 の

 1056: name = name.replace(/-([a-z])/ig, function(all, letter){
 1057:   return letter.toUpperCase();
 1058: });

です。このエントリイでは第 2 引数に関数を取る replace メソッドについて学習します。

参考サイトで調べる

この件で全面的に参考になる Web Pages は MDC(Mozilla Developer Center)にありました。こちら【 replace - MDC 】には、第 2 引数が関数の場合の例題があり、またこのメソッドの詳細な解説も掲載されています。(残念ながら英語版です。)

それでも「例題」というものの神通力はたいしたもので、例題を見て本質は十分理解できます。以下がその例題です。

function replacer(str, p1, p2, offset, s){
  return str + " - " + p1 + " , " + p2;
}
"XXzzzz".replace(/(X*)(z*)/, replacer); // returns:XXzzzz - XX , zzzz
ここに

置換の対象となる文字列:"XXzzzz"

置換対象文字列:/(X*)(z*)/

一致文字列:str

置換後文字列:str + " - " + p1 + " , " + p2(つまり return 値)

改めて jquery.js での使われ方を調査

#1056-1057 の以下のコードを改めて見てみると

 1056: name = name.replace(/-([a-z])/ig, function(all, letter){
 1057:   return letter.toUpperCase();
 1058: });

これは、"abc-def" を "abcDef" に置換する、所謂駱駝文字列作成コードです。

置換対象はハイフンに続く一文字のアルファベットであり、letter は部分文字列である [a-z] (任意の小文字アルファベット)を指しますから、letter.toUpperCase() によって "-" とその直後にある小文字の任意のアルファベット 1 文字、合計 2 文字が 1 文字の大文字に変わります。

ここに、学習のために上のコードを一寸拡張し、name 文字列に複数のハイフンのある文字列を与えてみて挙動をフォローしてみます。

var str ="abc-defghi-jkl-mnop"
str = str.replace(/-([a-z])/ig, function(hit, letter, offset, orStr){
    return " , hit : "+ hit + " , letter.toUpperCase() : " + letter.toUpperCase() +
     " , offset : " + offset + " , orStr : " + orStr + "\n";
});

以上のコードを走らせると置換後の str は以下のようになります。

str == "
abc , hit : -d , letter.toUpperCase() : D , offset : 3 , orStr : abc-defghi-jkl-mn
efghi , hit : -j , letter.toUpperCase() : J , offset : 10 , orStr : abc-defghi-jkl-mn
kl , hit : -m , letter.toUpperCase() : M , offset : 14 , orStr : abc-defghi-jkl-mn
nop"
  • 各行行頭の文字列は置換対象外の文字列です。具体的には abc、efghi、kl 及び nop です。
  • 続く hit : -d 等は正規表現 /-[a-z]/ にヒットした置換対象文字列 -d とその説明文字列です。
  • 次の letter.toUpperCase() : D 等が置換後文字で、コロンの前が説明、後が置換後の文字です。
  • offset : 3 等は対象文字列の先頭からのヒット位置の説明とヒット位置です。最初の「-」は3番目にあります。(最初の文字は 0 )
  • orStr : abc-defghi-jkl-mn は対象文字列全体を意味しています。orStr が説明で、コロンに続く文字列は、置換対象となった文字列全体、すなわち上の場合には置換前の str です。

このように僅かな文字列に対しても、replace メソッドは多くの情報をもたらします。正規表現による部分文字列指定がこうした複雑な情報取得を可能にしています。

自作例題を作ってみる

上の例題を踏まえて以下のような例題を作ってみました。

var str="I love you.";
var ret = str.replace( /^(i)(.+)(you).*$/i, function(all,p1,p2,p3){
 return p3.charAt(0).toUpperCase() + p3.substring(1) +" must" + p2 + "me !"} );
// この結果 ret=="You must love me !" となる。

柔軟に置換結果を得られることが分かりました。

jQuery() の挙動を解読する(30) jQuery を使ってタグ属性( css 値含む)を設定/取得する upon ver1.3.2──jQuery解読(45)

このエントリイでは、HTML タグ要素の属性の設定/取得に係る(その属性の 1 つである CSS スタイル値の設定/取得を含む) jQuery の各種メソッドについて学習を深めたいと思います。

HTML要素の属性とは

そのためにまず、jquery.js 解読の前に、タグ要素属性、或いは 要素属性とは何か、どのような特性があるのか、改めて復習しておくべきでしょう。

HTML文では要素の属性を開始タグの中で指定します。属性には殆どの要素で共通して使用することの出来る次の 6 つの「汎用属性」があります。id、class、lang、title、style 及び dir です。

他方、href、src、xmlns 等々のように要素の機能・性格に応じた固有の属性が沢山あります。

さて、今日ではマイナーとなり、W3C非推奨とされていますが、ブラウザの黎明期から20世紀末頃までは、例えば、color、border、center、align等々のように、表示を左右する属性を要素属性に書き連ねる HTML 文が当たり前でした。

今や「文書の構造は HTML 文で、装飾を CSS と Javascript で」とのルールが支配的になっており、要素の style 属性を使用するインラインスタイル設定さえも推奨されていませんから、構造化したHTML文を書き続けていると、ついタグ要素属性のことは軽視しがちになってしまいます。

しかし、このエントリイで取り上げる attr インスタンスメソッドは、Javascriptからタグ要素属性を操作する訳ですから、改めて要素属性について認識を新たにする必要があります。特にstyle属性が存在する場合にのみ、当該要素に係るstyleオブジェクトが存在していることに注意しなければなりません。

jQuery.attr(elem, name, value) メソッドの機能

このメソッドは、第 1 引数で指定する要素の、第 2 引数で指定する属性を取得するか、または設定するものです。このように目的は単純ですが、ここでもまた IE 対策に多くのコードが費やされています。解読作業を通じて、IE の勝手な仕様に費やされたユーザーの労苦と年月を思い、改めて驚愕してしまいました。

さて、整理のためにもまず IE のタグ属性に係る固有仕様やバグを列挙しておきたいと思います。

Javascript でタグ属性をコントロールする場合に
配慮しなければならないIE 固有の仕様等
class 属性

DOM では class 属性を className 名で扱うが、W3C 準拠ブラウザの場合には getAttribute/setAttribute メソッドにおいては "class" で class 属性にアクセスできる。

しかし、IE では getAttribute/setAttribute メソッドにおいても class ではなく className としなければならない。

getAttribute メソッド
IE では "style" では style 属性値は取得できず、"cssText"を使わなければならない。
IE は href、src、style 属性値を取得する場合第 2 引数を要求する。
setAttribute メソッド
IE では DOM 要素に直接 style 属性を設定できない。替わりに elem.style オブジェクトに対して設定する。
IE は setAttribute メソッドにおいて自動的に value 値を文字列に変換しない。
opacity 属性
IE は alpha filter に固執している。
input タグの type 属性
IE はその属性値を勝手に変更する。
float プロパティ
IE の場合 style.cssFloat ではなく、style.styleFloat

jQuery.attr() クラスメソッド解読

■ jQuery.attr() クラスメソッド■
    // 引数は順に対象要素、属性名称、属性値
 967: attr: function( elem, name, value ) {
 968:  // don't set attributes on text and comment nodes
 969:  if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
 970:   return undefined; // テキスト/コメントノードの場合には未定義値を返す。
 971:
 972:  var notxml = !jQuery.isXMLDoc( elem ), // elem が xml 文書内の要素かどうか
 973:   // Whether we are setting (or getting) // value があれば set = true、
 974:   set = value !== undefined; // なければ set = false。
 975:
 976:  // Try to normalize/fix the name  // xml 文書ではなくかつ、
     // name の値が登録されている標準名称であればそれを使う。
     // jQuery.props は #3212-#3223 で定義されているクラスプロパティ
 977:  name = notxml && jQuery.props[ name ] || name;
 978:
 979:  // Only do all the following if this is a node (faster for style)
 980:  // IE elem.getAttribute passes even for style
 981:  if ( elem.tagName ) { // elem にタグ名称があれば
 982:
 983:   // These attributes require special treatment
 984:   var special = /href|src|style/.test( name ); // 特殊名称かどうかチェック
 985:
 986:   // Safari mis-reports the default selected property of a hidden option
 987:   // Accessing the parent's selectedIndex property fixes it
 988:   if ( name == "selected" && elem.parentNode )
 989:    elem.parentNode.selectedIndex; // safari バグ対策。親ノードの index 値を設定
 990:
 991:   // If applicable, access the attribute via the DOM 0 way
      // elem 内に name があり xml 文書ではなく、特殊文字列でもなければ
 992:   if ( name in elem && notxml && !special ) {
 993:    if ( set ){ // value が与えられていれば(つまり設定の場合)
 994:     // We can't allow the type property to be changed (since it causes problems in IE)
        // 処理対象が input タグで name 値が type で、親要素があれば
 995:     if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
 996:      throw "type property can't be changed";
 997:
 998:     elem[ name ] = value; // name 値に value を「敢えて」代入する。
 999:    }
1000:
1001:    // browsers index elements by id/name on forms, give priority to attributes.
       // 処理対象が form タグ要素で name 属性値が取得できる場合には
1002:    if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
        // そのノードバリュー値を return する。
1003:     return elem.getAttributeNode( name ).nodeValue;
1004:
1005:    // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
1006:    // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
1007:    if ( name == "tabIndex" ) {
1008:     var attributeNode = elem.getAttributeNode( "tabIndex" );
1009:     return attributeNode && attributeNode.specified //★不明な箇所
1010:      ? attributeNode.value
1011:      : elem.nodeName.match(/(button|input|object|select|textarea)/i)
1012:       ? 0
1013:       : elem.nodeName.match(/^(a|area)$/i) && elem.href
1014:        ? 0
1015:        : undefined;
1016:    }
1017:
1018:    return elem[ name ];
1019:   }
1020:   // IE では "style" を getAttribute/setAttribute メソッドに設定できないので、
1021:   if ( !jQuery.support.style && notxml &&  name == "style" )
       // elem として elem.style を、また nameとして cssText を使用して
       // jQuery.attr() メソッドを再帰呼び出しする。
1022:    return jQuery.attr( elem.style, "cssText", value );
1023:   // #1022 による再帰呼び出し時には、#981 の if 条件が成立しなくなるので、
      // #1024-#1035 は適用されない。つまり #1024-#1035 は name=="style" 時には
      // 適用されなくなる。
1024:   if ( set ) // 属性を設定する場合 IE では value 値を文字列に変換しないため
1025:    // convert the value to a string (all browsers do this but IE) see #1070
       // elem の name 属性ノードの値を強制的に文字列に変換する。
1026:    elem.setAttribute( name, "" + value );
1027:
1028:   var attr = !jQuery.support.hrefNormalized && notxml && special
1029:     // Some attributes require a special call on IE
        // 特殊な属性(href, src, style)の場合、取得に際して
        // IE のみ第 2 引数が必要となり、この 2 は「値を返す」の意だそうです。
1030:     ? elem.getAttribute( name, 2 )
1031:     : elem.getAttribute( name );
1032:
1033:   // Non-existent attributes return null, we normalize to undefined
      // #1028 で取得した attr が null 値ならば undefined に変える。
1034:   return attr === null ? undefined : attr;
1035:  } // #981以降の if 文がここで終わる。
1036:
1037:  // elem is actually elem.style ... set the style
1038:  // #1039-#1054 は #1040 行の if 条件により他のブラウザには適用されない。
     // 専ら IE だけに適用され、しかも再帰後の attr メソッドだけで意味のある
     // ブロックである。このブロック内の elem は #1021-#1022 により、既に
     // elem.style となっているが、1度目の attr メソッド実行では、このブロッ
     // クに到達する前に attr メソッドが再帰呼び出しされてしまう。よって
     //  #1039-#1054 ブロックは IEに対する再帰呼び出しの場合にのみ適用される。
1039:  // IE uses filters for opacity // IE の不透明度を処理する
1040:  if ( !jQuery.support.opacity && name == "opacity" ) {
1041:   if ( set ) { // 属性設定の場合
1042:    // IE has trouble with opacity if it does not have layout
1043:    // Force it by setting the zoom level
1044:    elem.zoom = 1;
1045:
1046:    // Set the alpha filter to set the opacity
       // elem.filter プロパティに opacity 属性をセットする。
1047:    elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
        // alpha(・・・)があればそれを消去し、value に整数部がなければ空文字を、
        // 整数部があれば value 値を 100 倍してフィルター値を作成する。
1048:     (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
1049:   }
1050:   // filter プロパティが存在し、"opacity=" 文字列があれば
1051:   return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
       // 2 番目の opacity(・・・) を100で割ってからその値を文字列化して返す。
       // なお何故 2 番目なのかは解明できなかった。
1052:    (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
1053:    ""; // "opacity=" 文字列がない場合空文字を返す。
1054:  } // IE 不透明度対策終わり
1055:  // name 文字列を駱駝文字列化する。置換後文字列に関数が置かれているが、
     // その使用と使い方については、学習のためにも後術する。
1056:  name = name.replace(/-([a-z])/ig, function(all, letter){
1057:   return letter.toUpperCase();
1058:  });
1059:
1060:  if ( set ) // 属性設定の時には elem の name 属性にここまで処理してきた
1061:   elem[ name ] = value; // value を代入する。
1062:
1063:  return elem[ name ]; // elem の name 属性値を返す。
1064: },

▲ToTop

jQuery().attr() インスタンスメソッド解読

attr() インスタンスメソッドは、当該インスタンスに登録されている要素の属性値を取得/設定するためのメソッドで、その内実は attr() クラスメソッドに他なりません。

とっても attr() メソッドは複雑な構造となっているため、引数の状態に応じて具体的なコード進行を探ってみます。

  • まず、引数が文字列型の name だけの場合

    インスタンスに登録されている最初の要素の name 属性値を取得します。このとき $.props() メソッドによりブラウザにより差異のある名称の統一が図られ、また当該 name に相当する属性が存在しない場合には(#1034)未定義値が返されます。(#172-179)

  • 次に、引数が nama と value の 2 つの場合

    当該インスタンスに登録されている各要素に対して、当該 name 属性に value 値を設定します。(#182-191)この場合 type がありませんから、$.attr メソッドの第 1 引数は this となります。

  • 第 3 に、引数が object 型の name だけの場合

    直前の場合と同様に #182-191 の部分が作動して、当該インスタンスに登録されている各要素に対して、nameオブジェクト内の各プロパティ名を属性とし、プロパティ値をその属性値に設定します。

  • 第 4 に、第 3 引数が与えられた場合(と言っても jquery.js においてそれは "curCSS" だけしかケースとしては存在していません。)

    name が文字列型ならば $.curCSS(this[0],name) が起動されて、インスタンスの最初の要素の、 name 属性の算出 CSS スタイル値を取得します。
    ここに算出 CSS スタイル値とは、ブラウザが当該要素を描画する際に算出されるスタイル値のことで、当該スタイル属性の定義有無に関わらず、ブラウザが算出するスタイル値となります。

    なお、後述するようにこのケースはまさに css() インスタンスメソッドそのものに他なりません。

  • 第 5 に、第 3 引数 type == "curCSS" が与えられ、かつ name がオブジェクト型の場合

    インスタンスの各要素の style オブジェクト( this.style )に対して、$.attr(this.style,name,$.prop(this,options[name],"curCSS",name) が起動されます。

    ここに prop クラスメソッドは、単位 "px" が必要なスタイル名にはそれを付け、不要ならば付与しない役割を果たします。

    こうして、各要素のstyle属性に対して value または value + "px" が設定されます。

■ jQuery().attr() インスタンスメソッド
   // 引数は順に属性名、属性値、type は 実際には"curCSS" 
168: attr: function( name, value, type ) {
169:  var options = name;
170:
171:  // Look for the case where we're accessing a style value
172:  if ( typeof name === "string" ) // name が文字型で
173:   if ( value === undefined ) // attr( name, type ) ならば
      // インスタンスの最初のノードが存在すれば、jQuery[type]( this[0], name )
      // 又は jQuery.attr( this[0], name ) の返値を返す。
      // type == "css" | "curCSS" ならば CSS 値を取得することになる。
      // 実際 css() インスタンスメソッドは this.attr(key,value,"curCSS") の
      // 形式で attr() インスタンスメソッドを呼び出す。
174:    return this[0] && jQuery[ type || "attr" ]( this[0], name );
175:
176:   else { // name が文字列型でないならば
177:    options = {}; // 変数 options を空オブジェクトに変えてから、
178:    options[ name ] = value; // { name : value } とする。
179:   }
180:
181:  // Check to see if we're setting style values
    // このブロックは name が文字型ではない場合に作動する。
182:  return this.each(function(i){ // インスタンス内の各要素エレメントに対して
183:   // Set all the styles  // 実際には style 以外の要素属性の設定も行う。
184:   for ( name in options ) // オブジェクト走査を行う。
185:    jQuery.attr( // jQuery.attr() クラスメソッドを起動して
186:     type ?
187:      this.style : // type があれば this.style
188:      this, // type がなければ this
       // attrメソッドの第 3 引数はjQuery.prop()メソッドの返値とする。
       // 返値は value + "px" または value となる。
189:     name, jQuery.prop( this, options[ name ], type, i, name )
190:    );
191:  });
192: },
193:

jQuery().css() インスタンスメソッド解読

このメソッドは css 値をインスタンスに設定したり、インスタンスから読み取るもので、内実は attr() インスタンスメソッドを利用しています。

具体的には、上述の jQuery().attr() インスタンスメソッドの第 4 のケースを参照してください。

■ jQuery().css() インスタンスメソッド
194: css: function( key, value ) {
195:  // ignore negative width and height values
196:  if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
197:   value = undefined;
198:  return this.attr( key, value, "curCSS" );
199: },

▲ToTop

jQueryに学ぶ Javascript の基礎(8) jQuery.support upon ver1.3.2──jQuery解読(44)

ver 1.3.x から登場した jQuery.support クラスプロパティとは?

直前エントリイで触れた curCSS() クラスメソッドの中にも jQuery.support が登場していましたが、別エントリイで触れようと思ったので、この新しい jQuery クラスプロパティには言及しませんでした。

しかし、曖昧さは無知と安逸への下り坂をスタンバイします。この気持ち悪さを解消するには曖昧なままにせず、解明する道しか選択肢がありません。

そこで、意訳も含めて大変うまく訳していると思われるサイト【jQuery 1.3.2 日本語リファレンス】をこっそり(^_^);眺めさせてもらって jQuery.support を学習しました。

極めて雑な言い方ですが、このプロパティはクロスブラウザ対応に係る 1 つの答えです。

それはブラウザの基礎的な事項について W3C 対応か否かを調査する多数のメソッドを内包させたブラウザ調査ツールとも言うべきものです。

これにより、各種ブラウザの W3C 対応状況を今までよりも遙かに分かりやすく知ることが出来るようになりました。

使っているブラウザのW3C対応を知るために

jQuery.support を使えば今見ているブラウザの性能を把握できます。

大変分かりやすいので先に触れたサイトにも出ているサンプルを、ここに引用して理解を深めたいと思います。

因みに、IE8 であっても殆どのプロパティ値が false ( =W3C 標準仕様ではない)となります。相変わらずの Microsoft 社の対応が垣間見られます。

他方、当初は IE もどきとして開発されてきた Opera は途中から路線を変更し、今や W3C 準拠ブラウザとなり、実際 Opera Ver 9.64 でこのページを表示したところ、以下の全ての結果が true となりました。

今見ているブラウザの jQuery.support による W3C 対応チェック

出典:jQuery 1.3.2 日本語リファレンス

 $(function(){$.each($.support, function(key, val){
  $("dl").append("<dt>"+"・"+key+"</dt><dd class='ml_4'>"+val+"");
 });});

▲ToTop

jQuery() の挙動を解読する(29) jQuery で DOM エレメントの CSS 値を取得する upon ver1.3.2──jQuery解読(43)

jquery.js は jQuery.css() や jQuery.curCSS() クラスメソッドで、スタイル宣言されていない CSS スタイル値や未描画要素の CSS スタイル値を取得する

CSS 値はそれが明示的に style 宣言されている場合には elem.style.styleName で容易に所得出来ます。しかし明示的に宣言されていない限り、要素が隠蔽( display : none )されていたり、ブラウザによる描画前の時点では、当該要素の幅や高さは取得できません。

さて、jQuery サンプルで多用される 1 つのメソッドに jQuery(x).css(y) インスタンスメソッドがあります。これは jQuery(x) によって Web サイト内から抽出した DOM Nodes を対象として、y で指定された様々な CSS スタイルを設定したり、宣言されているスタイル値を取得する汎用的なメソッドです。

これに対して jQuery.css( elem, name, ・・・) は elem DOM Node の name スタイル属性値を取得するためのクラスメソッドで、このメソッドの前後に定義されている swap() メソッド(#734-#748)や curCSS() メソッド(#781-#845)と連携して、明示的にスタイルが宣言されていない場合や要素が隠蔽されている場合も含めて、スタイル値を取得します。

このエントリイでは jquery.js がどのように CSS 値の取得を行うのかを解読しようと思います。

なお、以下に登場するボックスモデルに係る名称は下図に拠ります。

ボックスモデルの寸法説明図

呼称は全て日本語化し、マージン辺、ボーダー辺、パディング辺、内容辺とします。

jQuery.css() クラスメソッド解読

■ jQuery.css() クラスメソッド ■
750: css: function( elem, name, force, extra ) {
    // 引数は順に、タグ要素、スタイル名称。force は算出スタイル値を求めか否か、
    // extra は内容辺、ボーダー辺またはマージン辺のどこまでのスタイル値を求めるか
751:  if ( name == "width" || name == "height" ) {
752:   var val, props = { position: "absolute", visibility: "hidden", display:"block" },
       which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
753:
754:   function getWH() {
755:    val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
756:
757:    if ( extra === "border" )
758:     return;
759:
760:    jQuery.each( which, function() {
761:     if ( !extra )
762:      val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
763:     if ( extra === "margin" )
764:      val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
765:     else
766:      val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
767:    });
768:   }
769:
770:   if ( elem.offsetWidth !== 0 )
771:    getWH();
772:   else
773:    jQuery.swap( elem, props, getWH );
774:
775:   return Math.max(0, Math.round(val));
776:  }
777:
778:  return jQuery.curCSS( elem, name, force );
779: },

jQuery.css() メソッドは次のようにして CSS 値を取得します。

  1. ユーザーが値を取得したいスタイル( name で指定)が幅か高さである場合(#751)

    この場合には Microsoft 社が IE において定義し、他のブラウザも追随した便利なメソッド offsetWidth あるいは offsetHeight を使って幅や高さを求めます。

    1. elem.offsetWidth 値があれば(#770)、つまり当該要素がブラウザで描画されていれば、getWH() を起動し、要素の幅又は高さを計測します。勿論、幅 or 高さのいずれを取得するかはユーザーが name で指定します。
    2. elem.offsetWidth 値が取得できない場合、そうなるのは描画されていないからですから jQuery.swap() を起動して、ブラウザ表示に変化が出ないように、要素の CSS スタイル設定を、「絶対配置・非表示かつブロック表示」に一時的に変更してから、getWH() メソッドを呼び出して当該要素のスタイル値を取得します。
    3. getWH() 関数内の処理……ボーダー辺までの幅か高さを求める場合を除き jQuery.curCSS() メソッドを呼び出して計測します。
      1. ボーダー辺までの幅か高さを求める場合には、offset 値はボーダー辺までの値ですから、offset 値をそのまま返します。(#757-758)
      2. マージン辺までの幅か高さを求める場合(#763-764)margin 値を offset 値に加算します。
      3. 内容辺までの幅か高さを求める場合(#761-762及び#765-766)パディング値とボーダー値を offset 値から差し引きます。
      4. パディング辺までの幅か高さを求めることは意味がないと考えたのでしょう、算出しません。
  2. ユーザーが取得したいスタイル名が幅でも高さでもない場合(#772)

    処理を jQuery.curCSS( elem, name, force ); に委ねます。(#778)

▲ToTop

jQuery.css() の肝は中で使用されている this にあり

さて、このメソッドは決して難解な箇所はありません。たった 1 つのことを除いては!

中で 3 箇所使用されている this が一寸見には何を指しているのか、大変わかりにくいのです。

前後の文脈から何を指すべきかは推測できますが、「どうしてこの this がそれを指すのか?」───それが暫く分かりませんでした。

解明の糸口は推測した内容と値から、逆に色々考えていて突如見えてきました。

Javascript の基本中の基本ですが、メソッド内で使用される this は、当該メソッドを呼び出したオブジェクトを参照します。

これを改めて踏まえて、css() クラスメソッド内で使用されている this について解明してみます。

上のコードで注目に値する箇所は getWH() です。その中では each() クラスメソッドが起動され、更にその中で curCSS() クラスメソッドが呼び出されていますが、3 カ所の this は全て getWH() 内の each() 内の curCSS() の引数内にあります。

要点は 3 つの this が全て each() クラスメソッド内にあって、jQuery.curCSS() クラスメソッドの引数となっていることです。

each メソッドは第 1 引数の配列要素毎にそれを呼び出し元にして、第 2 引数の関数を実行します。callback.call( arguments[0][i], arguments[1] ) です。

上のコードにおいて which は [ "Left", "Right" ] か又は [ "Top", "Bottom" ]です(#752)。これを第 1 引数として、第 2 引数である 760 ~ 766 行の関数が call されます。ですから呼び出された関数内で this はまず which を参照します。

しかし、呼び出された関数内部で更に別の関数 jQuery.curCSS() メソッドが起動されますから、この関数が起動した後は this は jQuery 関数オブジェクトを参照します。

しかし、this は jQuery.curCSS() メソッドの引数として登場しています。ここが肝心なところです。

もし this が jQuery.curCSS() メソッドの内部で使用されれば、それは this の仕様から jQueryを参照します。しかし、引数である this は無名関数内から jQuery.curCSS() メソッドに外挿され、投入されます。つまり、所与の値として付与されるのですから、このメソッドはそれを受け取って起動するわけで、受け取る時点では this は jQuery ではなく 巡回処理対象 which が参照するいずれかの要素を参照しているのです。

こうして this は "Left"、 "Right"、"Top"、あるいは "Bottom" に置き換えられることになります。

▲ToTop

jQuery.swap() クラスメソッド解読

このクラスメソッドは elem に対して、options による新たなスタイルを一時的に設定し、elem に設定されている CSS スタイルではなく、optionsによるその新たな CSS スタイルに callback( 実際には getWH ) 関数を適用します。

swap() メソッドは、jquery.js の中では css() メソッドからたった1度呼び出されるだけですが、隠蔽されているために、あるいは描画されていないために CSS スタイル値を取得できない要素のスタイル値を、ブラウザ表示を一切変えることなく取得するためのものです。つまり、要素の幅や高さを描画前に知るために使われます。

display:none が指定されている要素は、ブラウザから見れば存在しないことと同値です。また、ボックス内容領域幅よりも長いテキストノードは、暗黙的か明示的かに関わらず CSS スタイルが設定されていない限り、描画されなければその表示幅や高さは決まりません。

しかし、ブラウザ描画前に要素の高さや幅を知りたいケースは決して少なくありません。例えば mouseover 時のポップアップ表示はその好例です。popup は mouseover した要素の直近位置で、ブラウザ表示領域からはみ出さぬような位置に、適切な大きさの領域を表示させ、その中に解説文字などを示す場合に使用します。そのときには幅を指定し高さを文字数に応じて可変となるようにしますが、その大前提として事前に高さを知る必要があります。そうしないとポップ領域がウィンドウの上又は下にはみ出してしまうからです。

以上のようなケースに対処するための関数が、swap() メソッドです。

■ jQuery.swap() クラスメソッド ■
734: // A method for quickly swapping in/out CSS properties to get correct calculations
735: swap: function( elem, options, callback ) {
    // 引数は順にタグ要素、css() メソッド内で定義した要素の css style オブジェクト
    // callback は swapメソッド呼び出し時の引数定義により getWH() メソッドとなっている。
736:  var old = {}; // elem の本来のスタイルプロパティを記憶させるオブジェクト
737:  // Remember the old values, and insert the new ones
738:  for ( var name in options ) { // elem に設定されている各種スタイル値を
739:   old[ name ] = elem.style[ name ]; // old オブジェクトに複写する。
     // css() メソッド内で次の props が設定済みとなっている。
     // props = { position: "absolute", visibility: "hidden", display:"block" }
     // 第2引数 options に登録されているこの CSS 値を elem のスタイルオブジェクトに複写する。
740:   elem.style[ name ] = options[ name ];
741:  }
742:  // CSS 値計測用プロパティが追加された elem から callback 関数 getWH を呼び出す。
    // この処理の中で必要な CSS 値を取得する。
743:  callback.call( elem ); // css() から呼び出されたときには callback = getWH となっている。
744:
745:  // Revert the old values
746:  for ( var name in options ) //処理が終わったら記憶させてあるプロパティを
747:   elem.style[ name ] = old[ name ]; // elem に戻す。
748: },

▲ToTop

jQuery.curCSS() クラスメソッド解読

これはその名の通り Current CSS 値(算出スタイル値とも呼ぶ)を取得するメソッドで、jquery.js 内では css() メソッドなど 10 箇所から呼び出されています。

IE 以外の W3C 規格に準拠するブラウザの場合と、準拠を頑なに拒絶する IE の場合とで、全く異なる方法で CSS 現在値を取得しなければならないため、つまりクロスブラウザ対応を図る必要があるためコードが長くなっています。

■ jQuery.curCSS() メソッド ■
781: curCSS: function( elem, name, force ) { // force については後述する797行の解説を参照
782:  var ret, style = elem.style;
783:
784:  // We need to handle opacity special in IE
785:  if ( name == "opacity" && !jQuery.support.opacity ) { // IE の場合
786:   ret = jQuery.attr( style, " opacity" ); // 不透明度値を ret に取得する。
787:
788:   return ret == "" ? // 返値を決める。
789:    "1" :
790:    ret;
791:  }
792:
793:  // Make sure we're using the right name for getting the float value
794:  if ( name.match( /float/i ) ) // 正しく float セレクタ名があれば
     // jquery.js では W3C 準拠表現の cssFloat も IE 固有の styleFloat も共に
     // styleFloat で統一して扱っています。( #3210 )
795:   name = styleFloat;
796:
    // force == false で style と style.name があれば style.name 値をそのまま使用する
    // つまりブラウザによる算出スタイル値を使わずに、スタイル属性の設定値を利用する。
    // 裏返せば force=="true" ならば、style 属性の有無に拘わらず、カレントスタイル値を算出する。
797:  if ( !force && style && style[ name ] )
798:   ret = style[ name ];
    // 以下はブラウザによる描画値を取得するためのコード
    // force を指定することによりブラウザの算出描画値を取得する。
799:  // getComputedStyle 関数があれば( つまり IE 以外 )
800:  else if ( defaultView.getComputedStyle ) {
801:
802:   // Only "float" is needed here  // float 文字列が name に含まれる場合、
803:   if ( name.match( /float/i ) )
804:    name = "float"; // 使用する呼称は「 float 」だけでよい。
805:   // 所謂駱駝文字列内の大文字を小文字化してハイフンを前に付ける。
     // Javascript スタイル属性表現を CSS スタイル表現に変える。
806:   name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
807:   // elem 要素の描画値を取得する準備。
808:   var computedStyle = defaultView.getComputedStyle( elem, null );
809:
810:   if ( computedStyle ) // computedStyle があれば値を取得する。
      // その値をretに代入する。これによりブラウザによる描画値が ret に代入される。
811:    ret = computedStyle.getPropertyValue( name );
812:
813:   // We should always get a number back from opacity
814:   if ( name == "opacity" && ret == "" )
815:    ret = "1";
816:   // 以下は #842 迄 IE の場合
817:  } else if ( elem.currentStyle ) {
818:   var camelCase = name.replace(/\-(\w)/g, function(all, letter){
819:    return letter.toUpperCase(); // 駱駝文字列への変換を行う。
820:   });
821:   // 値を取得する。
822:   ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
823:
824:   // From the awesome hack by Dean Edwards
825:   // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
826:
827:   // If we're not dealing with a regular pixel number
828:   // but a number that has a weird ending, we need to convert it to pixels
     // #822 の取得値 ret が数字始まりで "px" が途中にあるか、ない場合には、
829:   if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
830:    // Remember the original values
831:    var left = style.left, rsLeft = elem.runtimeStyle.left;
832:
833:    // Put in the new values to get a computed value out
      // left 値を取得してランタイムスタイルの left 値に代入し、
834:    elem.runtimeStyle.left = elem.currentStyle.left;
835:    style.left = ret || 0; // style.left 値には #822 の値を入れておき、
836:    ret = style.pixelLeft + "px"; // ret 値は正しい値を取得して代入する。
837:
838:    // Revert the changed values // 元の値を戻す。
839:    style.left = left;
840:    elem.runtimeStyle.left = rsLeft;
841:   }
842:  }
843:
844:  return ret; // ret 値を返す。
845: },
846:

jQuery() の挙動を解読する(28) pushStack()インスタンスメソッド再 学習 upon ver1.3.2──jQuery解読(42)

以前書いたエントリイを全面的に刷新し、ver1.3.2対応としましたので、よろしければご覧下さい。

jQuery()の挙動を解読する。(12) pushStack()解読 upon ver1.3.2──jQuery解読(21)

jQuery() の挙動を解読する(27) 事例による jQuery.extend() 学習 upon ver1.3.2──jQuery解読(41)

jQuery.extend() メソッド ──オブジェクトの拡張

このメソッドは jquery.js に頻繁に登場するので、屋上屋を重ねて事例を綴る必要は皆無ですが、敢えてjquery.js に登場しない表現を使って、へそ曲がりな extend メソッドの使い方を試してみました。これは jQuery.extend メソッドの定義コードを検証してみたいからです。

Test 1 ── jQuery.extend ( obj1, obj2, obj3 )

異なるプロパティを持つ 3 つのオブジェクトの統合です。obj1 に obj2 と obj3 が統合されます。

 var obj1 ={a:1}, obj2 ={b:2}, obj3 ={c:3};
$.extend( obj1, obj2, obj3 )  // obj1=={a : 1, b : 2, c : 3}

なお jquery.js において jQuery 自身の拡張ではないオブジェクト拡張は、ajaxSetup メソッドで使われています。#3362 jQuery.etend( jQuery.ajaxSetting, settings ) これ以外は未だ見つけていません。

Test 2 ── jQuery.extend ( obj1, obj2, obj3 )

一部重複するプロパティを持つ 3 つのオブジェクトの統合です。

重複するプロパティは後でコピーされるプロパティ値で上書きされます。

 var obj1 ={a:1}, obj2 ={a:2,b:2}, obj2 ={b:3,c:4,d:5};
$.extend( obj1, obj2, obj3 )  // obj1=={a : 2, b : 3, c : 4, d : 5}
Test 3 ── jQuery.extend ( true, jQuery ,{ temp: function(a,b){return a+b} } )

extend 定義コードによれば、このメソッドを実行すると 第 2 引数である jQuery オブジェクトを拡張し、jQuery.temp = function(a,b){return a+b} となるはずです。

そして確かにそうなりました。敢えて jQuery を引数にしても extend メソッドは成功裏に作動したわけです。

但し、第 2 引数を this にしたところ、window オブジェクトに新たなオブジェクトが作られてしまいました。

Test 4 ──jQuery.extend ( false, jQuery ,{temp: function(a,b){return a+b}} )

今度は第 1 引数を false としてみました。extend 定義コードによればこの結果も Test3 同様に 第 2 引数である jQuery オブジェクトを拡張して、jQuery.temp = function(a,b){return a+b} となるはずです。

ところが、駄目でした。#567 の typeof target === "boolean" が コードを見る限り true となるはずなのに、そうならないのです。

第 1 引数を false とすると #564 で target = false となるから、#567 は typeof false === "boolean" となり、これは true となるはずなのに、false となってしまうのです。この結果 #568~#571 のブロックがスルーされてしまうのです。

どうしてこうなってしまうのか原因を解明できず、この Test は後味の悪い未完のものとなってしまいました。

jQueryに学ぶ Javascript の基礎(7) 組み込みクラス Array の各種メソッドに apply() メソッドを適用する──jQuery解読(40)

組み込みクラス Array の全てのメソッドに apply() メソッドは適用出来るか?

直前のエントリイ において、Array.prototype.push() メソッドへの apply() メソッドの適用について考えてみました。また、その中で関連して使用した jQuey().get() メソッドについては、こちら ( jQuery()の挙動を解読する。(11) 簡単なインスタンスメソッドいくつか──jQuery解読(19) ) で解読しました。

これらのエントリイで学習したことは、組み込みクラス Array の push() メソッドと slice() メソッドに、apply() 又は call() メソッドを適用することでした。言い換えれば、jQuery インスタンスオブジェクトに対して、組み込みクラス Array の push() メソッドあるいは slice() メソッド を apply() または、call() することでした。

さて、組み込みクラス Array の2つのメソッドに appy() / call() メソッドが適用できるということは、Array の他のメソッドに対しても apply() / call() メソッドが適用出来るのではないか、と期待しても不思議ではありません。

そこで早速それを試してみたところ、確かにそれが可能であることが分かりましたので、以下にその検証過程を詳らかにします。

▲ToTop

Array クラスのメソッドリスト

Array クラスのメソッドにはよく知られているように次のようなものがあります。

join()、reverce()、sort()、concat()、slice()、splice()、push()、pop()、unshift()、shift()、toString()、toLocalString()

この他に Firefox1.5 以降に実装されている Javascript1.6 以降で使用可能な indexOf()、lastIndexOf()、forEach()、map()、filter() などがありますが、これらは標準にはなっていないので、ここでは検証対象外とします。

まず、オブジェクトのプロパティにとってその並び順に意味はありませんから、並び順に作用する Array クラスのメソッド ( reverce、sort ) に対する apply() メソッドの適用は、検証から除外します。

残ったメソッドは 10 種類になりますが、検証はメソッドの作用内容から次のグループに分けて行います。

  1. push()、pop()、unshift()、shift()
  2. slice()、concat()、splice()
  3. join()、toString()、toLocalString()

検証の方法

jquery.1.3.2 をインクルードした Local HTML ファイルを FireFox3.x で開き、 Firebug を走らせながら、そのコンソールウィンドウに任意のコードを入力し、結果表示を観察して検証を行いました。また、検証に際しては以下のプロパティ一覧ユーティリティを使用しました。

 // 検証に利用したオブジェクトのプロパティ名とプロパティ値を一覧するための関数
 var getProp = function(obj){
    var ret = [];
    for (var prop in obj) ret.push(prop + " : " +obj[prop]);
     return ret.join();
 };

▲ToTop

push()、unshift()、pop()、shift()

jQuery().setArray() インスタンスメソッドにおいて push() メソッドが利用されていたことから推測できることは、これらのメソッドに apply() / call() メソッドを適用することは、オブジェクトに対して array like な配列を追加又は削除する働きを持つだろう、ということです。

以下にそれらの検証過程と結果を記します。

unshift()
 /*++++++ unshift() +++++*/
 // unshift 関数に apply(obj,[array])メソッドを適用する
 Array.prototype.unshift.apply ( obj,[ "test",100,["test",500] ] );
 // 適用結果を一覧する
 getProp(obj) // 結果 "a : 1,2,3, b : function (x, y) { return x + y; },
  c : object, d : [object Object], 0 : test, 1 : 100, 2 : test,500, length : 3"

以上の結果を見るとこれは push() メソッドの場合の
   Array.prototype.push.apply ( obj, [ "test", 100, ["test",500] ] );
と同一の結果になることが分かります。

つまり、2 つの異なるメソッドの効果が同一となるのは、配列と異なってオブジェクトの場合には、プロパティの並び順に意味はありませんから、最後に要素を追加する push() であれ、最初に要素を追加する unshift() であれ、作用効果が同様になると考えられます。

▲ToTop

次に、配列から要素を削除する pop()、shift() メソッドを調べてみます
 /*++++++ pop().apply() or shift().apply() の検証 +++++*/
 // obj 再定義
 var obj = {
  "a":[1,2,3],
  "b":function(x,y){return x+y},
  "c":"object",
  "d":{x:789,y:321}
 };
 // pop 又は shift メソッドに apply(obj) 又は call(obj) メソッドを適用する
 Array.prototype.pop.apply ( obj ); /*又は*/ Array.prototype.shift.call ( obj );
  /*あるいは*/
 Array.prototype.pop.call ( obj , "" ); /*又は*/ Array.prototype.shift.apply ( obj , [] );
 // 適用結果を一覧する
 getProp(obj) // 結果 "a : 1,2,3, b : function (x, y) { return x + y; },
 // c : object, d : [object Object], length : 0"

以上から分かることはこの操作では何も変化しないということです。

ところが objに「配列のような」プロパティが存在する場合に pop().apply(obj) やshift().call(obj)を適用すると、事態が変わります。

 /*++++++ array like なプロパティがある obj に対して、
 pop 又は shift メソッドを apply(obj) 又は call(obj) する +++++*/
 // obj 再定義 ( array like プロパティを含ませる )
 var obj = {
  "a":[1,2,3],
  "b":function(x,y){return x+y},
  "c":"object",
  "d":{x:789,y:321},
  0:"test",
  1:100,
  2: ["test",500] 
 };
 // pop() 又は shift() メソッドに apply(obj) か call(obj)メソッドを適用する
 Array.prototype.pop.call ( obj ); /*又は*/ Array.prototype.shift.apply ( obj ); /*など*/。
 // 適用結果を一覧する
 getProp(obj) // 結果 "a : 1,2,3, b : function (x, y) { return x + y; },
  c : object, d : [object Object], 0 : test, 1 : 100, length : 2"
  // array like なプロパティの、最後のそれ( 2 : test,500 )が削除されたことが分かる。

 // 上記の後に再度 pop() 又は shift() メソッドに apply(obj) メソッドを適用する
 Array.prototype.pop.apply ( obj ); 又は Array.prototype.shift.apply ( obj );
 // 適用結果を一覧する
 getProp(obj) // 結果 "a : 1,2,3, b : function (x, y) { return x + y; },
 // c : object, d : [object Object], 0 : test, length : 1"
  //  array like なプロパティの、最後のそれ( 1 : 100 )が削除されたことが分かる。

以上をまとめると、次のようになります。

  1. obj オブジェクトに array like なプロパティがある場合には、 Array.prototype.pop.apply ( obj ) 又は Array.prototype.shift.apply ( obj ) を実行すると、当該の array like なプロパティの 1 つを削除する。その削除対象は pop でも shift でも同様に、最大整数のプロパティ名を持つプロパティとなる。
  2. 一方、obj オブジェクトに array like なプロパティが存在しない場合には、 Array.prototype.pop.apply ( obj ) 又は Array.prototype.shift.apply ( obj ) を履行しても obj に何の変化も生じない。

▲ToTop

slice()、concat()、splice()

jQuery().get() では Array.prototype.slice.call( this ) によって this に格納されている DOM Elements を取り出しています。

これを踏まえて、上と同様にカスタムオブジェクトに対して Array.prototype.concat.call( ) 及び Array.prototype.splice.call( ) を適用した場合に、意味のある結果が得られるのかどうかを検証してみます。

Array.prototype.slice.call( obj )

最初に jQuery().get() で使用されている slice() メソッドについて改めて記しておきます。

 // obj 定義
 var obj = {
  "a":[1,2,3],
  "b":function(x,y){return x+y},
  "c":"object",
  "d":{x:789,y:321},
  0:"test",
  1:100,
  2:["test",500],
  length:3
 };

 // slice.call(obj) :結果はプロパティ値を要素とする 配列になるので、
 // toString() メソッドで要素を取り出すことにする。
 Array.prototype.slice.call(obj).toString(); // 結果 "test,100,test,500"

3 つの array like なプロパティ値だけをオブジェクトから取り出せることが分かります。

jQuery().get() の場合には jQuery() オブジェクトの array like なプロパティ値は DOM Elements ですから、 jQuery().get() によって当該 DOM Elements が「取り出せる」ことになります。(詳細は こちらに まとめてあります。)

Array.prototype.concat.apply( obj , [array] )
 // obj 定義
 var obj = {
  "a":[1,2,3],
  "b":function(x,y){return x+y},
  "c":"object",
  "d":{x:789,y:321}
 };

 // Array.prototype.concat.apply( obj , [array] ) はオブジェクトを
 // 返すので、concat.apply() メソッドの return 値を ret に代入する。
 var ret = Array.prototype.concat.apply( obj, [ "test",100,["examine",500],{} ] )
 // ret オブジェクトを一覧する
 getProp(ret) // 結果 "0 : [object Object], 1 : test, 2 : 100, 3 : examine, 4 : 500, 5 :  [object Object]"

結果を見ると、生成されたret オブジェクトは obj オブジェクトを最初のプロパティ値とし、続けて apply() メソッド第 2 引数配列の各要素を順に(配列がある場合にはその要素に分解して)プロパティ値とする、全体として array likeなプロパティによって構成されることが分かります。

つまり、concat() メソッドに apply( obj,[array] ) メソッドを適用すると、元 obj オブジェクトと第 2 引数の [array] 配列オブジェクトを、その配列内に配列がある場合にはそれを全て要素に分解してから、全体として array like な配列をプロパティとする、新たなオブジェクトが再構築されるわけです。

なお jquery.js(ver 1.3.2) 内に concat は 4 カ所ありますが、それらがここで触れた方法と目的で使われているのかどうかについては、まだ検証していません。

Array.prototype.splice.apply( obj , [array] )
 // obj 定義
 var obj = {
  "a":[1,2,3],
  "b":function(x,y){return x+y},
  "c":"object",
  "d":{x:789,y:321},
  0:"test",
  1:100,
  2:["test",500],
  length:3
 };
 /*---------(1) 単純に新規プロパティを追加するケース-------------------------*/
 // splice.apply(obj,[array])
 Array.prototype.splice.apply( obj, [ obj.length,0,1000,["today","yesterday"],{} ] )
 // 適用結果を一覧する
 getProp(obj) // 結果 "a : 1,2,3, b : function (x, y) { return x + y; },
 // c : object, d : [object Object], 0 : test, 1 : 100, 2 : test,500, 
 // length : 6, 3 : 1000, 4 : today,yesterday, 5 : [object Object]"

 /*-- (2) 一部の既存プロパティを削除し、かつ新規プロパティを追加するケース --*/
 // splice.apply(obj,[array])
 Array.prototype.splice.apply( obj, [ 0,2,1000,["today","yesterday"],{} ] )
 // 適用結果を一覧する
  getProp(obj)  // 結果 "a : 1,2,3, b : function (x, y) { return x + y; },
  // c : object, d : [object Object], 0 : 1000, 1 : today,yesterday,
  // 2 : [object Object], 3 : test,500, length : 4"

splice() メソッドは配列に要素を追加/削除するための、最も汎用的な関数です。上の例により、これをオブジェクトに apply することによって、array like なプロパティを必要なだけ追加出来ることが分かります。

例(1)では、第 2 引数である配列の第 1 要素の obj.length により開始番号を設定し、第 2 要素の 0 により何も削除しないことを指示し、1000 以降の 3 つの配列要素を obj オブジェクトに追加しています。

例(2)では、開始番号を 0 、削除数を 2 と指定することにより、既存の 2 つのプロパティ {0 : test, 1 : 100}を削除し、第 2 引数である配列の、第 3 要素から第 5 要素までの 3 つの要素を、新たなプロパティとして obj オブジェクトに追加し{ 0 : 1000, 1 : today,yesterday, 2 : [object Object] }、残存している既存のプロパティ値 "test,500" の要素番号をずらして {3 : test,500} とし、最後に 2 つの要素を削除し、3 つの要素を追加した結果として length 値を 4 としています。

つまり、splice() メソッドを obj オブジェクトに apply すると、obj オブジェクトに対して pop や unshift よりも自在に( 位置を指定してプロパティを部分的に削除することも可能 )、array like なプロパティを付加することが出来ることが分かります。

因みに jquery.js(ver 1.3.2) 内に splice は 3 カ所ありますが、それらがここで触れた方法と目的で使われているのかどうかについては、まだ検証していません。

▲ToTop

join()、toString()、toLocalString()

toString() 及び toLocalString() については、call(obj) を適用して意味のあるreturnは得られませんでしたが、join() については以下のように意味のある結果を得ることが出来ました。

Array.prototype.join.call( obj )
 // obj 定義は同上とする

 // join.call(obj, arg)
 Array.prototype.join.call( obj, " : " )
  // 結果 "test : 100 : test,500"
 // [ "test", 100, ["test",500] ].join(" : ") と同一の結果となっている。

上を見ると join.call(obj, arg) は obj オブジェクトの中の その他プロパティを無視しつつ、array like なプロパティ値だけを文字列として取りだし、arg 文字(列)で接続する機能を持っていることが分かります。

但し、そのような機能を必要とする状況にはどんなものがあるのか、想像すら出来ません。つまりこのメソッドは余り使い道がないような気がします。

jQueryに学ぶ Javascript の基礎(6) func.apply(obj , array) upon Ver1.3.2──jQuery解読(39)

このエントリイは、 jQueryに学ぶJavascriptの基礎(5) 論理演算子の使い方──jQuery解読(23) の続編です。(5)を書いたのが 2007/12/16 ですから、1 年以上間が空いてしまいました。その点からすれば「続編」と言うよりも「再開編」と言うべきかもしれません。

配列のメソッドである push() に apply() メソッドを適用する

apply() メソッドは Javascript 初心者には取っつきにくい関数です。それが別の関数を対象に適用され、しかも this キーワードが登場し、更に引数を配列で与えなければならない───このように約束事の多い仕様となっているためでしょう。

一般的に遭遇する fn.apply ( this, [ array ] ) という書式は、最初はなかなか理解しがたいものです。これは this.fn ( [array] ) とほぼ同意義なのだと説明されると、その場では分かったような気になりますが、半可通のママでは実際に使いこなすことは出来ません。

私もこの"主客転倒しているかのような" apply() メソッドには、ずっと悩まされ続けてきました。

そもそも apply()は「関数オブジェクトの組み込みメソッドであり、かつ特定の引数を取る」ことを理解すれば、決して複雑怪奇な関数ではないのですが、そのような理解に至るには時間が掛かるのではないでしょうか。(自省の弁)

さて、jQuery ではこのメソッドがあちこちで多用されています。

特に init() メソッドの return 値算出に setArray() メソッドが重要な役割を果たしていますが、この setArray() メソッドの実態は、組み込みクラスのプロトタイプメソッドである push() に適用された apply() メソッドに他なりません。

▲ToTop

対象オブジェクト obj に、配列 「 のような 」 プロパティを付与する

jQuery では push メソッドに apply メソッドを適用することによって、インスタンスオブジェクトに 「 配列のような (array like) 」 プロパティを与えています。そしてその array like なプロパティ値こそ、jQuery ( selector ) によって抽出されたところの、ユーザーが取得しようとしている DOM Elements に他なりません。

以下には今回の学習記録として、具体的な例によって apply() メソッドの効用を明らかにします。

まず、視認性を高めるために、オブジェクトのプロパティを抽出する以下のような汎用ユーティリティを作っておきます。

 // オブジェクトのプロパティ名とプロパティ値を一覧するための関数
 var getProp =function(obj){
    var ret=[];
    for (var prop in obj) ret.push(prop + " : " +obj[prop]);
     return ret.join() ;
 };
一般的な抽象オブジェクトを対象にした例

下の例 1 は、空のオブジェクト obj に push.apply(obj,[array]) を適用した場合です。適用後の obj は 名称が 0 から始まる連番プロパティと length プロパティによって構成され、length 値は第 2 引数の配列数となります。確かに配列のような プロパティが obj オブジェクトに付与されることが分かります。

 /*++++++ apply() 例 1 ++++++*/
 // 空オブジェクトを作成
 var obj={};
 // push 関数に apply(obj,[array])メソッドを適用する
 Array.prototype.push.apply(obj,["test",100,["test",500],400] );
 // 適用結果を一覧する
 getProp(obj) // 結果 "0 : test, 1 : 100, 2 : test,500, 3 : 400, length : 4"

例 2 は、 obj のプロパティ名に、push.apply(obj,[array]) で付与される array like なプロパティ名と重複する名称が既に存在する場合を検証してみました。この場合には obj プロパティが上書きされ、obj.length 値 は objプロパティの総数ではなく、push.apply() の第 2 引数である配列の要素数になることが分かります。

 /*++++++ apply() 例 2 ++++++*/
 // 空ではないオブジェクトを作成
 var obj={0:"test",1:"test1",2:"test2",3:"test3",4:"test4",5:"test5"};
 // このオブジェクトに push.apply(obj,[array]) メソッドを適用する
 Array.prototype.push.apply(obj,[100,200,300] );
 // 適用結果を一覧する
 getProp(obj) // 結果 "0 : 100, 1 : 200, 2 : 300, 3 : test3, 4 : test4, 5 : test5, length : 3"

例 3 は、obj のプロパティ名に 0 から始まる連番名がない場合の検証です。この場合には obj の既存プロパティは変化せず、第 2 引数 [array] の要素が array like なプロパティに姿を変えて追加されます。しかし、obj.length 値は obj プロパティの総数ではなく、あくまでも push.apply() の第 2 引数の配列の要素数となることが分かります。

 /*++++++ apply() 例 3 ++++++*/
 // 空ではないオブジェクトを作成
 var obj={"a":"test","b":"test1","c":"test2","d":"test3","e":"test4","f":"test5"};
 // このオブジェクトに push.apply(obj,[array]) メソッドを適用する
 Array.prototype.push.apply(obj,[100,200,300] );
 // 適用結果を一覧する
 getProp(obj) // 結果 "a : test, b : test1, c : test2, d : test3, e : test4, f : test5, 0 : 100, 1 : 200, 2 : 300, length : 3"

例 4 は、push.apply(obj,[array]) メソッド適用前に、obj.length を指定する意味について考えるために作成したものです。結論を言えば、array like なプロパティ名は obj.lenght で指定した値から始まる連番になります。しかしここでも、obj.length 値は obj プロパティの総数ではなく、あくまでも push.apply() の第 2 引数の配列の要素数となることが分かります。

 /*++++++ apply() 例 4 ++++++*/
 // 空ではないオブジェクトを作成
 var obj={"a":"test","b":"test1","c":"test2","d":"test3","e":"test4","f":"test5"};
 // 適用前にobj.length値を指定
 obj.length = 15;
 // obj オブジェクトに push.apply(obj,[array]) メソッドを適用する
 Array.prototype.push.apply(obj,[100,200,300] );
 // 適用結果を一覧する
 getProp(obj) // 結果 "a : test, b : test1, c : test2, d : test3, e : test4, f : test5, 15 : 100, 16 : 200, 17 : 300, length : 3"

以上から、Array.prototype.push(obj,[array])メソッドについて次のことが分かりました。

  • 同一プロパティ名は上書きされる。
  • obj に付加される array like なプロパティの名称は、前もって obj.length 値を指定した場合にはその値から始まる連番となる。(指定しない場合には obj.length = 0 を指定したことになる。)
  • obj.length 値はどのような場合であっても、obj の総プロパティ数には無関係に、第 2 引数で指定した配列の要素数となる。

▲ToTop

jQuery における push - apply 適用例(行数は Ver 1.3.2 による)

兎にも角にも setArray インスタンスメソッドにその端的な例を見ることが出来ます。

139: // Force the current matched set of elements to become
140: // the specified array of elements (destroying the stack in the process)
141: // You should use pushStack() in order to do this, but maintain the stack
142: setArray: function( elems ) {
143:  // Resetting the length to 0, then using the native Array push
144:  // is a super-fast way to populate an object with array-like properties
145:  this.length = 0;
146:  Array.prototype.push.apply( this, elems );
147:
148:  return this;
149: },

■コメント文を意訳すれば...
(ユーザー指定条件に)マッチした要素群を特別な配列に変換する。(スタックはその
過程で破棄されるので、スタックを活用するためには pushStack() メソッドを使わねば
ならない。 length を 0 に設定すれば、push メソッドを使って、超高速にオブジェクトを
配列のようなプロパティに変換することが出来る。

146 行の apply メソッド適用により、this すなわちインスタンスオブジェクトのプロパティに elems が配列のようなプロパティとして組み込まれることになります。

setArray() テスト

以上をより具体的に理解するために、Local 環境下に、<p> が 10 個存在する Web サイトを作成し、その中で jQuery ("p") .setArray ( ["a","b","c","d","e"] ) メソッドの適用結果を色々な形で求めてみました。

以下の出力結果は Firebug のコンソールに表示された内容です。
入力 : $("p").setArray(["a","b","c","d","e"]).get()
出力 : a,b,c,d,e
入力 : getProp( $("p").setArray( ["a","b","c","d","e"] ).get() )
出力 : "0 : a, 1 : b, 2 : c, 3 : d, 4 : e"
入力 : getProp( $("p").setArray(["a","b","c","d","e"]) )
出力 :
   length : 5,
   prevObject : [object Object],
   context : [object HTMLDocument], 
   selector : p, 
   0 : a, 
   1 : b, 
   2 : c, 
   3 : d, 
   4 : e, 
   5 : [object HTMLParagraphElement], 
   6 : [object HTMLParagraphElement], 
   7 : [object HTMLParagraphElement], 
   8 : [object HTMLParagraphElement], 
   9 : [object HTMLParagraphElement], 
   // 以下はプロトタイプオブジェクトを参照するプロパティ
   init : function (selector, context) { ・・・・(以下略)
参考
入力 : getProp( $("p") )
出力 :
   length :10,
   prevObject : [object Object],
   context : [object HTMLDocument], 
   selector : p, 
   0 : [object HTMLParagraphElement], 
   1 : [object HTMLParagraphElement], 
   2 : [object HTMLParagraphElement], 
   3 : [object HTMLParagraphElement], 
   4 : [object HTMLParagraphElement], 
   5 : [object HTMLParagraphElement], 
   6 : [object HTMLParagraphElement], 
   7 : [object HTMLParagraphElement], 
   8 : [object HTMLParagraphElement], 
   9 : [object HTMLParagraphElement], 
   // 以下はプロトタイプオブジェクトから継承されるプロパティ
   init : function (selector, context) { ・・・・(以下略)

此処で分かることは、以下の諸点です。

  • setArray(array) メソッドは、$("p") インスタンスオブジェクトのプロパティを、プロパティ名 0 から順に、引数とした array 配列の各要素で置換しています。
    実際の setArray メソッドの引数は elems( elems はその要素を DOM Elements とする配列 ) ですから、DOM Elements が順番にオブジェクトのプロパティに追加されることが分かります。
  • 因みに対象オブジェクトのプロパティ名 n よりも多い要素数 m を有する配列を引数にして、同じメソッドを履行してみたところ、0 から n-1 迄のプロパティが置換され、n から m-1 迄のプロパティが追加生成されました。
  • setArray() メソッド適用によりそのプロパティが一部上書きされてしまったところの、当該メソッド適用前の $("p") オブジェクトは、別途定義されている prevObject() メソッドによって、prevObject : [object Object] に格納されています。
  • 序でに触れておけば、get() メソッドはその実行対象となった jQuery インスタンスオブジェクトのプロパティの内、プロトタイプオブジェクトを参照しないプロパティの中で、その名前が 0 から始まる連番となっているプロパティ値だけを抽出する役割を担っています。
    なお、このメソッドではプロパティ名は抽出されません。

▲ToTop

jQueryの挙動を解析する(26) jQuery 定義コードの再解析 on Ver1.3.2──jQuery解読(38)

このエントリイは、jQuery() の挙動を解読する(24) jQuery 1.2.2 における jQuery 定義コードを分析する──jQuery解読(36) の続編です。続編と言うよりも改訂編と言うべきかもしれません。上記エントリイには未解明な問題もあり、改めてこのエントリイで詳述することにしました。

jQuery定義と酷似する構造の簡易なコードを作成して解析する

jQuery の定義コードへの理解を深めるために、それに類似した短いコードを作成して解析してみました。

 jQuery 定義コードを解読するための簡単な類似コード
 1: (function(){
 2:     // block-A
 3:     var R  = window.R = function(a,b){
 4:         return new R.prototype.init(a,b);
 5:     } 
 6:     // block-B
 7:     R.prototype = {
 8:         init:function(){
 9:             this.x = arguments[0];
10:             this.y = arguments[1];
11:             this.z = this.x + this.y;
12:             return this;
13:         },
14:         area:function(){
15:             return this.x * this.y;
16:         },
17:         periphery:function(e,f){
18:             return 2* (e + f);
19:         }
20:     }
21:     // block-C
22:     R.prototype.init.prototype = R.prototype;
23: })();
上のコードの簡単な説明
  • jQuery オブジェクトに相当するそれとして R を定義し、( 2~5 行)
  • jQuery 定義コードと同様に そのプロトタイプオブジェクトの init メソッド(つまり R.prototype.init(a,b) )をコンストラクタとします。init メソッドの引数は本質的にどうでも良いことですが、数を変えるのも面倒なので jQuery 同様に2つ用意しました。( 2~5 行 )
  • R.prototype はそのプロパティに init() の他に、2つの関数オブジェクトを定義し、併せて3つのプロパティ(全て関数)を持つようにしました。( 6~20 行)
  • jQuery のコードに準じて init.prototype が R.prototype を参照するように設定しました。( 21~23 行)
  • jQuery 同様に全体を無名関数で包含し、かつ即実行するようにしました。( 1 と 23行 )
  • ここに R() はコンストラクタに関する Javascript の仕様から R.prototype.init.prototype のプロパティを継承します。
  • そして init.prototype は R.prototype を参照するよう定義付けられているので、結局 R() は R.prototype オブジェクトのプロパティを継承することになります。

こうして R() は次のようなプロパティを持つオブジェクトとなります。

要点は init コンストラクタ関数によって設定されたプロパティ(x,y,z)と共に、R.prototype のプロパティ(init、area 及び periphery の3つのメソッド)を継承している点です。

R() = {
 // x,y,z は init コンストラクタによって設定された。
 // x プロパティは 9 行の this.x で定義されているが、
 // R() に引数を付けなかったので値は未定義となっている。
 x : undefined,
 y : undefined, // 10行で定義されたが同上から未定義となっている。
 z : NaN, // 11行の this.z で定義されたが x,y 値が未定義なので NaN となる。

 // 以下は 22 行の R.prototype.init.prototype = R.prototype によって設定され
 // たもの。init コンストラクタのインスタンスである R() は Javascript の仕様から
 // init.prototype のプロパティを継承することになり、この init.prototype が 
 // R.prototype を参照するため、結局 R() は R.prototype オブジェクトのプロパティ
 // をそっくり継承することになる。
 init : function (x, y) { // init メソッドは insObj を初期化する働きをする
  // this は コンストラクタの仕様から insObj を参照する。
  this.x = x; this.y = y; this.z = this.x + this.y; return this;
 },
 // 14~16行で追加されている area プロパティ
 area : function () { return this.x * this.y; },
 // 17~19行で追加されているプロパティ
 periphery : function (c, d) { return 2 * (c + d); }
}
さて、以上のコード進行過程を解析してみます。
  1. 無名関数が実行されると R.prototype プロパティ共々関数が定義されます。無名関数が行うことはそれだけです。
  2. その後ユーザーにより R() が実行されると、説明の都合上当該インスタンスを insObj と名付ければ、(1) インスタンスオブジェクトが生成され、(2) this がこれを参照し、(3) R.prototype.init.apply( insObj, [a,b] ) が実行されます。
  3. そして最後に、3~4 行のコードによりこのインスタンスが呼び出し元に返されます。つまり、
       R(a,b) = this = insObj
    となります。

▲ToTop

上のコードを使ったインスタンスの例

以上で jQuery 定義コードの根幹部分を解読できたと思いますが、以下の幾つかの例示によって上記コードへの理解を深める一助にします。

なお、以下の getProp(obj) 関数によりオブジェクトのプロパティを取得します。

    var getProp = function(obj){
        var ret = [];
        for (var prop in obj) ret.push(prop + " : " +obj[prop]);
        return ret.join("\n") ;
    };
getProp(R(2,3))
/*  x : 2  y : 3  z : 5  init : function (x, y) {   this.x = x; this.y = y; this.z = this.x + this.y;  }  area : function () { return this.x * this.y; }  periphery : function (c, d) { return 2 * (c + d); } */
R("selector","content"); // {x:selector,y:content,z:selectorcontent}
R(2,3).init() // NaN
R(2,3).init(4,5) // {x:4, y:5, z=9}
R(2,3).area() // 6
R(2,3).area(4,5) // 6
R().area(2,3) // NaN
R(2,3).periphery(5,6); // 22
R().periphery(5,6); // 22

インクルードされる jQuery.js の読み込み過程で jQuery はどのようにプロパティを獲得するか

構造が jQuery に類似した上記コードの解析により、jQuery の定義コードの根幹部分は理解できましたが、そもそも知りたいことは、jQuery.js がブラウザに読み込まれる過程で、jQuery オブジェクトはどのように構成されるのか、という点なのです。

上記の類似コードの解析により、jQuery.prototype オブジェクトの全てのプロパティが、jQuery(selector,content) インスタンスのプロパティになることが分かりました。

ここに、jQuery.prototype オブジェクト( jQuery.fn オブジェクトに同じ ) は 35 行から 530 行(基本部分)、554~602 行( extend )、1351~1397 行( extend )、2778~2871 行( Event 関係の extend )、3105~3190 行( Ajax 関係の extend )、3662~3817 行( Animation 関係の extend )、4038~4084 行( offset )、4124~4162 行( position )と繰り返し登場します。

こうして、jQuery インスタンスには膨大なプロパティが登録されますが、この膨大なプロパティこそが、jQuery インスタンスプロパティとインスタンスメソッド及び jQuery クラスメソッドを駆使した、単一の記述「jQuery」による様々な window オブジェクトの操作を可能にしているわけです。

さて、これらの膨大な prototype オブジェクトからのプロパティの継承は、全て jQuery インスタンス生成時に行われます。他方、jQuery.extend(){ prop1:fn1(){}, prop2:fn2(){},・・・ } メソッドが jquery.js インクルード時に「実行」され、これにより jquery.js 読み込み完了時点で膨大な数のクラスメソッド( jQuery.prop1, jQuery.prop2, ・・・ )が jQuery オブジェクトに登録され、直ぐに活用できる状態になります。

そこで、extend() メソッドの解明をエントリイを改めて行うこととします。

My jQuery Plugin : animated Popup 改訂版

このエントリイの改訂履歴など
  • 初稿:2009/04/19
  • 完結:2009/04/23
  • 第 1 回改訂:2009/04/25 right、bottom 指定をやめ全て left 又は top に統一した。
  • 第 2 回改訂:2009/04/26 画像も表示できるように引数処理を修正した。またポップアップ表示ボックスの色味を自在に操れるように引数を1つ追加した。これらによりこのプラグインの使いやすさが向上した。
  • 第 3 回改訂:背景やボーダーの色が思うように変化しないバグを修正

残念ながら IE ではここで説明するプラグインは動きません。

その理由は IE8 のエラーメッセージに拠れば「 jquery.js のアニメーション関連コードに問題有り 」と表示されるのですが、Firefox、Opera 及び Windows 版 safari で問題なくこのページのアニメーションが表示されるので、エラーメッセージ自体のエラーではないかと思われます。

つまりIE8の標準モードにおけるバグではないかと推測しています。

IE でも動くよう改訂するつもりですが、上のような理由ですので対応策が見つかりません。

ここで言う Animated Popup とは

ポップアップ表示をアニメーションメソッドを使って行うことを意味しています。例えば極小の点から一定の幅と高さを有する表示ボックスをズームポップアップし、それを消すときにはズームアウトするような、そんなポッップアップです。

Windows Vista でウィンドウ開閉時に行われるような、あのようなズームイン/アウトをポップアップ表示/隠蔽にも取り入れてみたいと思い立ち 4月11日頃に作成を開始しました。

何はさておき、その実例を示すことが先決でしょう。

下のボタンをクリックするとあるいはその近傍に、あるいは画面中央に、あるいは画面右下に、あるいは指定してある位置 {left:100px,top:200px} に、ポップアップがズームイン/アウトされます。表示する際のポップアップサイズは順に300、350、400、500px に、またアニメーションに要する時間はそれぞれ 1200、600、1600、2000 ミリ秒に設定してあります。

 クリックした位置の傍にポップアップ表示します。

クリックすると画面の中央にポップアップ表示します。

クリックすると画面の右下にポップアップ表示します。

クリックすると指定してある位置にポップアップ表示します。

クリックすると画面の中央に或るサイトの或る写真をポップアップ表示します。写真出典元は フォトライブラリ です。

▲ToTop

Animation と Easing

Easing は Animation 動作速度を変化させる「加速度メソッド」のことで、元々 Flash ツールである Acrion Script の Tween クラスの加速度メソッドとして誕生したようです。(半可通の知識しか持ち合わせていないので「ようです」としておきます。)

この Easing 関数が jQuery プラグインとして提供されているので、早速それを導入し、このページでは敢えて動きの激しい easeOutElastic を使用しました。本来、文字を表示するには変化の激しい elastic は相応しくないでしょうが、Easing を初めて使用した記念として「激しい」ものを選択してみたのです。

因みに、George Smith 氏により提供されているプラグイン「Easing Plugin」は、10種類あり、各々に3つの効果(easeIn、easeOut、easeInOut)を持たせて Easing 関数が定義されているため、都合 30 種類の easing 関数があります。→ Plugins | jQuery Plugins

その Easing の各々の動きの違いを示したサイトとして興味深いのは、flash ムービーですが easing_demo が実に分かりやすく有益です。これによりそれぞれの Easing がどんな挙動をするのか一目瞭然に理解できます。easing 関数内の代数式がグラフ化されていることも非常に有益です。

そこで、このデモサイトをまねて、このページ上で実現しているアニメーションポップアップに対して 32 種類の Easing 効果を適用するリストボックスを設置してみました。easing_demo に比べて文字表示の場合には各 easing の差異がわかりにくいのですが、まあそれはご愛敬ということで...。

下のリストボックスの説明
  • アニーメーションポップアップの表示位置は 「 画面中央 」 としました。
  • Easing に要する時間はそれぞれの関数の差異が分かり易いように長めに設定しました(2秒)。
  • jquery.js に組み込まれている linear と swing の 2 つの Easing 関数もリストアップしたので都合 32 種類の easing 関数が登録されています。
  • 別の easing 関数を選択する前に表示されているポップアップを隠蔽する必要はありません。自動的に前の表示を消して、新たな表示を行います。
補足:上の リストボックスに係るスクリプトの説明

このページのソースコードを表示すれば分かることですが、jquery.js を使って初めて form を扱ったので、記録を残すために敢えてここに記します。

■アイテムが選択された際の処理を行うスクリプトコード
$("#sel665").change(function(){
 var index = this.selectedIndex,
   selEasing = this.options[index].value,
   txt="これは easing 関数: "+selEasing+" を使って<br />表示/隠蔽するアニメーションポップアップです。";
 $(this).animatedPopup(txt,["c","c"],2000,selEasing,{width:"400px"});
});
html説明
selectタグに id="sel665" と size=10を、最初の option タグに非選択属性 disabled="disabled" を、二番目の option タグに初期値 selected="selected" を設定しました。
Javascript 説明
  • 要点は selectタグの jQuery インスタンスに change イベントを登録することです。
  • まずリストボックスから選択されたアイテムのインデックス番号を変数に代入し、その値を使って options 配列から選択されたアイテムの value 値を取りだし、最後にポップアップ文を構成します。
  • 以上の僅か 3 つの変数を定義するだけで準備は完了。後は適切な引数付きで animatedPopup メソッドを登録するだけです。

以上の簡単なコードによって、リストボックス内の項目が選択されたその瞬間に発生する change イベントにより、イベントハンドラーが起動されて必要な処理を行います。

▲ToTop

作成した Animaeted Popup の仕様

  • 作成した Animaeted Popup は jquery.js のプラグインとして組み込める形式としました。
  • Animaeted Popup の呼び出しは次の書式で行います。jQuery(expr) はjQueryインスタンスならば何でも構いません。
    jQuery(expr).animatedPopup(contents, layout, duration, easing, options, options2);
  • <引数の説明>
     * contents: Strings in Animated Popup
     * layout: displaying position of Animated Popup
     * 	       text,ex. ["c","t"] (It means :left="center",top="top")
     *         or object,ex.{left:"100px",top:"200px"}
     *         or {left:"center",top:"100px"}
     * duration: durating time of animation(msec), ex.500 or "slow" etc.
     * easing: easing name (text), ex."easeInOutQuad","easeOutBounce",..32通りの指定が可能
     * 但し 32 種類を利用するには Easing Plugin を組み込む必要がある。
     * このサイトで利用している jquery.js には Easing Plugin を組み込み済みです。
     * options: Add CSS Object to Popup DIV, ex.{width:"400px",・・・}
     * options2: Add CSS Object to Popup DIV's CLOSE Bar, ex.{background:"midnightblue",・・・}
     * 全ての引数にはデフォルト値が設置済みなので複数の任意の引数が省略されても誤動作しません。
     * 何も引数がない場合、その旨を表示する animatedPopup を画面中央に表示します。

▲ToTop

作成した Animaeted Popup の Javascript コード

コードは長めになりました。説明を含めて 190 行あります。

ここでその全てを掲載しブロックごとに解説を加えることとします。

animatedPopup プラグインの全コード
  1:(function($){
  2:$.fn.extend({
  3:animatedPopup: function(contents,layout,duration,easing,options,options2){
  4:/*
  5: * <Example of Implementation>
  6: * jQuery("p").animatedPopup("And I love you so. Yesterday. Yellow Submarine.",
  7: *   ["c","b"],800,"swing",{width:"300px"})
  8: *
  9: * <arguments explanation >
 10: * contents: Strings in Animated Popup
 11: * layout: displaying position of Animated Popup
 12: *      text,ex. ["c","t"] (It means :left="center",top="top")
 13: *      or object,ex.{left:"100px",top:"200px"}
 14: *      or {"left":"center","top":"100px"}
 15: * duration: durating time of animation(msec), ex.500 or "slow" etc.
 16: * easing: easing name (text), ex."easeInOutQuad","easeOutBounce",...
 17: * options: Add CSS Object to Popup DIV, ex.{"width":"400px",・・・}
 18: * options2: Add CSS Object to Popup DIV's Title DIV, ex.{background:"midnightblue",・・・}
 19: *
 20: * <history>
 21: * released 2009/4/22 ver0.1
 22: * update 2009/4/25 ver0.2, 2009/4/26 ver0.3
 23: */
 24: var jQInst=$(this), winWH = {width:$(window).width(),height:$(window).height()},
 25:  errFlag,lcr,tcb,xName,yName,m=[],addValue={x:0,y:0},isImg,
 26:  mousePos={left:0,top:0},xCenter=$(window).width()/2,yCenter=$(window).height()/2;
 27:  // CSS 既定値は width 値が contents により変わるので、関数化しインスタンス毎に設定する。
 28: var defaultCSS =function(){
 29:  isImg = contents.match(/.*((<img.+src.+)|(<object.+)|(<embed.+)).*/i);
 30:  return {
 31:   "color":"white","font-weight":"bold","margin":0, "padding":"19px 5px 5px 5px",
 32:   /* 画像の場合ここで width を決めては駄目*/
 33:   "width": (isImg && isImg[1]) ? null : "300px",
 34:   "background-color":"royalblue", "border":"5px plum ridge", "text-align":"center",
 35:   "display":"block","visibility":"visible"
 36:  }
 37: }
 38: /* ポップアップに使用するCSS値を設定する*/
 39: var popupCSS = $.extend(true,defaultCSS(),options || {});
 40: /* closeBar CSS値も容易に変更できるように設定する */
 41: var closeBarCSS = function(){return $.extend(true,{
 42:  "position":"absolute","zIndex":"1001","text-align":"center",
 43:  "opacity":0.75,"top":0,"left":0,"width":"100%","cursor":"pointer",
 44:  "font-size":"small","lineHeight":"1.2em","background-color":"midnightblue"
 45: },options2 || {})};
 46:
 47: // bind onmousemove event on document
 48: mousePos.oX = 4; mousePos.oY = 16; //マウスカーソルからの離隔距離
 49: $(window).mousemove(function(e){
 50:  mousePos.left = (jQuery.browser.msie ? window.event.clientX - document.body.clientLeft : e.clientX) + mousePos.oX;
 51:  mousePos.top = (jQuery.browser.msie ? window.event.clientY - document.body.clientTop : e.clientY) + mousePos.oY;
 52: });
 53:
 54: // error 処理関数(後に拡張できるように関数にしておく)
 55: var errFunc = function(){ errFlag=true; return function(){return}}
 56:
 57: // animatedPopup の配置位置算出関数
 58: var getPos = function(layout){
 59:  if (layout && layout.constructor!= Object && layout.constructor!= Array ){
 60:   alert("配置は2つのテキスト:例 \"Center\",\"Top\" 、または\nオブジェクト形式:例 {left:\"10px\",top:\"100px\"} で指定してください。");
 61:   errFunc()();
 62:  }
 63:  var setPos = layout;
 64:  if (setPos.constructor==Array) {
 65:   var chk1 = /^((l.*)|(c.*)|(r.*))/.exec(setPos[0].toLowerCase());
 66:   var chk2 = /^((t.*)|(c.*)|(b.*))/.exec(setPos[1].toLowerCase());
 67:   if (chk1 && chk2){
 68:    lcr = chk1[2] && "left" || chk1[3] && "center" || chk1[4] && "right";
 69:    tcb = chk2[2] && "top" || chk2[3] && "center" || chk2[4] && "bottom";
 70:    xName = (lcr!=="right") ? "left" : "right";
 71:    yName = (tcb!=="bottom") ? "top" : "bottom";
 72:   } else {
 73:    if (!chk1 && !chk2) {
 74:     alert("左右上下 \""+ setPos[0] +" , "+ setPos[1] +"\" 共に指定が間違っています。\nやり直してください。");
 75:    } else if (!chk1 && chk2){
 76:     alert("左右の指定 \""+ setPos[0] +"\" が間違っています。\nやり直してください。");
 77:    } else if (chk1 && !chk2 ){
 78:     alert("上下の指定 \""+ setPos[1] +"\" が間違っています。\nやり直してください。");
 79:    }
 80:     errFunc()();
 81:   }
 82:  } else if (typeof setPos!=="string" && setPos.constructor==Object) {
 83:   for (name in setPos){
 84:    name=name.toLowerCase();
 85:    if (name=="left" || name=="right") {xName = name;lcr=name}
 86:    if (name=="top" || name=="bottom") {yName = name;tcb=name}
 87:   }
 88:   if (xName==null || yName==null){
 89:    alert("left、right、top、bottom 以外の\n指定は無効です。やり直してください。");
 90:    errFunc()();
 91:   }
 92:   // 位置指定値が "left" のように文字で為されたときの対応
 93:   if (!setPos[xName].match(/\d+/))
 94:    m[0] = setPos[xName].toLowerCase().match(/^(left|center|right)$/);
 95:   if (!setPos[yName].match(/\d+/))
 96:    m[1] = setPos[yName].toLowerCase().match(/^(top|center|bottom)$/);
 97:   if (!setPos[xName].match(/\d+/) && m[0]===null || !setPos[yName].match(/\d+/) && m[1]==null){
 98:    alert("配置指定値が間違っています。\nやり直してください。");
 99:    errFunc()();
100:   }
101:   addValue={
102:    x :( m[0] ? ((m[0]==="center") ? xCenter :0) : parseInt(setPos[xName])),
103:    y :( m[1] ? ((m[1]==="center") ? yCenter :0) : parseInt(setPos[yName]))
104:   };
105:  } else if(layout){
106:   alert("配置指定が無効です。やり直してください。"); errFunc()();
107:  }
108:  var obj={};
109:  obj[xName]= ((lcr==="center") ? xCenter : 0) + addValue.x;
110:  obj[yName]= ((tcb==="center") ? yCenter : 0) + addValue.y;
111:  return obj;
112: };
113:
114:$(function(){
115: // popup 表示用の div 要素タグの作成
116: if (!$("#dispElem").size()) {
117:  $("<div id='dispElem'></div>").css({
118:   position:"absolute",display:"none",zIndex:"1000"
119:  }).appendTo(document.body);
120: }
121: var disp=$("#dispElem");
122:
123: // Popup 隠蔽用×タグの作成
124: if (!$("#xMark").size()){
125:  $("<div id='xMark'>CLOSE</div>").append("<div style='width:13px;float:right;margin-top:-1em;'>×</div>").appendTo(disp);
126: }
127: var xMark = $("#xMark").css(closeBarCSS());
128:
129: // STEP1:*************** 表示前に popup エレメントの高さを測定する
130: var getElemWH= function(){ // 表示 popup サイズ算定
131:  // popup 表示前に横幅と文字数に応じた高さを計測する(非表示描画で測定)
132:  contents = contents || "ポップアップする内容が指定されていません。<br />やり直してください。";
133:  disp.html(contents).css($.extend(true,popupCSS,{"visibility":"hidden","height":null}));
134:  if(isImg && isImg[1]) disp.css("width",null);
135:  return {
136:   iW: popupCSS.width=(options && parseInt(options.width) || defaultCSS().width && parseInt(defaultCSS().width) || disp.width()),
137:   iH: popupCSS.height=disp.height(),
138:   oW: disp.outerWidth(),oH: disp.outerHeight()
139:  }
140: }
141:
142:  // 幅/高さが極小の要素 css 値を設定する。これにより拡張/縮小を演出する。
143:  // またここで初めてスクロールされていた場合の変動値を配置px値に追加する。
144: var shrinkCSS=function(){
145:  var scrLeft=$(window).scrollLeft(),scrTop=$(window).scrollTop(),
146:   obj={"width":"1px", "height":"1px", "border":"0px", "padding":"0px","margin":"0px"};
147:  if (!layout){
148:   obj.left=mousePos.left+scrLeft+"px";
149:   obj.top=mousePos.top+scrTop+"px";
150:  } else {
151:   var pos = getPos(layout);
152:   if (errFlag) return;
153:   obj.left=((xName=="left")-(xName=="right"))*pos[xName]+
154:    (xName=="right")*winWH.width + scrLeft+"px";
155:   obj.top=((yName=="top")-(yName=="bottom"))*pos[yName]+
156:    (yName=="bottom")*winWH.height + scrTop+"px";
157:  }
158:  return obj;
159: }
160:
161: var hideElem = function(e){ // popup 要素をアニメーション隠蔽する
162:  disp.empty().animate(shrinkCSS(),{queue:false,duration:duration,easing:easing});
163: }
164: var animaElem = function(e){
165:  $(":animated").queue('fx',[]).stop(); // 登録済みのアニメを全て削除停止する
166:  var doneShrink = shrinkCSS(); // この関数内で1回だけ起動する
167:  if (errFlag) return;
168:  // STEP2:***************
169:  var elemWH = getElemWH(); //エレメントサイズ計測実行
170:  if (!layout){ // 画面からはみ出さないように CSS 値を調整
171:   if (winWH.width < mousePos.left+elemWH.oW)
172:    popupCSS.left = winWH.width + $(window).scrollLeft() - elemWH.oW +"px";
173:   if (winWH.height < mousePos.top+elemWH.oH)
174:    popupCSS.top =  winWH.height + $(window).scrollTop() - elemWH.oH +"px";
175:  } else { // popup をセンター配置する場合の CSS 設定
176:   popupCSS.left = parseInt(doneShrink.left) -
177:    ((lcr==="center" || m[0] && m[0]==="center") ? Math.round(elemWH.oW/2) : (xName==="right") ? Math.round(elemWH.oW) :0) +"px";
178:   popupCSS.top = parseInt(doneShrink.top) -
179:    ((tcb==="center" || m[1] && m[1]==="center") ? Math.round(elemWH.oH/2) : (tcb==="bottom") ? Math.round(elemWH.oH) :0) +"px";
180:  }
181:
182:  // STEP3:*************** 極小要素を指定されたアニメーション起動位置に配置
183:  // dispElem の幅と高さを1pxにして所定位置に配置する(但し非表示描画)
184:  disp.empty().css(doneShrink);
185:  // STEP4:*************** 表示アニメーション
186:  disp.html(contents).append(xMark.css(closeBarCSS()))
187:   .css({visibility: "visible",display:"block"})
188:   .animate(popupCSS,{queue:false,duration:duration||"slow",easing:easing||"swing"});
189:  // STEP5:*************** × クリック時に隠蔽アニメーションを起動する。
190:  // ここで隠蔽関数を起動するようにしておかないと、複数回ボタンがクリックされた
191:  // 場合に隠蔽操作ができなくなる。理由は未解明。
192:  xMark.click(hideElem);
193: }
194: jQInst.click( errFlag ? function(){errFlag=false; $(this).unbind("click");} : animaElem );
195:}); // End of "DOMReady function"
196:} // End of "animatedAlert function"
197:}); // End of "Extend function"
198:})(jQuery);

▲ToTop

animatedPopup プラグインの解説

その意味や役割から幾つかのブロックに分けて解説します。

第Ⅰブロック(#1-22)

まず最初のブロックは、このメソッドの引数を定義しそれらを解説している部分です。

無名関数による起動と DOMReady 関数の利用(#1、#100)

このプラグインでは、一般的方法に倣って全体を無名関数で括り、即実行するようにしました。またポップアップ表示やポップアップ消去のための絶対配置 div 要素を追加し、かつそれらに様々なメソッドを適用する必要があることから、必要最小限の範囲を DOMReady function で括りました。

$.fn.extend クラスメソッドの利用(#2)

これにより、jQuery インスタンスのメソッドとして animatedPopup 関数を登録します。

animatedPopup 関数の引数(#3-18)

引数は6つあります。第一引数はポップアップボックス内に表示する文字列で、第2引数 layout は様々な方法でポップアップ位置を指定できるよう工夫し、第2引数を指定しない場合には、起動元 jQuery インスタンスが指し示す要素の近傍にポップアップするように設計しました。

第5及び第6引数も工夫しました。extend メソッドの2つめの使い方、つまり「 オブジェクト拡張 」を利用して、popup 要素のデフォルトCSS を設定しておくと共に、options で自在にそれを変更できるようにしました。幅や色、ボーダーの色と幅等々気分自由に変更することが出来るようにすることが目的であり、それを達成しました。

なお、この方法は jquery.js の Ajax ブロックで採用されていますのでそれを参考に考案しました。

▲ToTop

第Ⅱブロック(#24-45)

このブロックは、ローカル変数を定義している部分です。

■変数定義部分(再掲)
 24: var jQInst=$(this), winWH = {width:$(window).width(),height:$(window).height()},
 25:  errFlag,lcr,tcb,xName,yName,m=[],addValue={x:0,y:0},isImg,
 26:  mousePos={left:0,top:0},xCenter=$(window).width()/2,yCenter=$(window).height()/2;
 27:  // CSS 既定値は width 値が contents により変わるので、関数化した。
 28: var defaultCSS =function(){
 29:  isImg = contents.match(/.*((<img.+src.+)|(<object.+)|(<embed.+)|(<iframe.+)).*/i);
 30:  return {
 31:   "color":"white","font-weight":"bold","margin":0, "padding":"19px 5px 5px 5px",
 32:   /* 画像の場合ここで width を決めては駄目*/
 33:   "width": (isImg && isImg[1]) ? null : "300px",
 34:   "background-color":"royalblue", "border":"5px plum ridge", "text-align":"center",
 35:   "display":"block","visibility":"visible"
 36:  }
 37: }
 38: /* ポップアップに使用するCSS値を設定する*/
 39: var popupCSS = $.extend(true,defaultCSS(),options || {});
 40: /* closeBar CSS値も容易に変更できるように設定する */
 41: var closeBarCSS = function(){return $.extend(true,{
 42:  "position":"absolute","zIndex":"1001","text-align":"center",
 43:  "opacity":0.75,"top":0,"left":0,"width":"100%","cursor":"pointer",
 44:  "font-size":"small","lineHeight":"1.2em","background-color":"midnightblue"
 45: },options2 || {})};
jQuery インスタンスの取得(#24)

まず最初にこのプラグインの呼び出し元となる jQuery インスタンスを取得します。

DOMReady 関数内で jQuery インスタンスを呼び出そうとしたのですが、関数内では this は window オブジェクトを参照してしまうため、前もってここで取得しておくことにしました。

表示中の window の幅と高さの取得(#24)

これは jquery.js で定義されているクロスブラウザな便利なメソッドをそのまま活用しました。

これらの値を取得する意味は、画面の中央や左や右にピタリと寄せて配置する場合に利用するためです。

ポップアップ窓のデフォルト CSS 設定(#28-37)

ボーダーやパディング、そして色と背景色──これらの初期値を定めておかないとその都度思案し確定し指定しなければなりません。これはいかにも面倒なので既定値を設定しました。options で特に指定しなければ既定値が適用された popup 窓が表示されるわけです。

実は画像や動画にも対応させるために、最終段階でこのブロックを大きく改変しました。

#29 では正規表現を利用して img タグ、object タグ、embed タグ、あるいは iframe タグ文字列が第一引数 contents に含まれるかどうかをチェックします。そしてそれらのいずれかが含まれる場合には、デフォルト値としての画像サイズを設定しないこととしました。(#33)

これは引用先のCSS設定を反映する場合があったので、敢えて width 値を定めずにおいて、引用先サイトの padding 設定値なども反映した outerWidth 値を取得するためです。

実際に表示するポップアップ窓の CSS 値の指定(#38-39)

これには extend メソッドの2つめの機能である"ボブジェクト拡張"を利用しました。

optionsで任意の CSS 値を与えれば、デフォルトCSSを上書きしてユーザーの要望通りの表現を実現することが出来ます。また ||{} により options が指定されなかった場合にデフォルト値を利用するように工夫しました。

ポップアップ窓上辺の CLOSE バー CSS 値の指定(#40-45)

これも extend メソッドの"ボブジェクト拡張"を利用しました。

options2で任意の CSS 値を与えれば、デフォルト値を上書きしてユーザーの要望通りの表現を実現することが出来ます。また ||{} により options2 が指定されなかった場合にデフォルト値を利用するように工夫しました。

その他の変数(#25-27)

重要な役割を果たすローカル変数をここで定義しました。

addValue は配置位置がピクセル指定された場合のそのピクセル値を格納します。

mousePos はイベント発生要素の近傍にポップアップを配置する場合に必要となるマウスカーソルの現在値を所得するための変数です。

xCenter、yCenter は画面中央に配置するために必要な値で、それぞれ横方向の画面中央位置、縦方向の画面中央位置を取得します。

▲ToTop

第Ⅲブロック:マウス move イベントの登録とエラー対応(#4752)

第2引数 layout を指定しない場合に、マウスカーソル近傍にポップアップさせるためには、マウスの動きを常に Javascriptが 「監視」 しその位置を取得していなければなりません。そのためのイベント登録を行う部分です。これにより document の任意の箇所においてマウスカーソルの動きを常駐監視し、その位置を取得することになります。

 47: // bind onmousemove event on document
 48: mousePos.oX = 4; mousePos.oY = 16; //マウスカーソルからの離隔距離
 49: $(window).mousemove(function(e){
 50:  mousePos.left = (jQuery.browser.msie ? window.event.clientX - document.body.clientLeft : e.clientX) + mousePos.oX;
 51:  mousePos.top = (jQuery.browser.msie ? window.event.clientY - document.body.clientTop : e.clientY) + mousePos.oY;
 52: });
 53:
 54: // error 処理関数
 55: var errFunc = function(){ errFlag=true; return function(){return}}

ここでも IE が、IE だけが特殊な方法を採用しており、そのために面妖な設定をしなければならないのはユーザーにとって不幸なことです。早く IE のユーザー比率が低下することを願ってやみません。IE8 の登場によりその願いはまた叶うことなく先送りされてしまいましたが、中長期的には IE 固有の仕様は消え去る運命にあることは間違いないでしょう。

実際、後のエントリイで触れましたが、IE8 では少なくともスタイル設定に関しては Web 標準準拠に切り替わりました。これは 1 社だけで頑迷に固執し続けてきた奢りが、10年来のブラウザ戦争の中でやっと崩れ去ったことを意味しており、記念すべき歴史的事件と言って差し支えないでしょう。

#54 のエラー対応は「関数を返値とする」特殊な関数を利用します。これにより返値は return 値を有する関数となるので、errFunc()() のようにして、返値である関数を呼び出し先で実行することによりトップレベルにおいて return 値を返すことが可能となります。

ここに最初の括弧は errFunc 関数を実行するため、2つめの括弧は errFunc 関数の返値としての関数を実行するためです。#61 など随所で活用しています。

▲ToTop

第Ⅳブロック:ポップアップ配置位置の設定メソッド(#57-112)

このブロックはたった1つのメソッド登録ですが長大になりました。しかし、様々な指定方法に対応するために必要不可欠な部分であり、長くなってしまったのはエラー処理をふんだんに盛り込んだためでもあります。

#59-62 は最初のエラー処理です。

不適切な layout 指定があった場合の警告表示とコード進行停止を指定しています。

#64-82 は layout が配列だった場合の処理です。

#65-72 で、left、le あるいは l だけでも受容するように指定文字を簡略化できるように工夫し、所定の文字が与えられた場合には、それらを left、center、right、top、bottom の文字列に変換しています。

lcr には left、center、または right の文字が入力され、tcb には top、center、または bottom が代入されます。

また、xName には left か right が、yName には top か bottom が代入されます。

#73-81 は配列指定が適切ではなかった場合のエラー処理です。

#82-107 は layout がオブジェクト指定された場合の処理です。

#83-87では layout オブジェクトを走査して、配置指定に係る定義値を取得し、変数に代入します。捜査の結果必要な文字がなければ #88-91 でエラー処理します。

#92-100 は 配置指定が px 値ではなく "center" などの文字列で行われた場合の処理です。

#93-97 で配列 m に横方向と縦方向の配置指定文字列を代入し、layout が適切な指定でなければ、#97-100 でエラー処理します。

#101-107 では縦横方向の配置指定値を取得します。

二項演算子を活用して、センター配置か否か、及び px 指定値がある場合の2つのケースから値を取得します。

エラー発生時には空オブジェクトを返すようにしました。

#108-110 ではここ迄の処理で確定した配置用変数を活用して CSS 値を設定します。

センター配置の場合のみ画面中央位置を取得し、その他は指定されていればピクセル値を代入します。

▲ToTop

第Ⅴブロック:ポップアップ用 div 要素の作成(#111-134)

ブラウザにとってポップアップ用要素が用意できなければ何も始められないため、これ以降は DOMReady 関数で括ります。

popup 表示用の div 要素タグの作成(#115-120)

2回目以降の animatedPopup 起動時に dispElem が重複設置されないように 116 行でこのノードの存在確認を行います。もし存在すれば、改めて変数 disp に当該ノードを参照する jQuery インスタンスを登録します。

存在しない場合には、タグ要素を CSS 設定値を含めて作成し、その後に jQuery インスタンスを作成し変数に代入します。

この CSS 設定では絶対配置、非表示、レイヤー順を指定し、ポップアップを自在に配置できるように、また body 部に追加した時点ではそれが表示されないよう準備します。

popup 隠蔽用×印タグの作成(#123-127)

ポップアップ用 div 要素の右上にこのポップアップを隠蔽するためのバツ印を用意しました。ここでも二重登録しないようにこの要素の存在確認を行います。

更に×印だけではマウスカーソルをその位置にフィットする手間が面倒なので、隠蔽クリックを受け入れる箇所を点から線に拡張して、タイトルバー形式にしました。

▲ToTop

第Ⅵブロック:アニメーション用の諸関数定義(#129-193)

ここ迄でアニメーションポップアップのための準備が終わりました。愈々、動きのあるポップアップ表示や隠蔽操作のための関数を定義します。

ポップアップする要素の高さのサイズを測定する getElemWH 関数

まず第一引数 contents が未指定の場合の対応を 132 行で行いました。未指定の場合には所定の文章( || の右側の文章 )を用意しておき、これを animetedPopup 関数を使ってポップアップします。

文字列をポップアップする場合特に、その要素サイズを「表示前に」如何にブラウザに知らせるかが課題となります。文字列の文字数が固定されていても、文字の横幅は一定ではないので内容によってはサイズは変わりますし、一般にポップアップ表示を行う文字列は長さは不定・可変です。そのためブラウザは、幅が指定されていなければ表示領域一杯の幅が指定されたものと見なし、或いは要素の幅が指定されている場合にはその幅で要素を表示しますが、ブラウザはこれらのいずれの場合においても、要素表示前にはその要素の高さを認識しません。

別のエントリイで不定型ボックスサイズを表示する前にブラウザに認識させる方法について詳細に触れましたが、このプラグインで採用した方法は、自前で考案した透明化してサイズを測定する方法ではなく、jquery.js で利用されている visibility 属性を利用する方法を採用しました。

jquery.js で採用している表示前サイズ計測方法は、1. position : absolute、2. display : block、3. visibility : hidden とすることにより、「絶対配置のブロック表示状態にしてそれを表示させない」状態を作るものです。(jquery.js: css 関数内の #773 で呼び出される #735-748 の swap 関数)

なお、このプラグインで実際に必要となるのは outerHeight と innerHeight なのですが、この際無意味ですが4つのサイズを測定しておくようにしました(苦笑)。

またinnerHeight については、代入式を iH のプロパティ値とすることにより、同時に 2 つのプロパティに値を取得させました。

ポップアップボックスの拡張/縮小を演出する shrinkCSS 関数

アニメーションを演出するために幅と高さが極小のスタイル値を設定します。これによりアニメーションの始点と終点の位置と要素の表示状態を取得します。

またアニメーションの始点/終点を取得するためには、縦横のスクロール状態を Javascript インタープリタに認識させなければなりませんから、この関数内でスクロール値を取得します(#145)。

後述するように、クリック時のイベントハンドラー内から、shrinkCSS 関数を呼び出して、そのときのスクロール値を取得します。

次にこの関数内から #151 で getPos 関数を呼び出していますが、これにより画面内の左右上下中心のどの位置に配置するか、あるいはマウスカーソルの近傍に配置するかを確定します。

エラーフラグが立っている場合にはコード進行を止めます。(#147)

#153-156で right や bottom 指定された場合に left や top に変換しています。

right / bottom 指定された値をleft / top に変換する計算式は、最後の最後まで苦労を重ねました。getPos 関数からの戻り値は、right / bottom 指定のママですが、#153-156 においてこれらを left / top に変換します。この結果 shrinkCSS 関数からの戻り値は left / top のみとなります。

popup 要素をアニメーション隠蔽する hideElem 関数

これはいわば逆アニメーションで、animaElem 関数で表示したポップアップを縮小しながら隠蔽します。最初にコンテンツを削除してから shrinkCSS 関数をよびだし、かつこのアニメーションを待ち行列に登録しないよう queue 値を false にします。

なお、shrinkCSS 関数でエラーが発生する可能性がありますが、hideElem 関数は、必ずanimaElem 関数が走行した後にしか起動されず、animaElem 関数内において shrinkCSS 関数にエラーが発生した場合の処理は記述されています。エラー発生時には hideElem 関数呼び出しまで到達しませんので、当該関数ではエラー処理を必要としません。

アニメーションポップアップ表示を行う animaElem 関数

ここでは次のような様々な処理を行っています。1.登録アニメーションの削除と停止(#165)、2. この関数内で一回だけ shrinkCSS 関数を呼び出し、結果を変数に記録(#166)、この返値が空の場合の処理(#167)3.画面からポップアップをはみ出させない処理(#170-174)、4.画面センターに配置する場合、あるいは right / bottom が指定された場合の、ポップアップサイズと表示位置の調整(#176-180)、4.アニメーションの始点設定(#184)、5.アニメーション表示(#186-188)(ここでも隠蔽処理同様に待ち行列には登録しません。)、そして 6.ポップアップ窓の隠蔽ハンドラー呼び出しです(#190-192)。

ここでの要点は以下の点です。

  • ポップアップ要素を画面外にはみ出させない処理のために必要となる要因は、画面サイズ、スクロール値及びポップアップ要素のサイズです。
  • アニメーション待ち行列の扱いは、全てのアニメーションを非登録としました。当然ですが登録してしまうと、二度目以降の animatedPopup() 起動時において、最初以降から直前までの以前に利用したアニメーションが順次起動してしまうためです。
  • 179行で、duration や easing が指定されなかった場合のデフォルト値を設定しました。
  • ポップアップの隠蔽はクリックイベントを登録して行いますが、表示関数の中から行うようにしました。連続してクリックしてポップアップさせた場合に、クリックしてもそれを消せない場合が発生したためです(原因は不明)。

▲ToTop

第Ⅶブロック:クリックイベントの登録(#190)

最後の処理です。要素タグへのクリックイベント登録では、エラー発生時に登録済みクリックイベントを削除するようにしました。ここで最初にして最後ですが errflag 値を利用します。

イベントハンドラー内で使用した $(this) は click が jQInst のメソッドですから、jQInst を参照します。

jQuery が Ver1.2.6 へUP

1.2.3 から一気に 1.2.6 へ

1.2.6 のリリースは 5月 24 日、本家サイトに拠れば「Releases 1.2.4 and 1.2.5 were skipped 」。1.2.4 と 1.2.5 はそれぞれ問題があったためだそうです(1.2.4 was built incorrectly, rendering it effectively identical to 1.2.3, and 1.2.5 was missing a patch.)

1.2.3 からの改良は高速化と新機能にあり、以下のように紹介されています。

  • Performance Improvements(速度改善)
    • Event Handling is 103% Faster
    • CSS Selectors are 13% faster
    • .offset() is 21% faster
    • .css() is 25% faster
    • .css() is 25% faster
  • New Features and Major Changes(新機能と大きな変更)
    • Dimensions Plugin is Now Part of Core
    • .attr() overhaul
    • .toggle() can now accept more functions
    • .index() supports jQuery collections
    • beforeSend can cancel Ajax calls
    • Exposed Speeds

それなりに魅力的なマイナーバージョンアップだ

高速化は歓迎すべき改良点であり、言わずもがなの前進でしょう。

それぞれの高速化がどの様なコードの変更によってもたらされたのか、その解明に興味をそそられます。

また、Dimensions Plugin の扱いやtoggleメソッドの機能拡張も興味深い点です。ユーザーフレンドリーなコード作成に活用出来るのではないか、と期待させられます。

▲ToTop

いずれにしても・・・jQuery の解読を先に進めたい

1.2.2における解読が暫く中断してますが、更なる発展を遂げつつあるjQueryの解読を今回のバージョンアップを契機として、更に先に進めたいと改めて感じ入った次第です。

イベントコードの解読を終えたのは既に約半年前になってしまいましたが、次は jQuery.js のAjax 関連コードの解読に踏み込みたいと考えている今日この頃です。

jQuery と FirebugLite を一括登録するブックマークレット

以前次のように書きました。

jQueryブックマークレット(=jQuerify)を利用する前提として、Firebug(firefoxの場合)又はFirebug Lite(IE、Operaの場合)を組み込む必要があります。前者の場合にはFirefoxのアドオンとしてBrowser自身に組み込めばお仕舞いで、簡単に jQuerify を利用することが出来ます。

一方、後者の Firebug Lite の場合には、jQuerify を適用するサイトに、Firebug Liteをインクルードしなければなりませんが、それを一々追加するのでは余りに面倒です。

そこで、Firebug Lite と jQuerify を同時にインクルードするブックマークレットを作成し、容易にインクルード出来るようにすれば便利なはずです。

ここに、jQuerifyブックマークレットにおいて使用する jQuery は、Google のファイル置き場に置いてあるので、そこへのリンクを貼ればよいのですが、IE 、Opera 及び safari 用の Firebug Lite の方は当該サイトに zip 圧縮ファイルがあるだけですから、自ブログサーバーまたは自サーバー等に展開したファイルを置く必要が出てきます。

この時に問題となるのは、Fc2ブログサーバーに置いたファイルに対して、別のドメインや他の fc2 ブログ頁からリンクを貼ることが認められてない、ということです。当然の措置ですが Fc2 はFc2ブログのファイル置き場を、外部サイトから利用する所謂「ファイル置き場」として利用することを、認めていません。

jQuerify.jsブックマークレットを利用したい in this Blog

先のエントリを記述した時点では、「ファイル置き場」を探して彷徨ったものの、適切なサイトを見いだせませんでした。そのため今回解決した 「jQuerify と firebugLite を一括してブックマークレットにする」ことを諦めていたのです。

▲ToTop

サーバーに FirebugLite を置き、その利用を認めているサイトがあった!───がしかし...

その後、あれこれと探す中で希少なサイトが見つかりました。展開した Firebug Lite ファイルを置き、その利用を認めているサイトがあったのです( WebOS Goodies )。そこで早速それを利用させていただくこととし、今回念願のブックマークレットを作成しました。

感謝! webos-goodies!

このエントリイでは、jQuerify と firebugLite を一括インクルードするブックマークレットを紹介させて貰いますが、IEには文字数制限がありますし、fireFox などの Mozilla 系ブラウザの場合には firebugLite をインクルードする必要はないので、それらを考慮して複数のブックマークレットを作成しました。

その後 webos-goodies の firebug は使用できなくなってしまいました。断りもなく勝手にリンクを貼ったことが失礼に当たってしまったのかも知れません。陳謝!

そこで別のファイル置き場を探してそこにfirebugを置くことにしました。

No1.簡易版ブックマークレット
( Firefox or Opera or IE 用. scriptタグの有無などを確認しない簡易版 )

まず、jQuerify と firebugLite を一括してインクルードするブックマークレット No1 を作成しました。文字数は 391 文字で IE6 における文字数制限 508 文字以内に納めるべく、ブラウザチェックやscriptタグチェックを一切行わない原始的なバージョンです。いくつかのサイト上で IE7、Firefox 及び Opera で動作を確認しました。

なお既に firebug が組み込まれている Firefox などでこのブックマークを実行すると、firebug 本体とは別に firebugLite が登録されてしまいます。こうした事態を発生させないために No2 を作成しました。

下のリンクをお気に入りに登録すれば使えます。
No1.簡易版 jQuerify + FirebugLite
No2.拡張版ブックマークレット( Firefox or Opera 用 )

上記のブックマークレットでも実用的に支障はないのですが、ブックマークを二度以上 click してしまうことも良くありますので、本来重複登録しないようにすべきです。また firebug 本体を組み込んだ Mozilla 系ブラウザにおいては、firebugLite を組み込む意味は皆無です。

そこでインクルード済みかどうかをチェックすると共に、Mozilla 系ブラウザの場合には firebugLite を組み込まないようにしました。つまり Firefox などの Mozilla 系ブラウザでは firebug は組み込み済みのものと仮定し、このブックマークレットを使う場合には、jQuery だけをインクルードするようにしました。

文字数は 543 で、いくつかのサイト上で Firefox 及び Opera で動作を確認しました。

なお、このブックマークレットは IE の文字数制限値 508 を越えていますので IE6 及び 7 では動作しません。このため、別途 No3 を作成しました。

次のリンクをお気に入りに登録すれば使えます。
No2:for Firefox or Opera, jQuerify + FirebugLite
No3.拡張版ブックマークレット( IE or Opera 用 )

script 存在を確認する機能を持たせた上で、IE でも動くような文字数制限ぎりぎりのコードを作成しました。文字数は 492 文字です。そのため性能を削らざるを得ず、具体的にはブラウザチェックを排除せざるを得ませんでした。

このため Mozilla 系でも動いてしまい、firebug 本体が組み込まれていても firebugLite をインクルードしてしまう欠点があるので、敢えて IE or Opera 用としました。

動作確認は IE と Opera 及び Firefox(Firefoxの場合には firebugLite が登録されてしまいますが)で行いました。

次のリンクをお気に入りに登録すれば使えます。
No3:for IE or Opera, jQuerify + FirebugLite
※ Opera の場合には IE 程の文字数制限はないようなので、 No2 、No3 のどちらでも使えます。

各ブックマークレットのコード

No1( script タグの存在有無を確認しない版 )

上のブックマークレットのコードは以下の通りです。分かりやすく改行等を入れました。

<div>下のリンクをお気に入りに登録すれば使えます。<a href="javascript:
(function(){
  // 文字数を減らすため最初に全ての変数を一括定義
  var%20h = document.getElementsByTagName('head')[0],
  s = document.createElement('script'),
  c = document.createElement('script');
  // jquery インクルード
  s.src = 'http://jqueryjs.googlecode.com/files/jquery-1.2.3.pack.js';
  s.type = 'text/javascript';
  h.appendChild(s);
  // ブラウザチェック(IE、Opera 及び Safari)
  if(/(msie|opera|webkit)/.test(navigator.userAgent.toLowerCase())){
    c.type = s.type;  // 文字数を減らすため複写出来るものはそれを活用
    // firebug Lite アドレス
    c.src = 'http://hkom007.googlepages.com/firebug-gp.js';
    h.appendChild(c);
  }
})()">No1.簡易版 jQuerify + FirebugLite</a></div>
No2( script タグの存在確認版で Firefox or Opera 用)

上のブックマークレットのコードは以下の通りです。分かりやすく改行等を入れ、また読みやすいようにスペースも入れました。

<div>下のリンクをお気に入りに登録すれば使えます。<a href="javascript:
(function(){
  // 文字数を減らすため最初に全ての変数を一括定義
  var x = 0,y = 0,h = document.getElementsByTagName('head')[0],
  t = document.getElementsByTagName('script'),
  s = document.createElement('script'),
  c = document.createElement('script'),i=0;
  s.type='text/javascript';  // これも文字数を減らすために二度表示しないように
  // script タグがあれば
  if(t){
    for(;i<t.length;i++){
      if( /jquery-/.test(t[i].src) ) x += 1;
      if( /firebug/.test(t[i].src) ) y += 1;
    }
  }
  // scriptタグがないか、又は
  // jquery 文字列がない場合の src = jquery.js の script タグ追加
  if (x==0) {
    s.src = 'http://jqueryjs.googlecode.com/files/jquery-1.2.3.pack.js';
    h.appendChild(s);
  }
  // Mozilla系ではなく、かつ scriptタグがないか、
  // 又は firebug 文字列がない場合の src = firebug.js の script タグ追加
  if ( !/mozilla/.test(navigator.userAgent.toLowerCase()) && y==0 ) {
    c.type = s.type;  // 文字数節約のためこうした
    c.src = 'http://hkom007.googlepages.com/firebug-gp.js';
    h.appendChild(c);
  }
})()">No2:for Firefox or Opera用, jQuerify + FirebugLite</a></div>
No3( script タグの存在確認版で IE or Opera 用)

No3 は No2 と以下の部分が異なるだけです。

No2:  if ( !/mozilla/.test(navigator.userAgent.toLowerCase()) && y==0 ) {
          ↓
No3:  if ( y==0 ) {

 90%近いシェアを握っているインターネットエクスプローラの描画エンジンを利用したタブbrowser。沢山のタブbrowserがあるが、多機能、カスタマイズフリー、スクリプト利用等で一日の長がある。Gekkoエンジンへの対応も行われ、IEからの自立独立の方向に向かっている。2005年7月にはIE7が登場する見通しの中で、今後の発展が望まれる。

 多様なCSS作成支援機能を備えた、タグ入力式 HTML&CSS作成支援エディタ。スキンデザインもすっきりしている。テキストエディター上で作成するよりも確実で安全にタグ打ちが出来る。
文字コードを選べないのが欠点。

 StyleNote同様のタグ入力式 HTML&CSS 作成支援エディタ。長年使用してきたが現在StyleNoteに乗り換えつつある。

 クリップボード履歴情報を活用する為のソフト。画像まで履歴を取ってくれるのが嬉しい。このソフトを使わない日は絶対ない程に重宝し、愛用している。

 起動中のウィンドウの「コピーできない」説明文などの文字列を取得し、コピー可能な文字データにするツール。何かと便利。

 ストリーミングデータを保存することが出来るソフト。動画利用には不可欠なソフトだ。

 無料ながらレイヤー機能を有し、スクリプトによる拡張も可能な、sleipnir作者が提供している優れもの画像編集ソフト。

 画面キャプチャソフトと言えばこれに勝るものなし、ではないだろうか? 様々な取得方法を有しており、ブログ作成にもHomepage作成に不可欠だ。Jtrimと並んでWoodyBellsの作品。

 複数ファイルの同時編集は出来ないが、透過pngも作れる画像編集ソフト。
(以下当該サイトから抜粋)初心者にも簡単に操作が出来るフォトレタッチソフトです。多くの加工機能で画像に様々な効果を与えることができます。非常に軽快に動作するため、ストレスなく操作できます。

 Animation Gifファイルを作れる無料ソフト。

 キャプチャソフト。画面内にサイト全体が表示しきれない場合でも、これを使えば全体をキャプチャすることが出来る。

 画像処理。画像のフォーマット変換のみならず、色数やサイズ、圧縮率の変更まで一括処理できてしまう『BatchGOO!』は、大量の画像をまとめて処理したいときに大変便利なソフト。BMP, TIFF, JPEG, PCX, PNG の相互変換をはじめ、色数・サイズ・解像度の統一、JPEG圧縮率の調節など、ホームページ用の画像や携帯電話用の壁紙を揃えるのに抜群の相性を見せる。(Vectorの当該ソフト紹介頁より抜粋引用)

 名前から直ぐに想像が付くように画像のサイズを測るためのソフトだ。Homepage作成には欠かせない。2カラム、3カラムのレイアウトを行う場合に大変重宝する。

 ランチャーソフトは沢山あるが、中でもこれが一押しだ。2年以上使ってきたがその操作性には毎日満足している。これを使い始めてからデスクトップには一切のアイコンを表示することをやめてしまった。

 AdobeReader7によって、起動時間が長すぎるという長年のユーザーの不満はある程度解消した。そのためこの高速化ソフトは存在価値が低下してしまったかもしれない。AdobeReader6迄はこのソフトによる起動高速化で恩恵を受けてきた。

 IE専用が難点だが、様々なサイト内でIDやパスワードを入力するのに重宝するソフト。コンテキストメニューから簡単に起動できるのがGood! sleipnir等のIEの描画エンジンを利用しているブラウザでも使える。

 利用しているパソコンの諸元値を取得するには、このソフトがベストだ。インストール済みソフトの一覧が取得できるのも嬉しい。

 WMPは機能が豊富なだけ重い。RealPlayerも同様だ。そこでMedia Player Classicを使いたい。動作が軽快なだけではなく、対応しているファイル形式もすこぶる多く、これひとつで、wmvもrmも表示できてしまうのだから凄い! 数多あるMedia Playerの王様と言えるだろう。

 自宅でPCを起動しているときには必ず起動しているメディアプレーヤー。何かと過剰なWinampよりも、起動も速くスキンはシンプルだ。

 DivX, Xvid, Mov, Vob, Mpeg, Mpeg4, avi, wmv, dv, などの動画をDVD-Video形式に変換できるフリーソフト。クリックするとDVD関連ソフト紹介サイト=「DVDなToolたち」なるHomepageが開きます。

----------
201007272344
200904050150
200904030753
200904020012
200903300116
200903240106
200903192026
200903181947
200903170136
200903160043
200903150302
200903120123
200903110018
200903081807
200903012228
200902250127
200902151108
200809281734
200806110106
200803060108
FC2 Management
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。