FC2ブログ

08 | 2019/09 |  10

  1. 無料サーバー

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

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


信じ難い IE の Javascript インタープリタの挙動

animatedPopup も makeTableContents も IE では動かない。

その他各種の jQuery plugin を作ってきたが、それらの多くが IE で動かない原因をずっと解明できないまま、時は空しく流れ去るばかりだった。IE 用にはお粗末なデバッガーしかないので、動かない原因がなかなか特定出来ないでいたのだ。

しかし、未だに相変わらずトップシェアを占めている IE で、目次作成プラグインなど全てのエントリイにおいて活用させたいものが動かないのは、ブログ製作のあり方として耐え難い。そこで、Firebug Lite や IE 8 の開発者ツール、あるいは DebugBar を駆使して、前から見当を付いていた " 怪しげな箇所 " を部分に細分化し、時には一行ずつ、場合によっては一文ずつ、一式ずつテストしてみた。

そしてやっとのことで、余りに<お粗末な> IE 固有のエラー発生原因の いくつかが究明できた。以下そのお粗末さを明らかにしておきたい。

なお、今回特定した問題はいちがいにバグとは言い切れないかもしれない。

余りにお粗末なインタープリタ

それは「 論理積演算子 」&& に関わる。この演算子のオペランドは左右とも「式」が許されているのに、IE では左側のオペランドが特定の或る式の場合には、「 式なのにエンドマークの;がないからエラー! 」 と判断するのである!

例えばこうだ。

 1: $("body").mousemove(function(e){
 2:   if (e.pageX-$(window).scrollLeft()<10 && $contents.is(":hidden")){
 3:     direction = false;
 4:     displayJumpList(e.pageY);
 5:   }
 6: });
 7: $(window).scroll(function(){
 8:   if (!$contents.is(":hidden") && Math.abs(initScrollTop-$(window).scrollTop())>20){
 9:     slideFuncOut.call($contents);
10:     initScrollTop = $(window).scrollTop();
11:   }
12: });

上のコードの 2 行目と 8 行目を比較すると、前者では&&演算子の左オペランドは比較式で、右オペランドは jQuery インスタンスメソッド、つまり関数呼び出し式である。他方、後者ではそれが逆となっており、左側に jQuery インスタンスメソッドがあり、右側には比較式がある。

さて、IE で、これらの 2 つの if 文のどちらが正常に働き、どちらがエラーとして認識されてしまうか、お分かりになるだろうか??

結論を言えば、Firefox、chrome、safari (いずれも windows 版)ではいずれの行も問題なく作動するし、IE に最も近い Opera でさえ問題はない。しかし、独り IE だけが 8 行目の && 演算子の左側オペランドにセミコロンがないからエラーとして評価してしまうのだ。

『Javascript 第 5 版』によれば「 関数呼び出しは厳密には式であるが、Web ブラウザを制御する副作用があるので文の仲間に入る 」。

つまり、IE のインタープリタは、「 関数呼び出しは厳密には式であるが、副作用を伴う文の仲間に入るからセミコロンが必要だ 」と判断するわけだ。

一方他のブラウザのインタープリタは、関数呼び出しは副作用を伴うが厳密には式なので、セミコロンは求めない、ということになる。

お粗末なインタープリタに対する 1 つのささやかな対策

2 行目は問題なく作動することが確認されている。しかし 8 行目はエラー。───ではオペランドの順番を変えてみたらどうか?───こう思い付くまでにさほどの時間は掛らなかった。

2 行目に準じて 8 行目の && 演算子のオペランドの左右を次のように入れ替えるのである。

8: if (Math.abs(initScrollTop-$(window).scrollTop())>20 && !$contents.is(":hidden")){

つまり、左オペランドには比較式を置き、jQuery インスタンスメソッド呼び出し式は右オペランドに移すのだ。このように入れ替えてから、開発者ツール等で試したところ、エラーはあっさり消え去り、問題は解決してしまったのだ。

構文ミスでも、記述ミスでもないこのような記述を、敢えて「 文だからエラー 」として認識してしまうとは、余りにお粗末ではないか。

因みに、次のように括弧で括って明確に式であることを明示してみたところ、これもエラーにはならなかった。しかしこの方法は可読性が低下するので決して好ましい方法ではないだろう。&& 演算子が登場する度に、関数呼び出しオペランド部分を一々括弧で括るなんて、余りに手間が掛りすぎる!

8: if ( (!$contents.is(":hidden")) && Math.abs(initScrollTop-$(window).scrollTop())>20 ){

2 つ目は mousemove メソッドの呼び出し元に関する問題である

最終的には 2 行目の mousemove メソッド呼び出し元は、body 要素としたが、コード作成時から最終改訂の直前までは、window としていた。そして $("window").mousemove(function(e){・・・}) で IE 以外のブラウザは意図を解釈してくれた。しかし IE だけはうんとも寸とも言わないのだ。

確かに、マウスは body 要素の上、あるいは中で動くのだから、mousemove イベントの呼び出し元は body であるべきだろう。それが論理的である。しかし、マウスは window の上あるいは中で動いていることもまた真なのだから、window が起動元であっても mousemove メソッドは作用すべきだと思われる。

独自仕様を乱発してきた IE はその裏で余りに厳格な解釈を行っている

上の 2 つの事例から言えることは、そういうことである。余りに厳格な解釈によりエラーを乱発するのだ。「 仕様だから仕方がない 」という立場もあるかもしれないが、インタープリタは運用上のスマートさも兼ね備えるべきだ。

次に想起されることはこうだ─── || 演算子は IE で正常に働くのだろうか?

こちらは<予想を裏切り>、他のブラウザと同様に正常に機能した。

8: if (!$contents.is(":hidden") || Math.abs(initScrollTop-$(window).scrollTop())>20){

上の 1 行では&&演算子の左側オペランドは関数呼び出し文であるが、実行してもエラーははき出されないのだ。このことから言えることは、「 IE のインタープリタには一貫性がない 」ということだ。

余りに厳格な解釈を行うと思えば、論理的一貫性を欠く。───それが IE の Javascript インタープリタなのだ!

【 結論 】&& 演算子における IE 対策

  1. && 演算子の左側オペランドに関数呼び出し文を置かないようにする。
  2. どうしても左側に関数呼び出しを置かざるを得ない場合には、&&演算子の左側オペランドを一括りの ( ) で括って、IE が文としての厳格な解釈を適用しないようにするのが得策だ。

ファイル保存場所(URI)が勝手に変わってしまいました。

インクルードしているjsファイルを上書き保存したら、アドレスが勝手に変わってしまった

最近では jquery.js を多用し、基本的にエントリイ末尾に jquery.js を利用したコードを記載しています。しかし、それとは別に、このブログを開設した当初から一貫して自作 js ファイルをインクルードし、このブログの基本構造を構築しています。そしてその自作 js ファイルは開設当初こそ頻繁に改訂していましたが、ここ数年は年に 1、2 回程度しか改訂することはありません。

そして昨日、jquery.js を使ったある機能を自作 js ファイルに取込んで上書き upload した時に「事件」が起きたのです。

上書き保存した js ファイルが全く include されないのです。

最初は何が起こったのかさえ分からず、Firebug に表示されるエラー数が余りに多いので吃驚仰天するばかり。早速原因究明に必死になりました。

ブログ閲覧機能の多くを自作 js ファイルに依存しているので、そのファイルがインクルードできないと、このブログは殆ど無価値になってしまうため、必死でした。

小一時間の格闘の末、自作 js ファイルの URI が勝手に変更されていることが発見出来た時には、原因が特定できほっと一息ついたのですが、その後に勝手にアドレスが変わってしまう仕様に疑問を覚えました。

▲ToTop

サーバー管理上の理由はあるのでしょうが、アドレスが変わったことを alert すべきです

アップロードしたファイルを上書き修正するケースは決して少なくないはずです。

どのようなケースの時にアドレスが自動的に変更されてしまうのか知る由もありませんが、せめてアドレスが変わったことが一目で分かるような警告表示を出すべきだと思います。

確かにアドレスは赤字で表示されますが、まさかその一部が変更になっているとは普通は思いもよりません。ですから単にアドレスを表示するだけでなく、変わった場合には変わったことを警告すべきです。

因みに今回自動的に変更されたアドレスの箇所は以下の通りです。

src="http://blog-imgs-15.fc2.com/・・・"

上の緑色の数字が 31 から 15 へと勝手に変えられてしまったのです。

このようなケースが多発するのかどうか分かりませんが、情報をお持ちの方がいればご教示願いたいと思います。

Firebug 1.3.0~1.3.2 のバグが 1.3.3 でやっと解消

やっとまともに使えるようになったようです。

Firebug はJavascript コードを書くために必須のアイテムですが、ver 1.3.0~1.3.2 までのそれは基本的な問題に関するバグがあり、この間1.2.1を使ってきました。

Firebug 1.3.x のバグについて

それが 1.3.3 になってやっと解消されたようなのです。

これでやっと支障なくコード作成が可能となりました!(^。^;)ホッ!

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

Firebug 1.3.x のバグについて

1.3.x ではステップイン中にコード表示画面が固まってしまう!

そうなのです。スクリプトコードの進行過程をステップイン等で追跡していると、左ペインのコード表示が突如 20 行までとなってしまい、21 行より先が表示されなくなってしまうのです。何回も試してみましたが、確実に再現されてしまいました。

バージョンダウンさせ 1.3.1 や 1.3 でも試してみましたが、同様の現象が発生してしまい一向に問題は解消されません。そこで、日本語サイトを対象にこのバグ報告を探しましたが、皆目見つかりません。

なお、Firebug 1.3.x 系には他にも 先のエントリイ でも触れたバグもあり、そのことはこちら( mapserver2007@避難所:Firebugがバグる - livedoor Blog(ブログ) )にも触れられています。

1.2.1 にダウングレードする

そこでやむなく、半年前にリリースされた 1.2.1 に入れ替えてみたところ、やっとこの現象が起こらなくなりました。

1.2.1 では、サイトを開く時点で必ず、「Permission denied to call method Location.toString」なるエラーメッセージが登場してしまうバグがありますし、細かなバグは他にも多数あるはずです。だからこそ 1.3.x 系が登場してきたのでしょう。

それでもなお、基本的な使い勝手上で起こる決定的なバグよりは遙かにマシです。

兎にも角にも、バグ調査のためのツールそれ自体がバグっているのでは困ります!

Firebug1.3.2 で使用するファイルの文字エンコードについて

問題解明に何と丸々2日を要してしまった。

jQuery 解読には Firebug は必須ツールです。これがなければ解読は一歩も前には進めません。故に、その使い勝手や使い方には通じていたつもりでした。

ところが1年ほど前には全く問題がなかったのに、ここ数日ローカルファイルを Firebug でチェックしていて大きな壁に突き当たってしまったのです。

自作のスクリプトが Firebug のスクリプトウィンドウに全く表示されないのです。これではブレークポイントを作れませんし、従って jquery.js の挙動を解明することが全く出来ません。

久しぶりの Javascript との格闘なので、もしかしてコードが間違っているのではないか、と何度も firefox のエラーコンソールでチェックしてみました。しかしコード上には全くミスがないにも拘わらず、Firebug のスクリプト画面ではエラー表示されてしまうのです。

何が原因なのか全く分からないまま悶々としつつ、時間だけが過ぎ去り、ネットにヘルプを求めたものの、Firebug のバグに関する記述は幾つか見つかったのですが、全く別の問題ばかりです。かなり探したのですがネット上ではついに解決策は見あたりません。

ということは私の基本的なミスに起因すると考えるしかない、と判断せざるを得ず、仕方なく対象となっているローカルファイルを適当に分割し、部分毎にコンテンツをとっかえひっかえしてみて、ついに原因解明に到達したのです。

▲ToTop

原因は文字コード指定にあった

Fc2 ブログは文字エンコードに euc-jp を使用しています。だからローカルファイルもそのエンコードで作成し、またそのように HTML文 の meta タグ内に指定してきました。そして、その指定でも Firebug 1.0 では全く問題なくスクリプト画面が表示されていました。

ところが、Ver1.3x になってから厳格に判断するようになったようでしょう。この文字エンコード指定が災いして euc-jp エンコードで記述した Javascript ファイルを表示することが出来ないのでした。( 但し興味深いことに Javascript 文の作動はきちんと行ってくれていました。「 作動すれど表示されない 」 状態になっていたのでした。 )

丸々二日間の格闘を経て、こうしてやっと地獄の苦しみ(苦笑)から脱却できたのでした。

--------------------------------------------------------

こちらのサイト( mapserver2007@避難所:Firebugがバグる - livedoor Blog(ブログ) )にこの問題に関する記述があります。

FireBug1.2.0b6 使用感

Firefox 3 登場に合わせて登場した firebug 1.2.0

6月18日の Firefox 3( 以下「FF3」)登場段階ではまだ未対応だと思っていた FireBug ( 以下「FB」)ですが、実は既に対応済みだったようです。兎に角早速 FF3 対応版を使い始めました。

さて、スキンが殆ど前のままなので、使い勝手も同様だろう、と踏んでいたのですが、それは誤解でした。

Webサーバーへの負荷を考慮したのか、デバッグ対象とするサイトを登録しなければならないのです。その使い勝手は今一で、最初は非常に戸惑いました。

あちこちの Web サーバーにある外部スクリプトファイルを利用しているので、そのデバッグを FB で行おうとして、これまでのようにスクリプトリストを開いてみたら、どうも違う!───いくつかのサーバーがそのリストに表示されないのです。以前のバージョンの場合にはそんなことは全くなかった訳で、「何がどうなったのか?」最初は戸惑いました。。

特に今行っている Ajax 通信に利用している Google Pages サイトがスクリプトリストに表示されないのには参りました。

また、新しいスクリプトを利用すると一々解析対象とするかどうかを聞いてくるのにも閉口しました。余りに「五月蠅い」のです。

▲ToTop

今は快適に使用している

そんなこんなで試行錯誤を数日重ねた結果、やっと仕組みが分かってきましたので今は何も問題なく使えているのですが、本家の英語サイトの説明を一々読み込むほど英語に強くないので、困惑は今後も継続するかも知れません。

何はともあれ、一つの問題が解決した、という余りにありきたりの話ではありました。

3 へアップデートした FireFox をやっと導入

最初はアドオンが揃っていなかったのでやめた

一ヶ月程前の6/18に、3 へのアップグレードを知って直ぐにインストールしてみました。しかし、その時点では多くのアドオンが未対応だったため、直ぐに 2 へダウングレードしてしまいました。

ところが、PC Japan 8 月号の 特別企画記事(「 Forefox3超入門 」 )を読んでみたら、著名なアドオンの多くが 3 対応になっている様子が克明に記されていました。そこで本日、早速 3へのアップグレードを行いました。

結果は極めて良好。

多用するアドオン( Firebug、 All-in-One Gestures、FlashGot、Web Developer 等)が 3 対応となっていたので、快適なブラウジングを履行できています。

なお、Firefox 3 の灯 - 日本灯 によれば 2008/7/21 10:15現在、日本では 累計 1,232,600 件以上のダウンロードが行われていました。

何故 Firefox 3 なのか?

上記特別企画記事でも触れていましたが、Firefox の普及度は暫時高まっているようですが、トップシェアは相変わらず IE が抑えています。

しかし、IE の最大の問題は Javascript 処理速度が、Firefox や Opera のそれに較べて極端に遅いことです。その結果 IE では Javascriptによる様々動作の反映が非常に遅いので、ユーザーの待機時間が長くなってしまうのです。

そんな訳で、自ブログにおいて Javascript による制御を多用するようになった最近は、 IE はもちろんのこと、Sleipnir 2 も IE と同じ描画エンジンを使って表示させる限り、動作が遅いと感じるようになり、専ら Firefox で閲覧、記事投稿、アップロードを行うようになりました。

▲ToTop

利用するアドオンを大幅に追加しました

下図は 2 と 3 における My addOn 利用状況ですが一目瞭然です。

2 から 3 にアップグレードするに当たって、利用するアドオンを 18 本から 30 本近くまで増やしたのです。

FireFox 2 と3 の利用 AddOn の差異<br />クリックすると別窓で原寸画像が表示されます

これらのアドオンの中で特筆するに値するものについて、触れてみたいと思います。

▲ToTop

Firefox アドオンソフトの中でも印象が強かったもの

PicLens

何と言っても筆頭は PicLens です。このようなソフトの存在は全く知らなかったのですが、上記 PC Japan 特別企画記事を見てそれを初めて知り、早速 Firefox はもちろんのこと、IE7 においても利用してみました。

それを使ってみた第一印象は、賞賛している以下のサイトでも強調されていますが、「衝撃的」でした。本家が「Immersive」つまり「実体験のように感じる」と宣伝するのも頷けます。

goo の画像検索ページでも使えるようになったそうですが「宜なるかな!」───先の iPhone 日本上陸と相まって、これからは、このような仕様の画像・動画閲覧アプリケーションはますます増え続け、直ぐに主流となるのではないか、と思います。

その基本は「視覚情報は、まさに視覚に訴えて処理する」──これに尽きるでしょう。

次期 OS である Windows 7 は 2010 年に登場する計画だそうです。Microsoft 社のこのようなロードマップは過去において何度も破棄され延期されているので全く当てには出来ませんが、それはさておき、その OS ではタッチパネル方式を本格的に仕様として取り入れるらしいのです。これも iPhone によるパラダイム転換の 1 つでしょうが、画像と動画の検索ツールは、今後ますます競争、シェア争い、そしてツールの概念( パラダイム )競争、つまり仕様変更競争がヒートアップして行くのではないでしょうか。

▲ToTop

Auto Pager

これまでは Gresemonkey のプラグインを利用して、Auto Pager を行ってきました。Google 検索結果が複数頁に亘る場合に、一々クリックして次ページを見るのではなく頁下部にスクロールすると、自動的に次ページが下に展開される───それがここで言う「Auto Pager」です。

それがアドオンで可能となっているのですから飛びつきました。

Video Download Helper

これまで YouTube などの動画サイトからのダウンロードは、IE のエクステンションを利用してきました。

しかし、Firefox 利用頻度が高まるにつれ、このブラウザでも動画サイトからのダウンロードをしたいと願っていたので、これ幸いのアドオンが見つかった訳です。

Make Link

Sleipnir 2 で便利なのは、閲覧しているサイトのリンク情報を簡単なショートカットキー操作で取得できる点ですが、Firefoxにそのような機能がないことをずっと訝ってきました。しかし、それを行えるアドオンがあったのです。使い勝手は Sleipnir2 のそれほどよくありませんが、目的は容易に果たせました。

マルチプルタブハンドラ

上のリスト画像にはありませんが、画像作成後に追加しました。Sleipnir 2 のタブ操作同等の機能が欲しかったので追加しました。Sleipnir 2 にあるナブロック機能がないのは残念ですが、URIコピーも使えるので気に入っています。上の Make Link は不要になるかも...。

Save Image in Folder

余り画像に関心がある訳ではないのですが、まあサイト上の気に入った写真をその場でローカルPCに保存できることは好ましいこと、と思った訳です。画像の保存に活用することでしょう。

その他

タブカタログ、テキストリンク、goo辞書、Media Player Connectivility などもオイオイ活用してみようと思っています。

jQuery を活用したオリジナルの Ajax 通信コードを全面的に改訂しました

何はともあれ結果を見て戴くのが最善でしょう

任意の Fc2 ブログを個別エントリイモードで開き、そこで Ajax 通信を行って、最新/以前/以後エントリイのタイトル情報などを取得する Ajax 通信プロジェクト(以下「 Fc2タイトル情報プロジェクト 」)を半年を要してやっと実現しました。その結果サンプルは以下の通りです。

サンプル 1【 Fc2総合インフォメーションブログ 】

下の画像は、Fc2総合インフォメーションブログ上で Fc2 タイトル情報プロジェクト を実行した際の表示結果抜粋です。

Ajax 通信によって最新/以前/以後エントリイのタイトル情報などを取得した結果を示すサンプル画像 1
サンプル 2【 関西ZIGZAG 】

下の画像は、人気ブログ「 関西 ZIGAZAG 」ブログ上で Fc2 タイトル情報プロジェクト を実行した際の表示結果の抜粋です。

Ajax 通信によって最新/以前/以後エントリイのタイトル情報などを取得した結果を示すサンプル画像 2
サンプル 3【 FCafe 】

下の画像は、自ブログ開設時からお世話になってきたサイト上で Fc2 タイトル情報プロジェクト を実行した際の表示結果の抜粋を示すものです。

Ajax 通信によって最新/以前/以後エントリイのタイトル情報などを取得した結果を示すサンプル画像 3

以上に例示した Fc2 タイトル情報プロジェクト の詳細については、こちらのエントリイ( ファイル置き場と Ajax 通信を活用して、任意のFc2ブログから情報を得る )をご覧いただければ幸いです。

「その光明」はなかなか見えなかった

jQuery.js を活用した Ajax 通信に、今年の 2 月からチャレンジしています。

しかし、目的とする連続的な Ajax 通信の結果取得(後に分かったことではあるが、結果取得ではなく取得結果の即刻表示)が巧くいかず、半ば諦め、4分の1ほどやけっぱちになり、4分の1ほど食らいついて解明したい、との思いが錯綜する中で、昼間の仕事の多忙さに追われて、なかなか「食らいつく」時間が取れませんでした。

しかしこの程、幸いにもその時間が取れたので、改めて jQuery.js( ver1.2.6 )コードと格闘し、最近刊行された書籍( Amazon.co.jp:『jQueryで作るAjaxアプリケーション』: 沖林 正紀著 2008年6月19日発売) )も購入し、 jQuery.js を利用した Ajax 通信に再チャレンジしてみました。

余談ですが、上記の書籍は日本語で書かれた jQuery.js 入門書としては初めてのものではないでしょうか?

その点での価値は大いに評価できると思います。

しかし、内容は、といえばまさしく「羊頭狗肉」と言わざるを得ません。

書籍名から推測すると、 jQuery.js を活用した Ajax アプリケーションのあれこれについて書かれているのではないか、と思っても決して無理はないと思います。しかし、内容は決してそのようなものではありません。

jQuery.js 入門書としては結構詳述されていますが、 Ajax アプリケーションのあれこれについてはたいした記述がありません。しかも Global イベントを利用した Ajax 通信のことしか書かれていないのです。jQuery 本家サイトを見れば分かるとおり、global イベントを利用した Ajax 通信は「それも出来る」行為であって、決して推奨されるべきものではないはずです。

何故ならば、Ajax 通信はその結果を取得するのに時間を要する行為であり、一方イベント処理も jQuery.js コード内を縦横に縦断する時間を要する行為です。二重に時間を要することを推奨すべきではないでしょう。

故に書籍名はまさに「看板倒れ」と言わざるを得ないのです。書名を「jQuery 入門」とした方が遙かに「体を表す名」だと思います。

▲ToTop

連続的な Ajax 通信による結果取得の困難性

以前のエントリイタイトル、最新のエントリイタイトル、そして以後のエントリイタイトル───Ajax 通信によるこれらの 3 つのフェースの情報取得をずっと試みる中で、特に躓いた点は過去/未来のエントリイタイトル取得、それもそれぞれに 10 個の、合計 20 個のエントリイタイトルを一発で取得する Ajax 通信でした。

もっと具体的に言えば、1回のコード進行で過去/未来の 20 個のエントリイタイトルを取得することは出来たのですが、それらの取得後可及的速やかにそれらを表示することがどうしても巧くいかなかったのです。

例えば、20個の取得を指示しても、過去エントリイは 3 つしか表示できないのに、未来エントリイは 10 個表示できたり、その逆だったり、二度目に実行すると必ず 20 個全てのタイトルを表示取得できたり、結果取得が非常に不安定だったのです。

どうしてそうなるのか、どうすれば問題を解消できるのか、その解決策を求めて上記書籍を購入したのですが、そこからは解決策は得られませんでした。

あれこれのサイトも探索しましたが、1回だけの Ajax 通信に jQuery.js を利用した例はいくつか見つかりましたが、連続してAjax通信を行って、その結果取得を試みたサイトは見つからないのです。

勿論 FireFox + Firebug を利用して進行過程をチェックしてみましたが、Ajax 通信の進行過程は流石の Firebug でもブラックボックス化してしまい、その過程を覗くことが出来ませんし、Firebug でコードのステップ進行している間にも Ajax 通信は進行するので、通信過程を垣間見ることは出来ませんでした。

更に、Firebug でコードをステップ進行するということは、その間に Ajax 通信の進行を許す訳ですから、いわば待機時間を作ることになり、通常では巧く取得できない結果も Firebug を使って進行をチェックすると取得に成功してしまうのです。

つまり、Firebug を使っても Ajax 通信のコードチェックにはならないのです。

▲ToTop

連続した Ajax 通信においてその全ての通信結果が取得できたことをどのようにして知るのか?

連続する Ajax 通信結果を可及的速やかに表示するためには、最後の通信結果が取得できたそのタイミングを Javascript に知らせなければなりません。スクリプトがそのことを知って初めて表示させる事が出来る訳ですから、通信結果が全て取得できたことをコード自体に分からせなければなりません。

その方法を色々と暗中模索した経緯の一端を以下に記しておきます。

最後の通信結果が存在すれば..

例えば「最後の通信結果が存在すれば..」という条件では不完全です。何故ならば最後の通信結果が取得できたからと言って、それ以前の通信結果が取得できている保証は何もないからです。

実際色々試してみましたが、Ajax 通信の所要時間は、それぞれの Ajax 通信の通信量と回線速度によって変化しますが、回線速度が同一であれば Ajax 通信量で決まります。つまり通信されるコンテンツの容量によって通信時間は決まります。今回の例においては、取得する HTML ファイルの容量、つまり各エントリイの本文の長短に応じて通信所要時間が変化します。大きなサイズのエントリイの場合には通信時間が長くなり、小さなサイズの場合には短くなります。

故に最後の通信が終わってもそれ以前の通信が終わった保証はどこにもありません。

通信毎に flag を立てたら..

flag を立てることも検討しましたが、Ajax 通信のどの段階で flag を記述すればよいのか、それが問題です。結果を取得できたその時に立つ flag でなくては意味がありません。ではどこに?

jQuery を活用する前提で考えてみると、なかなか適切な回答が見いだせないまま、空しく月日が流れました。

1 つの光明は jQuery.active プロパティの活用だった

flag をどの様にして立てようか思案する中で、改めて jQuery.js の Ajax 通信部分を眺め回し、また上記で紹介した書籍の Ajax 通信部分を見てみて、jQuery.js の Ajax 通信コード部分に既に flag が立てられていることを知りました。

それはクラス jQuery の 1 つのプロパティであり、コード実行時に Ajax 通信数を数えるカウンターとして利用される jQuery.active です。

そのカウンターは jQuery.ajax() メソッド内で活用されますが、その値は Ajax 通信を行おうとする度に、send コマンド実行直前に 1 だけ増加し(ver1.2.6 の 2650 行)、結果を取得し終えると 1 だけ減じられ(ver1.2.6 の 2835 行)ます。つまり、連続して Ajax 通信を指示すると、その値は順に増加し、それが終わる度に順不同で減じられる仕組みとなってます。(但し、jQuery Ajax 諸定数の1つである global 値を初期値の true ままにしておかなければ、何回 Ajax 通信を行っても、jQuery.active 値は 0 から変化しません。───jQuery.js ver1.2.6: 2650 行と 2835 行)

これを活用すれば、連続通信を開始した後にその値が 0 になるタイミングを捉えて取得結果をまとめて表示することが出来るはずだ、と推測し、実際にその推測は当たっていました。

jQuery.active プロパティを利用することにより、連続した Ajax 通信が完了したことを Javascript に知らせることの出来る flag を獲得することが出来たのです。

というよりも、正確に言えば、そもそも jQuery.active プロパティはそのような目的で作成されたもの、と言えるでしょう。

jQuery.active 利用と Ajax globalイベントの関係に係る若干のコメント

jQuery.active 利用しようとする時に、必ずしも Ajax global イベントを利用する必要はありません。global 値を true としておかなければ、jQuery.active は利用できませんが、それを利用するために Ajax global イベントを使う必要はありません。

jQuery.js の初期値のままで global 値は true となっているので、何も操作することなく「local callback」関数の中で jQuery.active を活用できます。

timerを使った jQuery.active 活用の実際

1回の Ajax 通信の場合には jQuery.active を活用するまでもなく、容易に結果を取得/表示出来ますし、複数回の通信の場合でも「概ね」問題なく結果を取得/表示できました。

これまでに試みてきた全ての自作 Ajax local コード(頁トップに掲載した関連エントリイ参照)において、jQuery.active が全てで目的通りに作動すること、つまり、第一に jQuery.active ==0 が全ての Ajax 通信が終了したことを意味し、第二に、その値になれば全ての Ajax 通信結果が取得出来ていることを確認しました。

jQuery.active 活用の要点

Ajax 通信コード部分より下に、以下のように同一関数の間欠起動を設定し jQuery.active 値がゼロになる迄 Ajax 通信完了を待機し続け、ゼロになってから(つまり全ての Ajax 通信が終わってから)結果をまとめて表示すれば良い訳です。

 function display(){
  if (jQuery.active==0) {
   if (timer) {clearInterval(timer);timer=null;}
   ・・・ここに全ての通信結果を表示するためのコードを記述する。・・・
  }
 }
 var timer = setInterval(display,milsec);
しかし、1 回の jQuery.active 活用だけではまだ不十分だった

ところが、1 回の jQuery.active の活用だけではどうしても全ての Ajax 通信結果を一括表示出来ないケースが発生しました。つまり、一括取得は出来ているのに、一括表示が出来ない場合が生じたのです。

そのケースとは、4 つめのチャレンジとして試みた、任意の Fc2 ブログの任意のエントリイの以前/以後/最新エントリイタイトルを取得するコードの場合でした。当然、そのような現象が発生する理由について、一人悶々とと分析しました。分析し続けました。数ヶ月に亘って・・・。

そしてやっと到達した要点は次のような、今更ながらの基本的なことでした。

  • 第一に、( 分かってみれば余りに当然のことですが )連続する Ajax 通信中において、先行する通信コードの中で設定した変数を、後続する他の通信中で活用しようとする時、先行する通信の中で変数が取得できていない時点で、後続する通信コードの中でそれを利用しようとすれば、当然エラー( 値は undefined となる)となること
  • 第二に、最も確実な連続 Ajax 通信コードの書き方は、先行する通信が終わったことを確認する何らかの flag を設定し、それを確認してから次の Ajax 通信が進行するようにコードを記述すればよいこと
  • 第三に、しなしながらその方法を採用すると、全体の通信完了までに相当の長時間を要してしまい、実用的ではないこと
  • 第四に、従って jQuery.active を複数回活用して各 Ajax 通信完了 を待機させれば、確実に各通信完了を javascript に認識させられること

───などでした。

そこで原点に返って可能な限り Ajax メソッド内で行ってみた

上記の再確認した基本的事項を踏まえて、Ajax 通信コードの外であれこれ操作して Ajax 通信結果を取得する試みがどうしても巧くいかないことから、原点に返って全てを Ajax 通信メソッドの内で行ってしまった方がよいのではないか、と考え直しそれを実行してみました。

すると驚いたことに、あるいは当然の結果なのかも知れませんが、これまでの悩みが嘘のようにあっさり解決してしまったのです。

詳細は以下のエントリイに記述しました。

FireBugLite における mousemove/mouseup イベントについて

FireBugLite のブックマークレットによる組み込み

firebug Liteを組み込みたいサイトに対して、一々その html を加工して firebug Liteをインクルードするのは面倒ですから、誰しもそのインクルードをブックマークレット等によって容易に行えるようにしたい、と考えるでしょう。

そして過去に、それを実現しているサイトを見つけたのですが、最近そのブックマークレットが利用しているファイル置き場が消失してしまい、当該ブックマークレットが使えなくなってしまいました。(WebOS Goodies:Firebug Lite をブックマークレット化しました - WebOS Goodies

そこで WebOS Goodies に倣って、 GooglePages に Firebug Lite 関連ファイルを置き、自作ブックマークレットを活用しようと思い立ったのです。

ところが、Firebug Lite サイトからダウンロードしたファイルを単純に解凍し、解凍後のファイルをファイル置き場として使用する Google Pages にアップロードし、それを利用したブックマークレットを作ってみても、それだけでは Firebug Lite は使えませんでした。

その原因を探るべく WebOS Goodies の「Firebug Lite をブックマークレット化しました」頁をよく見てみると、firebug.js を多少加工して upload している、等のブックマークレット作成上の留意点(※)が書いてありましたので、それを真似て firebug.js に必要な加工を施して upload してみたのです。すると、確かに Firebug Lite の起動には成功したのですが、新たに次の課題が出現しました。

Firebug Lite をブックマークレット化しました - WebOS Goodies からの引用
  • firebug.html の内容を文字列として firebug.js に埋め込み、IFRAME に document.write で流し込むように変更。
  • CSS の URL を変更。
  • ファイルを Google Page に保存する都合で、見分けやすくするためにアイコンのファイル名を変更。
  • Opera できちんと動くように、 z-index の値を小さめ(65536)に変更。
  • こちらの記事のアイディアを参考にして、改行が反映されるように writeRow 関数で生成するタグを pre に変更。ただし、 IE では効果がないようです(謎)。

Opera では問題なし。しかし IE では・・・

Opera では狙い通りに FirebugLite が組み込まれ作動しました。jQuery.js とのダブルインクルードも成功し、FirebugLite 上で jQuery.js を利用することも出来ました。

しかし IE においてはマウスカーソルを動かすだけで、Firebug Lite によって用意されるインラインフレームの高さが上下してしまうため、思うように使えないのです。

その原因を探るためFirebugLite.jsを紐解いてみたところ、原因は 202 行の「toolbar.onmousedown = onSplitterMouseDown;」にあることが判明しましたので、この行をコメントアウトしました。つまり、toolbar 上のマウスダウンイベントを無効にしてしまった訳です。

これにより、202 行によって登録されることになる onSplitterMouseMove 及び onSplitterMouseUp イベントも同様に無効になるわけですが、不如意なフレームサイズ変更は操作上大きな支障を来すためやむを得ない処置であると思います。

また、frame内に表示させるために用意されている firebug.html も不要なので、217行もコメントアウトしました。

▲ToTop

改訂した FirebugLiteを Google-Page 上に置く

こうした改訂を施した firebug.js を Google-Page 上に置いて活用するようにしたのです。

Fc2ブログの場合、ファイル置き場を認めていませんから、 Google-Page 上に置いて活用するようにしたことにより、任意の Web サイト上において FirebugLite を活用できるようになった訳です。

後はこれをインクルードする問題が残るだけですが、それについては別項( Query と FirebugLite を一括登録するブックマークレット: )でまとめました。

ファイル置き場と Ajax 通信を活用して、任意のFc2ブログから情報を得る

  • 2008/3/12 : 初稿
  • 2008/7/9 : コードの抜本改訂とファイル置き場利用版up
  • 2008/7/12 : Ajax 通信関連部分を更に抜本改正
  • 2008/7/20 : Ajax 通信関連部分で確実性を更に向上(ここで Ajax 関連コードは完成し、これ以降はあれこれのユーザビリティの向上対策を施した。)
  • 2008/7/21 : id 及び class 名称を他の Fc2 ブログで使われていないと推定される固有のものに変更
  • 2008/8/12 : 各 Ajax 通信所要時間を記録し閲覧できるように変更。このプロジェクトの削除(関連cssファイルやjsファイルの登録削除)と隠蔽(単にpopup小窓を隠すだけ)を分け、各ボタンを配置。
  • 2008/8/14 : responseText の 文字化け対策として、或る <meta/> タグを追加するように変更

他ブログ上での Ajax 通信をついに実現( 2008/7/9 記 )

上の関連エントリイでつらつらと述べてきた課題──すなわち他の任意のFc2ブログサイト上において、ブックマークレットによって必要なスクリプトをインクルードし、当該スクリプトによる Ajax 通信によって当該ブログの情報を取得し、それを当該ブログ上に表示する──が、やっと解決しました。( 8/12 完結 )

上記 「 Related Entries in this Blog 」 の No1エントリイで計画したことが、No.2 エントリイで述べた、過去/未来/最新エントリイタイトル取得/表示を行う Ajax 通信の成功と、No.3 とNo.4 エントリイで述べたファイル置き場問題の解決によって実現したのです。

何はさておき、結果を画像で紹介することが最も分かりやすいでしょう。( 2008/8/14 追記 )

サンプル 1【 Fc2総合インフォメーションブログ 】

下の画像は、Fc2総合インフォメーションブログ上でこのプロジェクトを実行した際の表示結果抜粋です。

Fc2 総合インフォメーションブログ上で Ajax 通信によって最新/以前/以後エントリイのタイトル情報などを取得した結果を示すサンプル画像 1
サンプル 2【 関西ZIGZAG 】

下の画像は、人気ブログ「 関西 ZIGAZAG 」ブログ上でこのプロジェクトを実行した際の表示結果の抜粋です。

関西 ZIGAZAG ブログ上で Ajax 通信によって最新/以前/以後エントリイのタイトル情報などを取得した結果を示すサンプル画像 2
サンプル 3【 FCafe 】

下の画像は、自ブログ開設時からお世話になってきたサイト上でこのプロジェクトを実行した際の表示結果の抜粋を示すものです。

FCafe ブログ上で Ajax 通信によって最新/以前/以後エントリイのタイトル情報などを取得した結果を示すサンプル画像 3

今回のプロジェクト「Fc2ブログ エントリイ情報取得」の概要

今回作成したコード群は、任意の Fc2 ブログ上で jQuery を活用してエントリイタイトル等を取得するツールです。また併せて jQuery.js と ( Firefox 以外の browser の場合には )firebugLite もインクルードするようにしたので、IE や Opera などにおいても jQuery と firebugLite を使用し、スクリプトテストや DOM 操作を行うことも可能としました。

つまり、或る Fc2 ブログサイトの、或る個別エントリイ頁を開いた状態でブックマークを起動すれば、以前、最新及び以後の各 10 個のエントリイに係る諸情報(具体的にはタイトル、Entry番号、投稿日など)が閲覧できると共に、そのページ上で jQuery.js と firebugLite.js を使って Javascript コードの実行が出来るわけです。

プロジェクトのファイル構成( 2008/7/9 記 )

コード群はブックマークレットを含めて以下の6つから構成されます。

  1. ブックマークレット……次の setjQnFbug_getFc2EnTts.js を登録するため
  2. スクリプト登録用JSファイル( setjQnFbug_getFc2EnTts.js )……今回のプロジェクト実行に必要な以下の 4 つの全てのファイルを、今閲覧中の任意の Fc2 サイトのHTML文のヘッダー部に登録するための、短い Javascript コードファイル
  3. 上の 2. のファイルで登録するファイルは以下の 4 つです。
    1. jQuery.js、
    2. firebug.js、
    3. getFc2EntryTitles-gp.js( Ajax 通信用オリジナルJSファイル)
    4. getFc2EntryTitles-gp.css(通信結果表示用オリジナルスタイルシート……Ajax通信結果を固有に用意した絶対配置要素内で表示させるために、各ブログに依存しない固有のスタイルシートを用意)

ここに、このプロジェクトでは Ajax 通信処理をはじめとして、随所に jQuery.js を多用したコードを記述し実行しているため、jQuery.js が組み込まれていないブログの場合も想定し(大多数は組み込まれていないと思われる)、なければそれをインクルードするようにしました。

また firebugLite も組み込むようにしたので、IE や Opera 上でも Javascript のテストが容易に出来るようにしました。

以上により、Fc2のブログであれば何であれ、個別エントリイ表示モードの場合において、jQuery を活用して Ajax 通信を行い、エントリイタイトル情報を取得すると共に、firebugLite を使った Javascript の走行テストが出来るようにしました。

▲ToTop

ファイル置き場は Google Page を活用( 2008/7/9 記 )

ファイル置き場に出来るかも知れない、と考えた Just Sysytem 社のインターネットディスクは、同社への問い合わせの結果、使えないことが判明しました。また SONY の WebPocket にも問い合わせましたが、こちらも駄目でした。

有料なのに、それでもなお、ファイル置き場を認めない理由は全く理解できません。

確かに「 アクセスが集中する、ウィルスファイルを置かれる可能性も否定できないから踏み台とされる、つまりサービスの利用よりも悪用に手を貸すことになる 」等の否定的な側面が懸念されます。そしてこの懸念から解放されるためには、ファイル置き場を認めないことが安易な方法なのでしょう。

───と、いくら批判し、嘆いていても仕方ないので、調べた中で、無料で容易にファイル置き場として利用できる Google Page ウェブサイトを活用することにしました。Google page 上にマイページを立ち上げ、そこに上記 6 つのファイルの内ブックマークレットを除く 5 つのファイルを GooglePage サイトに upload し、自作ブックマークレットを作動させてその稼働を半年以上に渡って確認しています。

※ この少し下に成功したブックマークレットを置きました。

このプロジェクトの利用方法( 2008/3/12 記 )

ブックマークレット

以下のブックマークレットを(ブックマーク又はお気に入りに登録し)クリックすれば、関連する JS ファイルや CSS ファイルが head タグ内に追加され、固有の絶対配置要素が画面上部に表示されます。FireFox 、 Opera 及び IE7 で動作確認済みです。

後は Before・・・、Recent・・・、After・・・ などのボタンをクリックすれば、所定の情報が絶対配置要素内に表示されます。

Ajax 通信をやり直すボタン( ReLoad Data ボタン )や、Ajax 通信に要した時間を知ることが出来るような Log 閲覧ボタンも設置しました。また、結果を表示する小窓が邪魔になったら、隠蔽することも出来るし、ブックマークレットによってインクルードした関連スクリプト・cssファイルを削除するボタンも設置しました(但し jQuery.js と firebug.js は残すようにしました)。

こうして、Ajax通信結果を示す小窓が邪魔になったら隠蔽し、ブックマークレットを再起動すれば、隠蔽直前の状態で小窓が復帰するようにすると共に(Ajax 通信結果は閲覧中の頁がリロードされない限りメモリ上に残る仕様としたのです)、インクルードファイルが邪魔になったら簡単に剥離・削除出来る仕様にしました。

次のブックマークレットをお気に入りに登録すれば、今回のプロジェクトを任意の Fc2 ブログサイトで共通して使うことが出来ます。また、単に以下のブックマークレットをクリックするだけでも、今ご覧戴いているこのエントリイを対象としてブックマークレットを利用できます。

このプロジェクトの起点となるブックマークレット

※ 2008/8/11迄上記ブックマークレットのコードに一部ミスがあり機能しませんでした。利用しようとしてくださった方々には大変ご迷惑をお掛けいたしました。

Ajax 通信結果の表示

ブックマークレットをクリックすると、次のような小窓がブラウザの上部に表示されます。

Ajax 通信結果を表示する絶対配置要素の図

上の図は既に Ajax 通信が終わった状態の画像ですが、7つのボタンは左から順に「(1)今見ているエントリイの前の10個のエントリイタイトル表示、(2)最新の10エントリイ表示、(3)今見ているエントリイの後に投稿された10エントリイのタイトル表示、(4)Ajax通信再実施、(5)所要時間表示閲覧、(6)小窓の隠蔽、(7)関連スクリプトやcssファイルの削除」を行うためのものです。

例えば 「 Recent Entries 」 ボタンをクリックすると下図のようにタイトル名とエントリイナンバーが表示されます。Ajax 通信をやり直すことができるように設けた 「 Relaod Data 」 ボタンは、何らかの事情で取得結果がおかしかった場合などに Ajax 通信をやり直すことが出来るように配置したものです。このボタンをクリックすれば Ajax 通信に要した時間を比較して知ることも出来ます。

Ajax 通信結果である過去エントリイ10タイトルを見ている図
表示エントリイ上で jQuery と Firebug Liteを活用!

単に、エントリイタイトルを閲覧するだけでなく、このプロジェクトでは jQuery.js と FirebugLite もインクルードしますので、IE や Opera において、ちょっとした Javascript 利用が(デバッグ環境とまではいかないが、テストや学習に活用出来る)、エントリイ頁上でリアルタイムに行えるようになります。F12 キー又は CTRL+Shift+Lキーを押せば firebug Lite を起動することが出来ます。

▲ToTop

Javascript コードおよびCSSファイル説明( 2008/3/12 初稿:その後漸次改訂。確定は2008/8/14 )

1. スクリプトをインクルードするためのブックマークレット

ブックマークレットは各種のスクリプトや CSS ファイルをインクルードするための、いわば初期化スクリプトです。たった1つのスクリプトの存在をチェックし、存在していなければインクルードします。

そもそも全てのコードをブックマークレットに記述できればそれが最も合理的なのですが、ブックマークレットにはブラウザ毎に文字数制限があり、ここで行ったことはとてもその制限値内には収まらないので、複雑な手続きを経なければなりませんでした。

<a href="javascript:
(function(){
  // 文字数を減らすために変数を纏めて指定
  var%20x=0,i=0,
  h=document.getElementsByTagName('head')[0],
  t=document.getElementsByTagName('script'),
  s=document.createElement('script');
  // headタグ内を検索して
  if(t) for(;i<t.length;i++) 
    // setjQnFbug 文字の有無を走査し、有れば x に 1 を代入
    if(/setjQnFbug/.test(t[i].src)) x+=1;
    // setjQnFbug 文字が見つからなければ所定のjsファイルをインクルードする
    if(x==0){
      s.src='http://hkom007.googlepages.com/setjQnFbug_getFc2EnTts.js';
      s.type='text/javascript';
      // headタグ内にスクリプトタグを追加
      h.appendChild(s);
    // 既に所定のjsファイルがインクルード済みならば所定の関数(chkScript)を起動する。
    // この関数は3つのjsファイルと1つのCSSファイルのインクルードを行うためのもの
    }else%20chkScript();
})()">set jQuery & FirebugLite & getFc2EntryTitles.js</a>

▲ToTop

2. Javascript 及び css ファイルの一括インクルードを行う

setjQnFbug_getFc2EnTts.js は 4 つのファイルを一括してインクルードするコードだけを記述した 小さな JS ファイルです。といってもブックマークレットにするには文字数が多すぎてIEで読み込めないため、ブックマークレットとは別に設置した訳です。このファイル作成の当初段階では、インクルードするだけではなく、その後の処理も1つのファイルに纏めようとしましたが、それは無理でした。

何故ならば、インクルードのためのコードに続けて、インクルードされたファイルを使用して様々な処理を行うコードを書いて実行すると、インクルードが中断されてしまうからです。

ブラウザがファイルをインクルードする処理を行っている間に、JS インタープリタが次の処理を行おうとすると、インクルードそのものが中断されてしまうのです。

そのためインクルード専用のファイルと、インクルードされたファイルを利用して諸処理を実行するコードを記述したファイルとを別々に分けました。

// このスクリプトがインクルード済みの場合に関数が起動できるよう
// 名前付き関数とし、最後の行でこの関数を起動する。
var chkScript = function(){
  var v=0,w=0,x=0,y=0,z=0,i=0,
    h=document.getElementsByTagName('head')[0],
  m=document.getElementsByTagName('meta'),
    t=document.getElementsByTagName('script'),
    l=document.getElementsByTagName('link'),
    s=document.createElement('script');
  // 文字化け対策 
  // http-equiv="content-type" かつ content="application\/x-javascript charset=euc-jp"であるmetameguがあるかどうか走査
  // 存在すれば v 値を加算する。 
 if(m) for(;i<m.length; i++){
  if(/content-type/.test(m[i].getAttribute("http-equiv"))
   && /application\/x-javascript charset=euc-jp/.test(m[i].content)) v+=1;
 }
  // v 値がゼロならば meta タグを追加する。 
 if (!v){ 
  var k=document.createElement('meta');
  k.setAttribute("http-equiv","content-type");
  k.content = "application/x-javascript charset=euc-jp";
  h.appendChild(k);
 }
  // 所定のスタイルシートがインクルード済みかどうか link タグ走査し、あれば記録。
 if(l) for(i=0 ;i<l.length; i++) {if(/getFc2EntryTitles/.test(l[i].href)) w+=1}
  // CSSファイルのインクルード
 if(!w) {
  var k = document.createElement('link');
  k.rel = "stylesheet";
  k.type = "text/css";
  k.media = "screen, print";
  k.href='http://hkom007.googlepages.com/getFc2EntryTitles-gp.css';
  h.appendChild(k);
 }
  // jquery、firebug 及び getFc2EntryTitles の文字を含むスクリプトタグを走査し、
  // 存在していればそのことを記録する。
 if(t) for(i=0;i<t.length;i++){
  if(/jquery[^u][^i]+/.test(t[i].src)) x+=1;
  if(/firebug/.test(t[i].src)) y+=1;
  if(/getFc2EntryTitles/.test(t[i].src)) {
      // ブックマークレットが複数回起動された場合の処理
      // インクルード済みで結果表示小窓が非表示ならば表示する
      var tmp = document.getElementById("ajaxPopup");
      if (tmp && tmp.style.display == "none") tmp.style.display="block";
    } // 結果表示小窓が表示済みならば、何もしないでif文を終える。
 }
  // 以降で反復利用する文字列を定義
 s.type='text/javascript';
  // jQuery.js のインクルード
  // jQuery.js は単に firebug Lite から利用するだけではなく、Ajax通信や
  // その取得結果を表示するコードで多用します。
  // なお、min 版を利用することにしました。
 if (!x){
  s.src='http://hkom007.googlepages.com/jquery126min-gp.js';
  h.appendChild(s);
 }
  // firebugLite のインクルード
 if(!/firefox/.test(navigator.userAgent.toLowerCase()) && y==0){
  var c=document.createElement('script');
  c.src='http://hkom007.googlepages.com/firebug-gp.js';
  c.type=s.type;
  h.appendChild(c)
 }
  // Ajax 通信を行う本体コードの getFc2EntryTitles.js のインクルード
 if (!z){
  var r=document.createElement('script');
  r.type=s.type;
  r.src='http://hkom007.googlepages.com/getFc2EntryTitles-gp.js';
  h.appendChild(r);
 }
  // 無名関数としなかったのは再呼び出しがあり得るからです。
};chkScript(); //関数実行

▲ToTop

3. 結果を表示する小窓用 CSS ファイル

取得結果を絶対配置要素内に表示させるために、CSS ファイルを作成しました。これによりどんな Fc2 ブログであっても Ajax 通信結果を同様に表示するようにしました。

なお、任意のFc2ブログで活用できるようにするには、id や class の名称に工夫が必要であることを、いくつかのサイトで試してみて納得しました。このプロジェクトで利用する名称が、閲覧しているブログサイト内で利用されてる名称と重複してしまってはいけない、ということです。

このことは振り返れば余りに当たり前のことですが、実行して初めて分かった次第です(^_^;)。

経緯はこうでした。( 2008/7/21 追記 )

FC2総合インフォメーション 【ブログ】画像ファイル挿入の仕様変更のお知らせ でこのプロジェクトを試してみたのですが、その際に当該サイトで使用されている id 名称( #menuBlock )が、このプロジェクトで利用していた id 名称と重複していたため、当該サイトでは予想外の挙動が起きてしまいました。

そこで初めて、任意の Fc2 サイトと id や class 名称が干渉しないよう、このプロジェクトでは他には絶対に存在しないであろう、固有の名称を使用しなければならない、と気がついた訳です。

こうして id 及び class 名称は以下にあるように長たらしいものとなりました。全ての id 及び class 名称に、「 getEntryTitles_ 」なる接頭語を付けたのです。これで「おそらく」世界で唯一の名称になったのではないか、と一人合点しています。

更に CSS ファイルでは重要なことがあります。( 2008/8/17 追記 )

それは自分のブログで様々な CSS コードを書いている限り全く問題とはならなかったことであり、他のサイト上で自分が書いた CSS ファイルを適用する際には、必ず注意しなければならない問題です。

その問題とは CSS ファイルの優先順位です。

ブラウザにはそれぞれ固有のスタイル値があり、また当然のことですがそれぞれのブログには、作者固有の、またはテンプレート固有のスタイル値があります。そして私が考え、CSSファイルで表現したスタイル値があります。これらの優先順位についてこれまで全く考慮する必要がなかったのですが、他の方のブログ上で MyCSS ファイルを適用することになった時点で、即座にこの CSS ファイルの優先順位が切実な課題となってきたのです。

そこで数年ぶりに改めて CSS の学習を思い起こし、また一部再学習して !important 属性を使うことに到達しました。

@charset "euc-jp";
	#getEntryTitles_pasteData ul { /* important によって任意の Fc2ブログにおいて同一の */
		margin:0 0 0.5em 1.5em !important; /* 結果を得るようにした。*/
		padding:0 !important; /* ここに到達するまでに結構悩んでしまった。*/
		clear:both !important;
	}
	#getEntryTitles_pasteData ul li{
		margin:0 !important;
		padding:0 !important;
	}
	#getEntryTitles_ajaxPopup{
		position:absolute; z-index:100000; top:150px;left:50%;
		width:0; height:0; margin:0px;
		font-size:small;
		text-align:left;
		color:black;
		display:none;
	}
	#getEntryTitles_menuBlock {
		padding:10px;
		width:0px; height:0px;	/*not auto*/
		background-color:#def;
		border:2px #777 solid;
		display:none;
	}
	#getEntryTitles_loading {
		clear:both;
		padding:5px 10px;
	}
	#getEntryTitles_pasteData {
		clear:both;
		margin-top:-28px;
		display:none;
	}
	#getEntryTitles_pasteData a:link{color:blue; text-decoration: none;}
	#getEntryTitles_pasteData a:visited { color: purple; text-decoration: none;}
	#getEntryTitles_pasteData a:hover {
		color:darkgreen;
		background:#dd0; 
		text-decoration: none; 
	}
	#getEntryTitles_pasteData a:active { color: lightblue; text-decoration: none;}

	#getEntryTitles_finish, #getEntryTitles_resultbefore, #getEntryTitles_resultrecent,#getEntryTitles_resultafter {
		padding:0 10px 10px 10px;
		background:#def;
		border-left:2px #777 solid;
		border-right:2px #777 solid;
		border-bottom:2px #777 solid;
		display:none;
	}
	.getEntryTitles_btn {
		width:120px;
		float:left;
		margin:0 2px;
		padding:2px;
		border:1px black solid;
		font-weight:bold;
		color:white;
		background-color:#89A;
		text-align:center;
	}
	#getEntryTitles_viewLog {
		float:left;
		width:50px;
		margin:0 2px;
		padding:2px;
		border:1px black solid;
		font-weight:bold;
		color:white;
		background-color:#89A;
		text-align:center;
	}
	#getEntryTitles_hideThis, #getEntryTitles_removeThis {
		float:right;
		width:20px;
		margin:0px 2px;
		padding:2px;
		border:1px black solid;
		font-weight:bold;
		color:white;
		background-color:#89A;
		text-align:center;
	}
	#cmtbtn1{
		text-align:center;
		margin-top:-2em;
		display:block; width:16em; float:right;
	}
	#cmtbtn2{
		text-align:center;
		border:1px solid darkgray;
		padding:2px;
		margin-top:-2em;
		width:12em; float:right;
	}

▲ToTop

4. メインの JS コード( Ajax通信実施、結果表示等全ての処理を実行する )

Ajax 通信処理、取得結果表示処理などを行うメインコードを書いたファイルです。

必要なファイル全てのインクルードを行ってから、getFc2EntryTItles-gp.js によって Ajax 通信処理を行い、その取得結果を表示中のブログ内に表示します。

結果を表示する小窓にはスクロールイベントを登録し、スクロール時にも常に画面上部に表示され続けるようにしました。

  1 :/* getFc2EntryTItles-gp.js
  2 : * 2008/7/21 Release
  3 : * 2008/8/12, 2008/8/17 update
  4 : */
  5 :// fc2ブログでかつ個別 Entry 表示モードの場合にのみコードを進行する。
  6 :if (location.href.indexOf("fc2")!=-1 && location.href.indexOf("blog-entry-")!=-1){
  7 :(function($){
  8 : var now=function(){return +new Date;}; // 時刻取得関数
  9 : $.extend({ // 時刻・timer起動回数記録用 jQuery 拡張オブジェクト
 10 :  tr:{
 11 :   start:now(),
 12 :   registerEvent:"",
 13 :   ajax:{ recent:[],before:[],after:[]},
 14 :   end:"",
 15 :   waitRecentAjaxCnt:0,setEndingCnt:0
 16 :  }
 17 : });
 18 : // ローカル変数定義
 19 : var aP,mB,ld,pD,fn,lg,r_c,r_b,r_a, constStr="getEntryTitles_";
 20 : var html = { before:[], after:[], recent:[] },getStr ={ before:"", after:"", recent:"" },
 21 :  blogTitle =null, regExpr = "",border={ before:0, after:0},realElm={before:0,after:0},thisEntryNo, lastNo, itval,complement,cmt=0;
 22 :
 23 : // 取得結果表示用のタグを作り表示する。
 24 : if ( $("#"+constStr+"ajaxPopup").size()==0) {
 25 :  $(document.body).append(
 26 :   '<div id="getEntryTitles_ajaxPopup">'+
 27 :    '<div id="getEntryTitles_menuBlock">'+
 28 :     '<button id="getEntryTitles_before" class="getEntryTitles_btn" title="このエントリイより前のエントリイのタイトル情報を見る">Before Entries</button>'+
 29 :     '<button id="getEntryTitles_recent" class="getEntryTitles_btn" title="最新エントリイのタイトル情報を見る">Recent Entries</button>'+
 30 :     '<button id="getEntryTitles_after" class="getEntryTitles_btn" title="このエントリイより後のエントリイのタイトル情報を見る">After Entries</button>'+
 31 :     '<button id="getEntryTitles_reload" class="getEntryTitles_btn" title="Ajax通信をやり直す">ReLoad Data</button>'+
 32 :     '<button id="getEntryTitles_viewLog" title="Ajax 通信の所要時間を見る">Log</button>'+
 33 :     '<button id="getEntryTitles_removeThis" title="このプロジェクトの関連スクリプトやcssを一気に削除する。">'+decodeURI(encodeURI("×"))+'</button>'+
 34 :     '<button id="getEntryTitles_hideThis" title="この小窓を隠蔽する。再表示はブックマークレットを再度クリックすれば良い。">-</button>'+
        // 通信中であることを表示するタグ
 35 :     '<div id="getEntryTitles_loading"><img src="http://hkom007.googlepages.com/loading_16.gif" width="16" height="16" border="0" alt="" /> Now Loading...</div>'+
 36 :    '</div>'+
 37 :    '<div id="getEntryTitles_pasteData">'+
        // 連続する Ajax 通信の全てが終わったことを表示するタグ
 38 :     '<div id="getEntryTitles_finish"><div><strong>Finish Ajax Communication !</strong></div><div id="getEntryTitles_log"></div></div>'+
        // 以前エントリイタイトル名等を表示するタグ
 39 :     '<div id="getEntryTitles_resultbefore"></div>'+
        // 最新エントリイタイトル名等を表示するタグ
 40 :     '<div id="getEntryTitles_resultrecent"></div>'+
        // 以後エントリイタイトル名等を表示するタグ
 41 :     '<div id="getEntryTitles_resultafter"></div>'+
 42 :    '</div>'+
 43 :   '</div>'
 44 :  );
 45 : } else {
 46 :  $("#"+constStr+"ajaxPopup").show();
 47 :  return;
 48 : };
 49 :
 50 : // 表示/非表示を操作するために必要な要素をショートカット変数に代入する。
 51 : aP=$("#"+constStr+"ajaxPopup");
 52 :  mB=$("#"+constStr+"menuBlock");
 53 :    ld = $("#"+constStr+"loading");
 54 :  pD=$("#"+constStr+"pasteData");
 55 :    fn = $("#"+constStr+"finish");
 56 :       lg =$("#"+constStr+"log");
 57 :    r_c = $("#"+constStr+"resultrecent");
 58 :    r_b = $("#"+constStr+"resultbefore"); r_a = $("#"+constStr+"resultafter");
 59 :
 60 : // 結果表示ポップアップに対するアニメーション関数。真ん中から吹き出すような効果を狙った。
 61 : var doExplodeShrink = function(elem,w,h,padbdr,left,state){
 62 :  elem.show().animate({
 63 :   width: w=="auto" ? "auto" : w+"px", height:h=="auto"?  "auto" : h+"px",
 64 : //  display:state, // IEでは機能しないためやむなく削除
 65 :   marginLeft:left ? (-parseInt(w/2)-padbdr+"px") : 0
 66 :  },400,"swing");
 67 : };
 68 :
 69 : // イベントハンドラー登録
 70 : $.tr.registerEvent=now();
    // 固定配置的に配置するためのスクロールイベントハンドラー
 71 : $(window).scroll(function(){
 72 :  $("#"+constStr+"ajaxPopup").css({
 73 :   top:10 +( window.pageYOffset || Math.max(document.body.scrollTop, document.documentElement.scrollTop) )+"px",
 74 :   marginLeft:-320+ ( window.pageXOffset || Math.max(document.body.scrollLeft, document.documentElement.scrollLeft) )+"px"
 75 :  });
 76 : });
 77 :
    // 最新/過去/未来エントリ情報を表示させるクリックイベントハンドラー
 78 : $.each([$("#"+constStr+"recent"),$("#"+constStr+"before"),$("#"+constStr+"after")],function(j){
 79 :  $(this).click(function(){
 80 :   $([fn,r_c,r_b,r_a]).each(function(i){
 81 :    i==j+1 ? $(this).show() : $(this).hide();
 82 :   });
 83 :   this.blur();
 84 :  });
 85 : });
 86 :
    // Ajax通信をやり直すボタン
 87 : $("#"+constStr+"reload").click(function(){
 88 :  $.tr.start=now();
 89 :  this.blur();
 90 :  pD.hide().children().hide();
 91 :  mB.hide();
 92 :  doExplodeShrink(aP,0,0,0,true,"none");
 93 :  ld.show();
 94 :  makeLists(10);
 95 : });
 96 :
    // Ajax通信に要した時間を見る為のボタン
 97 : $("#"+constStr+"viewLog").click(function(){
 98 :  $([r_c,r_b,r_a]).each(function(){$(this).hide();});
 99 :  fn.show();
100 :  this.blur();
101 : });
102 :
    // 小窓を一時的に隠蔽するボタン
103 : $("#"+constStr+"hideThis").click(function(){
104 :  this.blur();
105 :  aP.hide();
106 : });
107 :
    // プロジェクトに係るjsファイルやcssファイルを削除するボタン
108 : $("#"+constStr+"removeThis").click(function(){
109 :  this.blur();
110 :  aP.remove();
111 :  $("script[src*='getFc2EntryTitles']").remove();
112 :  $("script[src*='setjQnFbug']").remove();
113 :  $("link[src*='getFc2EntryTitles']").remove();
114 : });
115 :
116 : mB.children().hover( // ボタンにマウスオーバー/アウトした際のイベントハンドラー
117 :  function(){
118 :   $(this).css({color:"black",backgroundColor:"#dd0"});
119 :  },
120 :  function(){
121 :   $(this).css({color:"white",backgroundColor:"#89A"});
122 :  }
123 : );
124 :
125 :// 最近のタイトルを取得する関数を定義
   // limit 個数のエントリイタイトル情報を Ajax 通信によって取得する。
126 :var makeRecentEntryList = function (limit){
127 : var No, subject, date, iter=0, ret=[],
128 :  target ={  //xml ファイル内での順番
129 :     link:[],     //0
130 :   title:[],     //1
131 : // description:[],  //2 これは利用しない
132 : // content:[],    //3 同上
133 :    subject:[],    //4
134 :   date:[]      //5
135 :  };
136 :
137 : // エントリイタイトルを取得する
138 : $.ajax({
     // 閲覧中のFc2ブログの xml ファイルのアドレスを設定する。
139 :  url: /(http:.+fc2\.com\/.*)blog-/.exec(location.href)[1] + "?xml" || null,
140 :  type: "GET",
141 :  dataType: "xml",
     // xmlファイルが成功裏にダウンロード出来た場合の処理関数
142 :  success: function(xml){
143 :   var tmpStr = '<div>'+decodeURI(encodeURI("最新のエントリイ情報がありません。"))+'</div>';
144 :   if (xml==null) {getStr.recent = tmpStr; return;}
145 :   // Blogタイトルを取得する
146 :   blogTitle = $(xml).find("title").eq(0).text();
147 :   // 最新エントリイ情報を巡回取得
148 :   $.tr.ajax.recent["start"]=now();
149 :   $.each(target,function(key){
150 :    $.each($(xml).find("item"), function(i,n){
        // 取得結果を ret 配列の i 番目に代入
151 :     ret[i]= [$(n).children().eq(0), $(n).children().eq(1), $(n).children().eq(4),$(n).children().eq(5)];
        // ret配列内のテキスト文字列を抽出して target 配列に代入する。
152 :     target[key].push( ret[i][iter].text() );
153 :    });
       // 次のエントリイ情報を取得するために(46行で初期値ゼロを定義済み)
154 :    iter++;
155 :   });
156 :   try {
       // エントリイ番号、エントリイタイトル、投稿年月日を取得する。
157 :    for (var i=0 ; (i < limit) && target.link[i] ; i++) {
158 :     No = /entry-([0-9]+)/.exec(target.link[i])[1];
159 :     i==0 && (lastNo = Number(No)); // 最新エントリイ番号を取得
160 :     subject =" , " +target.subject[i];
161 :     date =" , " +target.date[i].substring(0,10); // 年月日のみを抽出
        // limit 個の取得結果を表示用に整序して配列 html.recent に代入する。
162 :     html.recent.push( "<li><a href='" + target.link[i] + "' target='_blank'>" + target.title[i] + "</a> (No." + No + subject + date + ")</li>" );
163 :    }
164 :    $.tr.ajax.recent["end"]=now(); // 時刻記録
       // 格納済み配列を HTML 文字列に併合して最終表示用に整序する。
165 :    getStr.recent = "<div><strong>Recent " + Math.min(limit,i) + " Entries</strong></div><ul style='margin-left:1.5em;list-style-type:disc'>" + html.recent.join('') + "</ul>";
       // 最終表示用に整序した HTML 文字列を表示用タグに挿入する。
166 :    r_c.html(getStr.recent);
167 :   } catch (err) { // 何らかのエラーが発生した場合にはその旨を表示する。
168 :    r_c.html(tmpStr);
169 :   }
170 :  } // End of success()メソッ
171 : }); // End of ajax()メソッド
172 :}; //End of makeRecentEntryTitle func
173 :
174 :// 前後のタイトルを取得するための準備を行う
175 :var makeEntryList = function(b_a,limit){
176 : var thisHTTP, getEntryNos=[], thisURL=[]; // ローカル変数定義
    // エントリイアドレス文字列を分解するための正規表現文字列定義
177 : regExpr = /(http:.+entry-)([0-9]+)/;
    // 今開いているエントリイのエントリイ番号を取得する。
178 : thisEntryNo = Number(regExpr.exec(location.href)[2]);
    // 今開いているエントリイアドレスのエントリイ番号前までの文字列を取得する。
179 : thisHTTP = regExpr.exec(location.href)[1];
    // 以前エントリイについてアドレス名にエラーが出ないように
    // この段階では最新エントリイタイトル名取得関数内で設定した。
    // 最新エントリイ番号値は上で取得済みなので、これを活用して
    // 以後番号も存在しない番号を取得しないようにする。
180 : border[b_a] = Math.min(limit+1, b_a == "before" ? thisEntryNo : Number(lastNo)-thisEntryNo+1);
181 : if (border[b_a]==1) thisURL.length=0; // 求める以前エントリイがない場合の処理
182 : else {
     // 求める以前または以後のエントリイが存在すれば
183 :  for (var i=1; i < border[b_a]; i++)
      // そのアドレス文字列を作成し配列に格納する。
184 :   thisURL.push(thisHTTP + (thisEntryNo - (b_a == "before" ? i : -i)) +".html" );
185 : }
186 : realElm[b_a] = 0; // 初期化
187 : // 準備完了! Ajax 通信開始
188 : getTitlesByAjax.call(this, b_a,thisURL);
189 :};
190 :
191 :// Ajax 通信によりエントリイタイトル等を取得する関数の定義
   // b_a は以前のエントリイか、以後のエントリイかを示す文字列
192 :var getTitlesByAjax = function (b_a,thisURL){
193 : if ( thisURL.length != 0 ) {
     // エントリイアドレス毎に巡回処理を行う。
194 :  $.each(thisURL,function(i,aryitem){
195 :   $.tr.ajax[b_a][i]=[];
196 :   $.tr.ajax[b_a][i]["start"]=now();
197 :   $.get(aryitem,function(data){ //data は thisURL[i] の html テキスト文
       // エントリイタイトル部分を抽出するための正規表現文字列を定義する。
198 :    regExpr = /<title>(.*)<\/title>/;
       // ブログタイトル文字列があればそれをエントリイタイトル文字列から削除する。
199 :    var titleStr = blogTitle && regExpr.exec(data)[1].replace(blogTitle,"") || "" ;
200 :    if ( /\S+/.test(titleStr)){ //空白だけのタイトル名は補足しない。
201 :     ++realElm[b_a]; // 取得したタイトル数をカウントする。
        // 1つのエントリイのタイトル名、エントリイ番号を取得

202 :     html[b_a][i]="<li><a href='" + aryitem + "' target='_blank'>" + decodeURI(encodeURI(titleStr)) +" (Entry No." + /entry-([0-9]+)/.exec(aryitem)[1] + ")</a></li>";
203 :    }
204 :    $.tr.ajax[b_a][i]["end"]=now(); // 時間記録
       // 最後の通信処理が終わったらsetEnding()関数をタイマー起動する。
205 :    if (b_a == "after" && aryitem == thisURL[thisURL.length-1]) 
206 :     itval=setInterval(setEnding,20);
207 :   });
208 :  });
209 : } else {if (b_a == "after") itval=setInterval(setEnding,20)}
210 :};
211 :
   // 終了処理関数定義
212 :var setEnding = function (){
213 : $.tr.setEndingCnt++; // 回数記録
214 : if (jQuery.active==0){ // Ajax通信結果が取得出来たならば
215 :   if (itval) {clearInterval(itval);itval=null;} //タイマー変数停止無効化
     // 以前・以後別に処理
216 :   $.each(["before","after"],function(i,b_a){
      // 取得結果がない場合
217 :  if (realElm[b_a]==0)
218 :   getStr[b_a]="<div>"+ (b_a=='before' ? 'Before ' : 'After ') + "Entry "+ decodeURI(encodeURI('はありません。')) + "</div>";
219 :  else {
      // 欠番があった場合の説明文を complement 変数に代入
220 :   complement = (border[b_a]-1 -realElm[b_a]!=0) ? 
221 :    " ( " +decodeURI(encodeURI("欠番があります。")) +" )" : "";
      // 取得結果を HTML 文字列にして変数に代入
222 :   getStr[b_a] = "<div><strong>"+ (b_a=='before' ? 'Before ' : 'After ') + realElm[b_a] + " Entries" + complement +"</strong></div><ul>" + html[b_a].join('') + "</ul>";
223 :  }
     // HTML 文字列化された取得結果文字列を所定のタグに挿入する。
224 :  $("#"+constStr+"result"+b_a).html(getStr[b_a]);
225 :  if (b_a=="after") {
      // Now loading...文字を隠蔽
226 :   ld.hide();
      // 結果表示ボタンの無効化と半透明化を解除
227 :   $(".getEntryTitles_btn").attr("disabled","").animate({opacity:1.0});
      // 以前タイトルの所要時間を設定(ゼロの時にも対応)
228 :   var beforeTime=$.tr.ajax.before.length ? "<li>以前タイトル取得 Ajax 通信所要時間: "+ ($.tr.ajax.before[realElm.before-1].end-$.tr.ajax.before[0].start)/1000 +" 秒</li>" : "";
      // 以後タイトルの所要時間を設定(ゼロの時にも対応)
229 :   var afterTime=$.tr.ajax.after.length ? "<li>以後タイトル取得 Ajax 通信所要時間: "+ ($.tr.ajax.after[realElm.after-1].end-$.tr.ajax.after[0].start)/1000 +" 秒</li>" : "";
      // 所要時間を表示するためのHTML文の作成
230 :   var AjaxLog ="<ul style='margin-bottom:5px'>"+
231 :    "<li>クリック後 Ajax 通信開始迄の所要時間: "+($.tr.ajax.recent.start-$.tr.start)/1000 +" 秒</li>"+
232 :    "<li>最新タイトル取得 Ajax 通信所要時間: "+ ($.tr.ajax.recent.end-$.tr.ajax.recent.start)/1000 +" 秒</li>"+ beforeTime + afterTime +
233 :    "<li>このプロジェクト全体の所要時間: "+ (($.tr.end=now())-$.tr.start)/1000 +" 秒</li></ul>";
      // ログ閲覧回数の記録
234 :   cmt++;
      // 一度目の所要時間表示文字列
235 :   var cmtbtn1="<button id='cmtbtn1' onclick='this.blur();window.open(\"http://hkom.blog1.fc2.com/blog-entry-631.html\",target=\"_blank\")'>この Ajax 通信や所要時間について</button>";
      // 二度目以降の所要時間表示文字列
236 :   var cmtbtn2="<div id='cmtbtn2'>" +(cmt-1)+" 回目の Reload 結果</div>";
237 :   lg.append(AjaxLog+(cmt==1 ? cmtbtn1:cmtbtn2)); // 経過時間ログの挿入
      // 結果表示エレメントの表示
238 :   pD.css({width:"640px",height:"auto"}).show();
      // 通信終了を知らせる文字列の表示(アニメーション)
239 :   fn.show().children().eq(0).fadeIn("slow",function(){$(this).css({color:"darkgreen",background:"pink"})}).fadeOut("slow").fadeIn("slow",function(){lg.show()});
240 :  }
241 :   });
242 : }
243 :};
244 :
   // 一連のAjax通信関数を起動する関数
245 :var makeLists = function(num){
    // popup小窓のアニメーション表示のため
246 : doExplodeShrink(aP,640,600,0,true,"block");// ここでサイズ指定。最外側の div 要素を表示
247 : doExplodeShrink(mB,616,50,12,false,"block"); //OK42=Firefox.Opera,but50=forIE,
    // 結果表示用のボタンの無効化と半透明化並びにその他のボタンの有効化等
248 : $(".getEntryTitles_btn").each(function(i){
249 :  i<3 ? $(this).attr("disabled","disabled").animate({opacity:0.4})
250 :   : $(this).attr("disabled","").animate({opacity:1.0});
251 : });
252 : $.each(["recent","before","after"],function(i,n){ // 結果を格納する変数の初期化
253 :  getStr[n]=""; html[n].length=0;
254 : });
255 : makeRecentEntryList(num); // 最新エントリイ情報取得開始
    // Ajax通信開始待機関数の定義
256 : var nextAjaxTimer = function(){
257 :  $.tr.waitRecentAjaxCnt++; // 回数を記録
258 :  if (jQuery.active==0) { // Recent Entry Titles 情報の取得が終わったら
259 :   if (timer) {clearInterval(timer);timer=null;}
      // 「以前」情報取得のためのAjax通信関数起動
260 :   makeEntryList("before",num); // ここは連続して履行しても
      // 「以後」情報取得のためのAjax通信関数起動
261 :   makeEntryList("after",num);  // 問題なく取得結果を html 化できる。
262 :  }
263 : };
    // タイマー起動
264 : var timer = setInterval(nextAjaxTimer,100);
265 :};
266 : makeLists(10); // 一連の関数を起動
267 :})(jQuery); // 引数 jQuery で無名関数を起動
268 :} else alert("閲覧中のサイトは FC2 ブログではないか、Fc2 ブログであっても個別エントリイ表示モードではありません。\nこのプロジェクトは、Fc2ブログの個別エントリイ表示モードの場合のみ使用できます。");
269 :

ファイル置き場問題(2) 結局有料サイトしかないのか?! 否!Google Codeが!

概ね5時間掛けて色々調べた結果...!!

リンクが貼れるファイルを無料ホスティングサービスでゲットすることは、かなり困難であることが判明しました。

そこで最後のチャレンジで Google Code に登録してみました。すると何と!upload 出来て、かつリンクを貼ってダウンロード出来るではありませんか!

早速、その例を下に掲載してみます。

firebug.js

firebugx.js

既存のファイルなので Google Code Website の主旨に反するでしょうが、テストということで許して貰うとして(苦笑)、上のリンクをクリックすると直ぐにダウンロードを開始するダイアログが表示されます。まさにこれこそ願っていた環境なので、ついに目的を達することが出来ました。

これにより、各種のブックマークレットによって各種のサイト上でスクリプトをインクルードし、Ajax 通信などによる情報取得/表示を実現する道が開けました。

例えば、先のエントリイで掲載したブックマークレット( jQuery と firebugLite を一括してインクルードするもの)において、その中の firebug.js ファイルのリンク先を webos-goodies から、Google Code に変えたものを以下に掲載しました。

これで (1) jQuery1.2.3.jsと (2) firebug.js のインクルードが実現すれば、万々歳です。

そしてそれは実現できました!!

下のリンクをお気に入りに登録すれば、 jQuery と firebugLite を一括してインクルードするブックマークレットとして使えます。
No1簡易版 jQuerify + FirebugLite

▲ToTop

それでも有料サイトを使うべきかも知れない

Google Code サイトは他者に貢献できるようなファイルを置く場所だと思われますので、私のような使い方、あるいは私のようなレベルの素人が使うのは、いかにも僭越な気がします。

よってGoogle Code には遠慮して、有料サイトを使うべきかも知れません。

しかし、暫くの間は自作の ブックマークレットを含む Javascript コード等を Google Code に upload し、このブログ上からダウンロードやインクルード出来るようにしてみようか、と思っています。

JustSystem や Sony が提供するホスティングサービスを利用することは、いつでも可能なことですし、Google Codeでは写真や動画あるいは通常の文書ファイルは保存できません。毎月 300 円程度で確実にファイル置き場として利用できるのであれば、決して高くはありませんから、追って有料サービス利用に踏み切ろうかと、思案中です。

ps:昨日確認したところ、二社とも無料期間を設けて自社サービスへの誘導を行っていました。

ファイル置き場問題に明るい展望が開けた!、かと思いきや・・・駄目だった。

駄目元でも良いから...とグーグったら!

検索用語はずばり「無料 ファイル置き場」。するといくつかの候補がリストアップされるではありませんか!

iDrive───これは英語圏のもので無料ではあるものの、利用規約などとても読む気にはならないので、最後の手段かな、と概観してみました。使えそうではありました。( Free Online Backup - IDrive - Encrypted, Secure and Automated! )一度は無料サービス提供をやめたはず(2001年)ですが、その後また再開したものと思われます。何と 2GB の容量が無料で使えるようです。

但し、バックアップ用途としてのディスク容量の提供に限定されているのかも知れませんが、詳細は調べ切れていません。

なお、有料の eXys iDrive(WebDAV対応 大容量 オンラインストレージ - eXys iDrive - 簡単・安全にファイルを保存・共有できます) とは全く別物です。

次に、Google の無料サービスでも使えそうなものがあるようです。プロジェクト ホスティング「独自のオープンソース プロジェクト用に コードとドキュメントのホスティング サービスを無料で提供。」とありますが、jQuery.js ファイルも Google サイトに置いてあったことを思い出しました。( 開発者ホーム - Google Code

しかしこちらは色々とハードルが高そうな気がします。

更に、オンラインストレージに関して、概括的・具体的・網羅的な情報を提供してくれるこんなサイト( オンラインストレージを使ってみよう )もありました。

そしてたどり着いたのは infoseek です。今や楽天傘下に組み込まれているとは知りませんでしたが、こちらの無料ホームページサービスはファイル置き場を認めているらしいのです。

日本語でファイル置き場と言う用語に引っかかるサイトの多くは infoseek の登録 Homepage であることからも象徴されるように、infoseek の Homepage サービスをファイル置き場として活用している方が沢山いるらしいことも判明しました。

▲ToTop

というわけで早速 infoseek に Homepage 開設しようとして...But!

ユーザー登録を終えると「iswebライト新規登録時のご確認」画面が表示されますが、そこには「他サイトからのCGI及びデータの参照を禁止します。 」と書かれているではありませんか!

つまりファイル置き場には出来ない、と言う結論になります。

しかし一方ではいくつかのブログサイトで、ファイル置き場として使っていることを公言しているエントリイもあったりしますので、もしかしたら最近になって「置き場禁止」へと方針を転換したのかも知れません。うむ...。出鼻を挫かれてしまいました!

▲ToTop

そこで更にネット検索を重ねたところ...!!

あった。ありました!

メールアドレスとパスワードを登録するだけで容量制限なしで使えるという、超簡単ストレージが!
「 何の予告もなくファイルを全削除されました(08.1/1)。そして消えていたファイルが全て元通りになってました。何だったんだ?!(08.1/4) 」との記述が象徴しているように( 8倍速対応 | 無料無制限のオンラインストレージ-MediaFire )、何時消えてなくなるかも知れませんが、そのことを覚悟した上で( まさしく駄目元で )利用してみようかと思います。

容量制限なし、登録は超簡単な無料オンラインストレージ

早速アップロードしてみましたが、アップロードが終わるとメルアドとパスワードを入力する画面が表示され、Image Page URL とリンク HTML 文とが直ぐにゲットできてしまいます。

使用例1:例えば写真を登録してみました

Unlimited Free Image and File Hosting at MediaFire
(例)Media Fireに保存した写真

使用例2:jsファイル

firebug.js

しかし...

写真などをダウンロードするのは良いでしょうが、インクルード用 js ファイル置き場としては適さないことが分かりました。

ダウンロードしようとすると MediaFire サイトが開くのです。そしてウィルスチェックが自動的に行われ、その後画面をクリックして初めてダウンロードするようになるのです。これではjsファイルのファイル置き場としては実用的ではありません。

そこでブックマークレットの一部にMediaFireからインクルードするjsファイルを組み込もうと思いましたが、ファイル名が http://www.mediafire.com/?ymw9kjb0jig のように独自ネームに変えられてしまうため、これも駄目だと分かりました。

ということで、明るい展望は開けませんでした。

苦闘5時間の末に!

それでも食い下がった結果、苦闘5時間の末についに「発見し、利用を開始」しました───外部からリンクを貼りそれを参照可能な無料サービスを。このことについては次の Entry( ファイル置き場問題(2) 結局有料サイトしかないのか?! 否!Google Codeが! )で詳述します。

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 ) {

jQuery の AJax Utilityを利用して、最新 20 エントリイの諸情報を取得表示(ResponseXML版)

改訂履歴
  • 初出:2008/2/18
  • 全面改訂:2010/5/16

結論:最近の 20 エントリイに関する Ajax による各種情報取得結果

Click すると1秒足らずの内に、Ajax 同期通信により xml ファイルをゲットし、次に、ゲットした xml ファイルから必要な情報を取りだし、この文章の下にリストとして表示します。表示される内容は 20 個の最新投稿エントリイに係る情報です。

    jQuery の AJax Utility を ResponseXML モードで使ってみる。

    FC2ブログでは URI: http://userName.blogNo.fc2.com/?xml で xml 形式のファイルがブラウザ表示されます。サイトRSSとして利用されるこのファイルには、次のような諸情報が格納されています。( 但し、2010年4月から、ブログの設定において「 RSS 広告を表示しない 」設定に変えないと、素の xml ファイルは取得出来ず、xmlファイルは RSS リーダーによって解読されてしまいます。 )

    • 最近のエントリイタイトル
    • 最近のエントリイのリンク
    • 最近のエントリイの所属カテゴリイ
    • 最近のエントリイの投稿日時
    • 最近のエントリイの要約文
    • 最近のエントリイ本文

    などです。

    そこで、サーバー上にあるこの xmlファイルを Ajax 通信で取得すれば、上記の情報の一部又は全てを取得することが随時可能となります。

    jQuery を利用した Ajax Utilities 練習のために

    但し、この xml ファイルの情報には常に「最新の」ものに過ぎません。各エントリイからすれば常に最新情報が必要な訳ではありませんから、任意のエントリイからこの情報を活用することはまずないでしょう。その点ではこれから行うことは余り有り難みのあるものではなく、むしろ無用の長物と言えます。

    更に、ブロック変数を利用すればいくらでも収集できる情報ですから、ますます意義もないことと言えます。

    ところが、各エントリイにとっては「無用の長物」であるこの xml ファイルは jQuery.js の Ajax Utilities を具体的に詳しく知るための一助とするのには、適しています。

    そんな訳で、上記の6つの情報の内、タイトル、本文及び要約文を除く4つの情報を jQuery Ajax を活用して取得して上記に表示してみました。なお、ブログ環境設定において サイトRSS 情報に掲載するエントリイ数は 20 としましたので、ここではその全てをゲットしています。

    ▲ToTop

    完成しこのエントリイで利用している Javascript コード

    2つ前のエントリイの ResponseText 版同様、今回の ResponseXML 版でも苦労を重ねました。

    そして結論として同期通信を採用することにしました。今回の目的はxmlファイルを扱うことであり、またボタンをクリックしてAjax通信を行うわけですから、敢えて非同期通信にする必要がないと考えました。

    完成したコード Ver1

      1:var getXMLObj = function(limit){
      2:  $put = $("#put625").empty();
      3:  $put.prev().children().empty();
      4:  var  data, No, subject, date, html=[],
      5:    target ={
      6:        title:[],
      7:        link:[],
      8:        "dc\\:date":[],  //meta-character
      9:        "dc\\:subject":[]  //meta-character
     10:    };
     11:  $.ajax({
     12:    url: /http:.+fc2\.com\//.exec(location.href)[0] + "?xml",
     13:    type: "GET",
     14:    dataType: "xml",
     15:    async:false, //同期通信を行う
     16:    success: function(xml){
     17:      data = xml;
     18:    }//End of success()
     19:  });  //End of ajax()
     20:  $.each(target,function(key){
     21:    target[key] = $.map($(data).find(key),function(n){
     22:      return $(n).text()
     23:    });
     24:  });
     25:  for (var i=1; i < limit+1; i++) {
     26:    if (!target.link[i+1]) break;
     27:    No = /entry-([0-9]+)/.exec(target.link[i])[1];
     28:    subject = " , " +target["dc\\:subject"][i];
     29:    date =" , " +target["dc\\:date"][i].substring(0,10);
     30:    html.push( "<li><a href='" + target.link[i] + "' target='_blank'>" +
     31:         target.title[i] + "</a> (No." + No + subject + date + ")</li>");
     32:  }
     33:  html.length = limit;
     34:  $("#put625").prev().children().html("jQuery Ajax をつかって取得した過去の " +
     35:     ( (i==20) ? 20 :i-1 ) +" エントリイタイトル等一覧").end().end()
     36:    .html(html.join('')).slideDown("slow");
     37:};  //End of function()

    ▲ToTop

    完成したコード Ver2───こちらの方が遙かにスマートだ

      1:var getXMLObj = function(limit){
      2:  $put = $("#put625").empty();
      3:  $put.prev().children().empty();
      4:  var data, No, subject, date, html=[];
      5:  $.ajax({
      6:    url: /http:.+fc2\.com\//.exec(location.href)[0] + "?xml",
      7:    type: "GET",
      8:    dataType: "xml",
      9:    async:false,
     10:    success: function(xml){
     11:      data = xml;
     12:    }//End of success()
     13:  });  //End of ajax()
     14:  $.each($("item",data),function(){
     15:    No = /entry-([0-9]+)/.exec($(this).find("link").text())[1];
     16:    subject = " , " +$(this).find("dc\\:subject").text();
     17:    date =" , " + $(this).find("dc\\:date").text().substring(0,10);
     18:    html.push( "<li><a href='" + $(this).find("link").text() +
     19:                     "' target='_blank'>" +
     20:           $(this).find("title").text() + "</a> (No." + No + subject + date + ")</li>");
     21:
     22:  });
     23:  html.length = limit;
     24:  $put.prev().children().html("jQuery Ajax をつかって取得した過去の " +
     25:    ( (i==20) ? 20 :limit ) +" エントリイタイトル等一覧").end().end()
     26:    .html(html.join('')).slideDown("slow");
     27:};  //End of function()

    ▲ToTop

    コード作成上の苦労談( 1~5 は Ver1 について、6 は Ver2 について )

    その1──目的とするタグの xml ファイルからの抽出

    xml ファイルを扱うのは初めてのことだったので暗中模索しました。

    Fc2 ブログの xml ファイルの場合、例えば日付情報は <dc:date> と言うタグ名に格納されています。これは XML 名前空間と呼ばれる拡張されたタグ名で(XML名前空間の簡単な説明)、その検索を行うメソッド getElementsByTagNameNS によって Firefox 等では検索が可能ですが、IEはこのメソッドに対応していません。。

    しかし、そこは jQuery。 素晴らしさの一例でしょうが、このようなダグ名でも抽出することが出来る仕様となっています。

    Selectors - jQuery API 頁の冒頭にリテラル文字列内に【 " ; $ , . + - : & > <・・・】などの meta-characters が存在する場合の対処法が、端的に「 you must escape the character with two backslashes: \\. 」と記されています。

    この既述を見いだすまでに、あちこちグーグってみて、この件の記載がある幾つかのサイトを見つけましたが、本家サイトにしっかり記載されているとは盲点でした。

    なお、2008年段階では、駄目元とばかりに "dc:" を除く date や subject 文字列だけで jQuery 検索を掛けてみたところ、何と見事に目的の情報を取得出来てしまったのです。しかし、2010年にはそれは駄目でした。jquery.js のバージョンの違いによってこの差異が出たのかもしれませんが、それは兎も角、2010年には 2008 年時点のコーディングでは、ここで目的としたパフォーマンスは全く実現できなくなったので、今回コードを全面的に見直したのです。

    その2──IE 対応策

    【この項は 2008 年段階のもので既に解決していますが、経過として残しておきます。】

    IE においてはなかなか(10日弱の間) 20 エントリイタイトルが表示できませんでした。その原因を探ろうにも FireBug のような素晴らしい性能を有する IE 向けの debugger が存在しないため(正確に言えば存在するかも知れませんが、その存在を知らないため)、表示を妨げている原因の探索に大変苦労しました。

    悩んでいても何も解決しないので、やむを得ず性能の悪い debugger = IEに備え付けのものやIE専用のもの = を利用して原因を探求し、四苦八苦の末にやっとその原因を究明することが出来ました。

    上述のように xml 文書では、日付とカテゴリイに関するタグ名が、dc:date、dc:subjectとなっていますが、jQuery.js を使ってこの xml ファイルを対象として走査する場合において、FireFox と Opera においては find("date") と find("subject") メソッドで当該タグを抽出できるのに、IE においてはそれが出来ずに未定義値をゲットしてしまうのです。このことが求める結果を取得・表示できない真相の闇でした。

    そこで当座このエントリイにおいては(この後に、もっと一般化したIEでも同様の結果が得られるようなコードを作成したので)、IE で取得できない値はこれをゲットしない用にプログラムを改変して、やっと IE の場合でも最新20エントリイ情報を表示できるようになったのです。ふぅ~(;_;)ウルウル

    ▲ToTop

    その3──選択したタグからのテキストの抽出

    これには $.each メソッドか $.map メソッドのいずれかを使うことが出来るだろうと考え、今回は後者を利用してみました。どちらでも同様のことが出来るので余り使ったことのない map メソッドに挑戦してみた訳です。

    悩んだことは map メソッドの引数でした。ここではnと言う文字を使いましたが、要は対象配列の各要素を指す訳ですが、これをどの様に表現して良いか思案してしまいました。結局どうということはなかったのですが、map メソッドを使い慣れていなかったためにちょっと苦労してしまいました。(16-17行)

    その4──二重イテレーター

    今回作成したコードで最も自画自賛している箇所が二重イテレーターです。

    この方法に至ったのは、xml ファイルから取得したいタグが4つ( title、link、date、subject )あることから、各々のタグ取得に関してほぼ同一内容のコードを書いても仕方ない、それは余りに冗長であると思ったからです。

    そこでまずタグ名でイテレートできるよう、タグ名をプロパティ名とし、空配列をプロパティ値とするオブジェクトを作成し、その各プロパティに対して $.each メソッドによるイテレート処理を行うことにしました。(2-7行、15行)

    配列に対してではなくオブジェクトに対して $.each メソッドを使うことは初めてでしたので、ここでも引数をどうするかでちょっと手間取りました。

    この each イテレータの中に、先に述べたように各タグ要素から必要とする文字列を取得するための $.map メソッドによるイテレートを内包させて二重イテレータをしたのです。

    各タグを順番に対象とし、そのタグ要素に対して find メソッドを適用して当該タグの全ての要素から構成される配列を作成し、それを当該タグ要素名のプロパティに格納したのです。(15-19行)

    その5──xmlファイルの記事数

    Fc2ブログ環境設定 - ブログの設定に「RSSの設定」という項目があります。ここで表示件数と表示順を設定できるのですが、この内容が 21件以上でかつ全文表示となっていなければ、20エントリイのタイトルなどを取得できないことが判明しました。

    表示件数 20 以下でもエラーが出るし、たとえ 21 以上となっていても表示順序が「一部表示」となっていると結局 10 件の記事しか掲載されないために、エラーが出てしまいました。

    そこで期待したような環境設定になっていない場合の対応策を盛り込みました(21行)。存在有無を確認し、存在しなければ for loop を break するというものです。

    これにより 20 件のタイトルを取得できない環境設定となっている場合でも、獲得できる数だけのタイトル名などを取得することを可能としました。

    ▲ToTop

    その6──もっと効率的なコード Ver2 を作成しました

    2008 年と 2010 年 5 月とコードを弄ってきて、どうもすっきりしない感じがしていました。くどいというか、徒に複雑になっているというか、余り「消化が良くない」のです。

    xml ファイルの構成が良く分かったのにその知識が活かされていないと思われるし、もっと明快なコーディングが行えるのではないか、と考えました。

    そして完成されたのが Ver2 です。

    行数もかなり減少したし、target オブジェクトを使用しなくても済んでいることも合理的です。何よりも meta - character の扱い方がはっきり分かった以上、それを十分に活用すべきであり、そうすれば二重イテレーターも使わずに済むことも大きな利点です。

    jQueryのAJaxUtilityを利用して、他のエントリイのタイトルを表示する(ResponseText版)

    改 訂 履 歴
    • 初稿:2008/2/10 on ver1.2.2
    • 追記:2008/2/16
    • コードの抜本改定:2008/6/30 on ver1.2.6
    • コード微調整:2008/7/1~4
    • コードの更なる調整:2008/7/13

    初めて jQuery AJax Utilities を使ってみる

    直接そのページを開くのではなく、(今開いている頁はそのままで)当該ブログの過去のエントリイ情報を得るにはどうすれば良いのか、とふと思いました。直前の複数のタイトルが随時見られれば、色々便利かなと思ったのが事の発端です。

    言うまでもなく、この Fc2 ブログの場合には、直前/直後の各々 1 つずつのエントリイに関する所定の情報は、所定の単変数によって取得することが出来ます。例えば <%nextentry_url> で直前エントリイの URLを、<%preventry_title> で直後エントリイのタイトルを取得することが出来ます。

    しかしそれは直前直後の僅か 1 つずつのエントリイ情報しか得られません。

    また、最新エントリイのタイトル一覧もブロック変数 <!--recent--> ~ <!--/recent--> で取得することが出来ます。

    しかし、それはあくまでも最新のエントリイに関する情報であって、直前/直後ではない、もっと離れた過去と未来のエントリイ情報を取得する変数は用意されていません。

    任意の1のエントリイの直前/直後のエントリイタイトル一覧を見る変数は用意されていません。

    そこでそれらを取得するには AJax を利用するしかないだろうと判断し、初めて jQuery's Ajax メソッドを利用して試してみました。

    ここでは、このエントリイの前の 10 個のエントリイのタイトルをまとめて取得し、このエントリイ内に表示してみました。

    jQuery.get クラスメソッドを使って別エントリイのタイトル(リンク付き)を取得

    ※ 個別エントリイ表示以外のモードの場合には、ここに何も表示されません。

    •   Now Loading...

    ▲ToTop

    成功したコードを確認する。

    まず最初に、過去の任意の1つのエントリイタイトル情報の取得を試みたところ、これはあっさりと出来てしまいました。その時には、その余りの「容易さ」が、後にアダになるとは思いもしませんでした。(×_×;)シュン

    余りの容易さに意気込んで、次に複数のエントリイ情報を得ようとチャレンジを開始したわけですが、何と現在の表示状態に達するのに5日を要してしまいました。

    何故それ程の時間を要したのか───それを明らかにすることは jQuery を利用する上での注意点を体験を通じて示すことであり、意味のある行為であると思われるので、躓いたことを以下にまとめます。

    jQuery.get(url,data,callback,type) メソッドの callback 関数

    callback 関数は、url で指示したサイトの DOM ツリー全体を引数「 data 」に 取得し、次にその data 値から目的とする情報を取得して当該関数内で様々に処理する仕様となっています。なお、data はユーザーからサーバーへの送信データであり今回は記述しませんでした。

    さて、この callback 関数内で取得した値を、その外の別の関数や Javascript 文の中で直ぐに使用しようとしたのですが、ここで躓きました。

    callback 関数内で処理され取得した値を、その外では「直ぐには」「微妙に」利用出来ない場合があるのです。Ajax 通信故のタイムラグ、つまり Javascript コード進行と並行で行われる Ajax 通信がもたらす指示出しと結果取得とのタイムラグが、「微妙な問題」を引き起こします。

    その具体的な内容を以下に記します。

    過去 Entries のタイトル及びリンク先アドレスの取得に成功したコード

    何はともあれ、成功したコードは以下の通りで、コードに触れながら「微妙な問題」に言及しようと思います。なお以下のコードはこのエントリイの末尾に記述してあるので、基本的に DOM 読み込み完了を待機させる必要はありません。

       // 個別エントリイモード以外の時には、
      1: if (location.href.indexOf("entry")==-1) {
        // Ajax通信開始ボタンを隠蔽し
      2:  $("#pastTitle623").parent().css("display","none");
        // 何もしないでコード進行を終える。
      3:  (function(){return;})();
      4: }
       // ボタンから起動される Ajax 通信実行関数を定義する
      5: var getTitleText = function(limit){
        // この関数を起動するボタンを隠蔽する。
      6:  $("#btn623").css("display","none");
        // 結果表示領域を表示する。この中には Ajax 通信中を知らせる文字列を埋め込んである。
      7:  $("#pastTitle623").css("display","block");
        // 諸変数定義
      8:  var thisEntryNo, getEntryNos=[], thisURL=[],
      9:   regExpr = /entry-([0-9]+)/, html=[], realcnt=0;
        // 今開いているエントリイのエントリイ番号を取得する
     10:  thisEntryNo = regExpr.exec(location.href)[1];
        // タイトル文字列を抽出するための正規表現を定義する
     11:  regExpr = /<title>(.*)<\/title>/;
        // 対象とするエントリイ番号を巡回処理で作成する
     12:  for (var i=1; i<limit+1 && i<thisEntryNo ; i++){
         // エントリイ番号を配列
     13:   getEntryNos.push( thisEntryNo -i);
     14:  }
     15:  // 複数のエントリイを対象として Ajax 通信を実行する
     16:  $.each(getEntryNos,function(i,item){
         // タイトル名などを取得するエントリイの URI アドレスを構成する
     17:   thisURL[i] = "http://hkom.blog1.fc2.com/blog-entry-" + item +".html";
         // 各々の個別エントリイ URI に対して Ajax 通信を行い
         // ResponseText 文を取得する。
     18:   $.get(thisURL[i],function(data){
          // Ajax通信結果であるエントリイHTML文全文からタイトル名を取得
     19:    var titleStr = regExpr.exec(data)[1].substring(19);
          // エントリイタイトル名があれば
     20:    if (!!titleStr){
           // 取得タイトル名などを表示用 HTML 文として整序する
     21:     html[i] = "<li><a href='" + thisURL[i] + "' target='_blank'>" + titleStr +" (EntryNo.:" + item + ")</a></li>";
           // 取得タイトル数をカウンター値に入力する
     22:     ++realcnt;
     23:    }
          // 巡回処理が最後の配列要素に到達したならば
     24:    if (item == getEntryNos[getEntryNos.length-1]){
           // realcnt、つまりタイトル取得数がゼロならば
     25:     var tmp = realcnt==0 ?
            // デコードして「ありません」を代入する
     26:      decodeURI('%E3%81%82%E3%82%8A%E3%81%BE%E3%81%9B%E3%82%93%E3%80%82')
            // タイトル取得数があれば取得結果である配列要素を併合する
     27:      : html.join("");
           // 結果表示タグ内に結果を表示する
     28:     $("#pastTitle623").html(tmp);
     29:    }
     30:   }); // End of $.get()メソッド
     31:  }); // End of $.each()メソッド
     32: } // End of function
     33: 

    dataType(text や json 等)を特に指定していませんので、data は テキスト形式となります(ver1.2.6 の 2881 行から)。このテキスト文は指定した「url」の html 文ですから、正規表現文字列(上のコードリストの 13 行)とその exec メソッド(同上 21 行)を使って、title タグ内の文字列を取得し、かつその中のエントリイタイトル名文字列を抽出しました(つまりブログタイトルを除外しました)。

    上のコード作成上で最も苦労したのは、$.get(url,fn(data){}) で取得した data 値の処理です。

    追記(2008/2/16)

    その後色々調べた結果、そもそもサーバー側が xml ファイルを提供しなければ当然それを取得できない───そんな簡単なことがやっと分かりました。(※ 詳しい方からは「そんなこと当たり前!何を今更言っているのか?!」と叱咤されそうです。)FC2ブログサイトでは通常のアドレスのままでは XMLHttpRequest.ResponseText は取得できても、 XMLHttpRequest.ResponseXML は取得できないことを理解しました。

    具体的には jQuery.js を使っている限り Ajax の動きがブラックボックス化されているので、『 Javascript 第5版』で紹介されているコードを使って、つまりjQuery.js ではなくもっと原始的な Ajax コードを使って確認しました。

    なお、今回行った Text ファイルにたいする正規表現による検索以外の方法でも、例えば XML ファイルを取得する Ajax 通信によってもブログタイトルやそのエントリイタイトルは取得出来ますが、それは一群の最新のエントリイに関するものだけに限定されます。

    追記2(2008/2/16)

    Fc2 ブログは RSS を発行できるのだから、xml が使えないはずはない、と食い下がった結果、次のことに気がつきました。

    uri アドレスの後に「 /?xml 」をオプション的に付加することによって xml ファイルが取得できると言うことを!!

    しかし、ここで取得できる xml ファイルにはある「固定された」制限があります。それは xml ファイルに含まれるエントリイ数です。ブログ環境設定で登録したエントリイ数の、しかも直近の情報しか xml ファイルには含まれません。そのためでしょうか、ブログ環境設定には RSS 固有用のエントリイ数登録が可能となる項目が新設されていました。

    つまり /?xml で取得できる xml ファイルは「最新」エントリイ情報であり、そのエントリイ数は環境設定に依存します。従って RSS 情報の場合のエントリイ数を 11 以上に設定しておけば直近直前の10エントリイのタイトル情報を取得することが可能となります。

    そもそも、この /?xml オプションを利用した Ajax によるタイトル取得については、ブロック変数 <!--recent--> ~ <!--/recent--> で取得できる情報と全く同一になるので、敢えて Ajax を利用する価値は皆無ですが、一応別のエントリイで述べてみたいと思います。

    ( 別 Entry 「 jQuery の AJax Utilityを利用して、最新 20 エントリイの諸情報を取得表示(ResponseXML版) 」 でそれを試みました。 )

    ▲ToTop

    何が問題となったのか

    1. $.get メソッドの後に続くコードの実行に要注意

    非同期通信中で、まだ目的の情報が取得できていないのに、お構いなしにスクリプトのコード進行は先に進む───このことに気がつくのに数日を要しました。これまで自前では全く非同期通信を実行したことがないので、このようなデータ取得時間とコード進行の兼ね合いなどを気に掛ける必要がありませんでした。

    この点こそ、非同期通信の特異性あるいは特徴と言えるかも知れません。

    従って、$.get メソッド内で取得した結果を利用するコードを、$.getメソッドの外でかつ当該メソッドの直後に記述するとエラーとなるか、意味のない結果が得られてしまいがちです。原因を探るべく jQuery 本家サイトをよ~く眺めてみたら、そのことが小さく書いてある箇所がありましたが、初めてAjax コードを書く上で、必ず留意すべき重要な点だと改めて確信した次第です。

    以上の問題、いな特性を踏まえて 18 行以下のコードを書きました。さんざん試行錯誤を重ねた結果、Ajax 通信結果を処理するコードは、全て Ajax 通信メソッドの中に入れるべきであることを改めて学習したのです。

    2. 繰り返し $.get メソッドを利用する場合の注意点

    次に、複数回の $.get メソッド実行によってサーバーから連続して情報を取得する場合においても、予想しなかった問題が発生しました。今回のケースで言えば過去の 10 個のエントリイのタイトルが新しいものから順に下に表示されるようにコードを作ったのです。否、作った筈でした。

    ところが表示結果を見てみると順番が不同になってしまうのです。今開いているエントリイの直前 10 個のエントリイタイトルを確かに取得できているのですが、その順番が大袈裟に言えば表示する度に異なるのです。少なくとも新しいものから順に下に並ばないのです。

    どうしてなのか思案した結果、それは 10 回のそれぞれのタイトル値の取得に要する時間が異なるのではないか、との結論に至りました。当該エントリイの情報量が多ければより多くの時間を、少なければより短時間で目的を達してしまうのだろう、と推測しました。

    実際 FireFox 版の Firebug で通信状況を調べたところ、Ajax 通信完了は各エントリイのファイルサイズに依存し、下図で分かるように完了順序は必ずしも通信開始順にはならないことを確認しました。

    複数の連続する Ajax 通信の進行状況図

    そこで順番に並べるために一工夫しました。(取得完了順ではなく番号順に)配列に順番に情報を格納し、それを順に結合すればよいと考え、それを実行したのです。

    3. DOM ready メソッドとの兼ね合い

    今日(2008/2/10)最終結果に到達するまでずっと、コード全体を DOM ready メソッドでくるんでいました。

    しかし非同期通信そのものやその過程で情報を取得する手順においては、DOM 読み込みが完了している必要はありません。その完了を確認してから実行しなければならない処理は、Ajax処理で取得した情報を、今見ているページ内に挿入する行為だけです。

    そこで本日(2008/2/16)、全体を無名関数で包含して不必要な待機をやめさせ、その結果、可能な限り速く結果を今見ているページに表示できるようにコードを改変しました。

    その後(2008/7/1)更に改変し現在の形にしました。エントリイ表示後に直ぐに無名関数を実行させて自動的に Ajax 通信をスタートするのではなく、ユーザー指示により通信を開始するようにしたのです。Ajax 通信に要する時間を体感できるこの方法がより相応しいとの判断からです。

    次に行うべきことは・・・

    • xml ファイルをゲットして、そこから過去エントリイ情報を取得する試みを行ってみたい。
    • 更に過去の10タイトルを、あるいは後続する未来のタイトルを、各々このエントリイ内においてクリック一つで表示できるようにするのも一興か?、と考えています。同時にその時には、それまで表示されていたタイトル一覧の消去と新しいタイトル一覧の表示を、アニメーションを使って入れ替えればより面白いかも知れない、と考えています。

    jQuery()の挙動を解読する(23) $.event.special オブジェクト ──jQuery解読(35)

    jQuery.event.special オブジェクトの働き

    special オブジェクトは bind( elem ,fn ) 又は unbind( elem ,fn ) メソッドを使って、reaady、maouenter 及び mouseleave イベントタイプとそのハンドラー関数を elem に登録し、あるいはこれを解除する為に ver1.2.2 で新設されました。それは、(un)bind('ready',fn)、(un)bind('mouseenter', fn) 又は (un)bind('mouseleave', fn) の6つのインスタンスメソッドが起動された場合にだけ、jQuery.event.add() 又は jQuery.event.remove() メソッド内で呼び出されます。

    special オブジェクトは ready、mouseenter 及び mouseleave の3つのプロパティを持っています。ready は setup と teardown の2つの関数オブジェクトで構成され、他の2つのプロパティは setup、teardown 及び handler の3つの関数オブジェクトから構成されています。(下図参照)

     special = {
      ready      : { setup : fn11, teardown : fn12 },
      mouseenter : { setup : fn21, teardown ; fn22, handler : fn23 },
      mouseenter : { setup : fn31, teardown ; fn32, handler : fn33 }
     }
    

    ここに、それぞれの関数の役割は setup はイベントの登録用、teardown は登録済みイベント解除用、handler は各タイプのイベントに対するハンドラー関数です。

    hover()メソッドと mouseover/out イベント、そしてIEへの「配慮」?

    hover()メソッドは jQuery ver1.2.1 では mouseover/out イベントを扱うためのトグルメソッドでした。ところが、1.2.2 ではコードを一見すると hover()メソッドは、mouseenter/leave を扱うように変更されたように見えます。mouseover/out は hover()メソッドから切り離されてしまったような印象を受けます。

    しかし、コードを詳細に見てみるとそうではないことが直ぐに分かります。hover()メソッドは IE においては mouseenter/leave イベントをサポートし、その他のブラウザにおいては mouseover/out イベントを扱うように拡張されたのです。

    この拡張が何故もたらされたか、その理由は判然としませんが、過去の hover() にバグはないようですから、市場占有率が相変わらず高い IE に配慮したと理解すればよいのかも知れません。mouseenter/leave イベントが IE だけのイベントであって他のブラウザでは存在しないのに、敢えてこれらのイベントをサポートする理由は、まだ IE を無視する訳にはいかないためとしか考えられません。

    関連して、それでは $(args).eventtype() 形式のイベント発動メソッドにおいて、eventtype として利用できるイベントの種類はどう変わったのか確認してみました。すると、こちらは 1.2.1 と 1.2.2 で全く同一でした。つまり、mouseenter/leave イベントは hover() メソッドだけで利用可能なのに対して、mouseover/out イベントは hover() メソッドでの利用だけではなく、それぞれを単独で利用することも想定した仕様となっています。

    勿論、bind メソッドを使って mouseenter/leave イベントを単独使用することは可能でしょうが、IE だけでしか動かないコードを作る意味はありません。

    ▲ToTop

    jQuery.event.special["type"]["setup/teardown/handler"]()メソッドの解読

    specialオブジェクトは、jQuery.evemt.add() メソッド又は jQuery.evemt.remove() メソッドから呼ばれます。前者は当該イベントをバインドする際に、後者はそのイベントの登録を解除する時に、です。

    type == ready の時
    2126: special: {
    2127:  ready: {
    2128:   setup: function() {
    2129:    // Make sure the ready event is setup
    2130:    bindReady();
    2131:    return;
    2132:   },
    2133:
    2134:   teardown: function() { return; }
    2135:  },
    2136:
    
    • jQuery.event.special.ready.setup()
      1. $(document).bind("ready",fn) メソッドが起動されると、add()メソッドを介して special.ready.setup() メソッドが呼び出されます。この special.ready.setup() は bindReady() メソッドを呼び出して、 jQuery.ready() メソッドを DOM 読み込み完了イベントハンドラーとして登録し、DOM 読み込み完了後にこれを起動させます。
        しかし、これらの動きのどこにも第2引数の fn 関数が登場しません。正確に言えば jQuery.ready() メソッドの最後の行に至って初めて fn が起動されます。jQuery(document).triggerHandler("ready"); です。これにより fn 関数が起動されます。
      2. 次に、special.ready()メソッドの最後(2131行)には返値なしの return がありますから、未定義値が呼出し元に返されます。この結果 1875-1878 行の Listner 等のバインダー関数は起動されません。
    • jQuery.event.special.ready.teardown()

      これは単に未定義値を return するだけです。何もしません。呼出し元に戻った際には return により返された undefined は !== false ですから、1875-1878 行の Listner 等のバインダー関数は起動されません。

    ▲ToTop

    type == mouseenter の時
    2137:  mouseenter: {
    2138:   setup: function() {
    2139:    if ( jQuery.browser.msie ) return false;
    2140:    jQuery(this).bind("mouseover", jQuery.event.special.mouseenter.handler);
    2141:    return true;
    2142:   },
    2143:
    2144:   teardown: function() {
    2145:    if ( jQuery.browser.msie ) return false;
    2146:    jQuery(this).unbind("mouseover", jQuery.event.special.mouseenter.handler);
    2147:    return true;
    2148:   },
    2149:
    2150:   handler: function(event) {
    2151:    // If we actually just moused on to a sub-element, ignore it
    2152:    if ( withinElement(event, this) ) return true;
    2153:    // Execute the right handlers by setting the event type to mouseenter
    2154:    arguments[0].type = "mouseenter";
    2155:    return jQuery.event.handle.apply(this, arguments);
    2156:   }
    2157:  },
    2158:
    
    • jQuery.event.special.mouseenter.setup()
      1. IE の場合、何もせずに呼出し元に false を return します。この結果 attachEvent() メソッド( 1878 行 )が実行されます。
      2. IE 以外のブラウザの場合、イベントタイプ名を mouseover に変え、ハンドラー関数にjQuery.event.special.mouseenter.handler を指定して bind()メソッドを再帰呼び出しします。
        再帰呼び出しされた bind() メソッドによって、更に add() メソッドも再帰呼び出しされ、その中で mouseover名の jQuery.event.special.mouseenter.handler を関数とするイベントがバインドされます。そして実は、この moueenter.handler 関数には、mouseenterイベント名と連動している fn 関数が入るようになっています。こうして、mouseenter 名で登録された fn ハンドラー関数が、mouseover 名のハンドラー関数として機能することになります。
        それが終わると再帰先から呼出し元の 2140 行に戻りますが、2141 行で true を return させることにより、呼出し元で再び addEventListner ()メソッドが実行させることはありません。
    • jQuery.event.special.mouseenter.teardown()
      1. IE の場合、何もせずに呼出し元に false を return します。この結果 detachEvent() メソッド( 1942 行 )が実行されます。
      2. IE 以外のブラウザの場合、イベントタイプ名を mouseover に変え、ハンドラー関数にjQuery.event.special.mouseenter.handler を指定して unbind()メソッドを再帰呼び出しします。
        再帰呼び出しされた unbind() メソッドによって、更に remove() メソッドも再帰呼び出しされ、その中で mouseover名の jQuery.event.special.mouseenter.handler を関数とするイベントがアンバインド( removeEventistner() メソッド起動 )されます。
        その後再帰先から呼出し元の 2146 行に戻った後には 2147行で true を return させることにより、呼出し元で再び removeEventistner() メソッドを実行させることはありません。
    • jQuery.event.special.mouseenter.handler()

      ※ この関数は IE からは呼び出されません。

      1. 内包されている要素にマウスが移動した時に、mouseout したとブラウザに判断させないために、withinElement 関数を起動させて、要素の「先祖/子孫」関係をチェックさせます。内包されている要素内にマウスがある場合には新たにイベントを発生させません。
      2. event.typeプロパティ値を mouseenter に戻した上で、mouseenter 名で登録されているイベントハンドラー関数がイベントハンドラー関数となるようにセットします。

    上の説明ではもちろんのこと、special["type"].setup()メソッドは大変分かりにくいので、IE 以外のブラウザで mouseenter イベントをバインドする時の、スクリプトの進行過程を辿るフローチャート的資料を作ってみましたので、以下に掲載します。

    $(elem).bind('mouseenter',fn)  process Analyze

    左図をクリックすると原寸画像が別窓で開きます。mouseenter イベントタイプ名から、どのようにして mouseover イベントの登録に至るのか、その経過を分かりやすく説明したつもりです。

    ▲ToTop

    type == mouseleave の時
    2159:  mouseleave: {
    2160:   setup: function() {
    2161:    if ( jQuery.browser.msie ) return false;
    2162:    jQuery(this).bind("mouseout", jQuery.event.special.mouseleave.handler);
    2163:    return true;
    2164:   },
    2165:
    2166:   teardown: function() {
    2167:    if ( jQuery.browser.msie ) return false;
    2168:    jQuery(this).unbind("mouseout", jQuery.event.special.mouseleave.handler);
    2169:    return true;
    2170:   },
    2171:
    2172:   handler: function(event) {
    2173:    // If we actually just moused on to a sub-element, ignore it
    2174:    if ( withinElement(event, this) ) return true;
    2175:    // Execute the right handlers by setting the event type to mouseleave
    2176:    arguments[0].type = "mouseleave";
    2177:    return jQuery.event.handle.apply(this, arguments);
    2178:   }
    2179:  }
    2180: }
    

    mouseleave が mouseout に、jQuery.event.special.mouseenter.handler が jQuery.event.special.mouseleave.handler に変わる以外は、上の mouseenter の場合と全く同様ですので記述を省略します。

    jQuery.event.special ["type"] の難解さについて

    上に見たように、special オブジェクトはまさに「 特別に 」難解です。

    特に type 名が mouseenter/leaveの場合の setup/teardown メソッドは、bind メソッドと add メソッドの再帰呼び出しを含み、しかも途中でイベント名を変更しつつ、ブラウザ毎に不必要なイベントは登録しないという「 裏技的 」技法を用いて mouseover/out イベントにも同時対応させており、難解を極めます。

    このエントリイ作成に当たり、沢山の時間を要してコード進行を図示したり、何度も FireBug で追跡したり、色々チャレンジしてやっと「 理解できた 」のですが、この一連の Event 関連コード解読作業において、この箇所が最も苦労させられました。

    上の記述でこれらの挙動を的確に表現できた自信はありませんし、読むだけで分かる表現になっているとも思えません。(書いておきながら、失礼なことですが...)

    本来、もっと分かりやすい説明・解読文章を書ければ、と悔やまれます。

    'eType'は 'mouseenter' 又は 'mouseleave' を表すものとします。 'fType'は 'mouseover' 又は 'mouseout' を表すものとします。 gn は $.event.special['eType'].handler メソッドを表します。  $(elem).bind('eType',fn)   → add( elem, 'eType', fn )     → $.event.special['eType'].setup.call(elem)       → $(elem).bind( 'fType', gn)         → add( elem, 'fType', gn ) jQuery.event.handle には special オブジェクトが呼び出されるより前の段階で、fn が登録されている。 そして gn では、add() メソッドによって既に登録されたこの fn を呼び出している。 例えば、既に jsファイルの読み込みが終わり、&(fn(){})が実行されてイベントのバインドを行う場合の、mouseenterイベントのバインド過程を綴ってみます。 >>> console.log(jQuery.cache[2]["events"]["mouseenter"][3].toString()) function () { $(this).css("background-color", "white"); } >>> console.log(jQuery.cache[2]["events"]["mouseover"][4].toString()) function (event) { if (withinElement(event, this)) { return true; } arguments[0].type = "mouseenter"; return jQuery.event.handle.apply(this, arguments); }

    jQuery Ver1.2.2公開───2008/1/14

    1.2.2が公開された。いや、公開されていた。

    数日前に未公開の(正確に言えばダウンロードページには公開されていない)1.2.2bに触れたが、この時に気がつくべきだった。

    b があれば当然その前に a があるはずであり、更にその前には、添え字のない1.2.2があるはずだ───と。

    変更点について

    さて、最も大きな変更はグローバル変数 jQuery の定義が変わったことだろう。

    従来の再帰呼び出し的定義は廃止され、「純粋に」コンストラクタとして位置づけられたのだ。

    次に、本家の note頁 で強調されている jQuery(DOMElement) 呼び出しの Speedup は、単項引数でそれがDOM Elementであった場合のコードが追加され、この結果としてもたらされたものである。

    なお、jQueryのコンストラクタへの純化による全体への影響がどうなるのか、近いうちに検証してみたい。

    また、直近で解読してきた event 関連の若干の追加修正もあったし、細かなバグ修正も120箇所以上に及ぶようだ。

    現在進めている解読作業の対象バージョンを、随時過去に遡って 1.2.2 に変更していこう、と思っている。

    Event関連コードの解読において、ver1.2.2対応の説明へと順次改訂中

    既に内容的に簡単なエントリイの改訂作業は終えた。

    可及的速やかにイベント関連コード解読の全体を1.2.2対応へとバージョンアップしようと思う。

    テスト

    次のリンクをお気に入りに登録すれば使えます。
    set jQuery & FirebugLite Tag

    jQueryに学ぶJavascriptの基礎(4) インスタンスオブジェクトからの値の取得について upon ver 1.3.2──jQuery解読(20)

    このエントリイの改訂履歴
    • 初稿:2007/12/3
    • インスタンスオブジェクトについての記述を大幅改訂:2009/3/5

    jQuery解読作業を進めるにつれ、Javascriptの基礎が如何に分かっていないか、ほぼ毎日のように痛感させられます。その意味ではjQuery解読は無謀なチャレンジであった訳ですが、それでも誤謬を犯すマイナスを埋め合わせて遙かに余りあるプラスがあることも毎日自覚されるので、恥を忍んで引き続きjQuery解読を進めるつもりでいます。

    しかし、基礎の基礎が余りに分かってない自分に嫌気が指してきてしまい、匙を投げ出すような醜態は演じたくありません。そこで、自戒を込めて敢えて誤解していたこと、理解していなかったこと、不十分な理解に留まっていたことなどについて、つらつらと記述していきたい、と思います。

    for in object loop と継承プロパティ及び固有プロパティ

    先に( this 活用──jQuery解読(17) )、for in loop でインスタンスオブジェクトのプロパティを総覧しました。ここでその総覧について考えてみます。

    この loop ではオブジェクトが持つ全てのプロパティを列挙出来る訳ではありません(※)が、プロトタイプオブジェクトからの継承プロパティが列挙対象となることを再確認出来ました。しかしそれさえも ECMAScript による仕様ではプロトタイプチェーンは for in loop の調査対象外、とされているようですから、いずれはこの loop でプロトタイプから継承されたプロパティも取得できなくなるかも知れません。偶々 FireFox2 や IE7 では取得できますが・・・。

    ※ コードによって追加されたプロパティは列挙対象となるが、組み込みオブジェクトにあらかじめ定義されたプロパティ(例えば配列のメソッドなど)は通常は列挙できない。───Javascriptクィックリファランスp.101)。

    さて、ここで何が問題かというと、$("a").size() あるいは $("a").length を実行すると当該サイトのリンクタグの個数が取得できますが、この場合の要素数に何故、プロトタイププロパティ=継承プロパティが含まれないのだろうか、とふと疑問に思ったのです。しかし、継承プロパティは配列として、あるいは配列のようなプロパティとして格納されている訳ではないから、当然数えられないはずだ、と直ぐに納得しました。

    そして、そもそも継承プロパティは、恰もインスタンスオブジェクトのプロパティであるかのように見えているだけ(『Javascript第5版p.154』)で、決してコピーされている訳ではないことを、改めて思い起こしました。継承プロパティはインスタンスオブジェクトの固有のプロパティではないことを、期せずして再確認した次第です。

    jQuery.js は何故「配列のような」形式で取得値をインスタンスオブジェクトに返すのか?

    次に、 jQuery.js が処理値をインスタンスオブジェクトに「配列のような」形式で返すことの意味を考えてみました。

    そしてその理由は、逆の場合を想定して直ぐに明らかになりました。つまり、配列のようにしないで通常のオブジェクトのプロパティとして返すと仮定すると、length で数を数えることが出来ませんし、 script によって、prototype オブジェクトからの継承プロパティと、返値であるプロパティを区別することも出来ないと思われます。

    つまり、スクリプトによって取得したノード(値を含む)をスクリプトで自在に扱うためには、それを配列のようなプロパティで取り込むことが必要不可欠である、ということになります。

    ここに 「 配列のようなプロパティ 」 とは、0:DOMNode1, 1:DOMNode2, 3:DOMNode3, ・・・・・, N:DOMNodeN のように、0から始まる整数値をプロパティ名とし、DOMノードをプロパティ値とするプロパティのことです。

    愚問のお陰で、改めて配列のようなプロパティの重要性に思いを馳せました。

    jQuery()関数を呼び出した直後のインスタンスオブジェクトの状態

    さて、主題です。「インスタンスオブジェクトからどの様にしてその値を取り出すか」ですが、ユーザーから呼び出された直後のインスタンスオブジェクトの状態は下図のようになっています。インスタンス=this は空の配列オブジェクトとして「出発」します。

    jQuery()呼び出し直後の「 this 」

    左図は $("p") によって jQuery() 関数を呼び出した直後の、init() メソッド内の変数の状態を示しています。

    this(つまりインスタンスオブジェクト)はオブジェクトとして定義され、変数 selector には文字 p が、その他の変数にはまだ値が付与されていません。

    jQuery()呼び出し直後の「 this 」その詳細

    左図は 当該 this のプロパティを見たものです。

    this=インスタンスオブジェクトは、既に prototype オブジェクトから継承された沢山のプロパティをもっていることが分かります。

    この後の jQuery.js 内における処理の結果、ユーザーが指示し目的とした要素ノードが配列のようなプロパティとしてインスタンスオブジェクトに返され、これが return 連鎖を通じて最終的にユーザーが入力したAPIに返されます。その最終結果を変数 t = jQuery("p") で受け取り、その t を Firebug で表示した内容は以下の画像となります。

    なお、ここに表示した一連の画像は Firebug 1.3.3 で表示された情報です。

    var t = window.t = jQuery('p') の結果詳細

    直上の画像でよく分かるように、jQuery("p") の結果、目的のノード( テスト用に使用したローカルサイトの p タグの一覧 )が、配列のようなプロパティ { 0:p#top, 1:p#top2, ..... 9:p } として取得されています。

    インスタンスオブジェクトの値を取り出す方法

    以上のように、jQuery("・・・") の返値であるインスタンスオブジェクトは、配列のようなプロパティも有するオブジェクトですから、オブジェクトからプロパティを取り出す一般的方法によって、DOM Nodes を取り出すことが出来ます。

    さて、jQuery,js のコード実行過程においては、 this[num] ( num は 0,1,2,・・・であり、コード進行過程の当該段階における DOM Node 取得要素数マイナス 1 まで有効 ) によって、インスタンスから DOM ノードが取り出されるシーンがいくつもあります。

    つまり、jQuery("・・・")[n-1}とすれば、目的のノード群の中から n 番目のそれを取得できます。

    ところで、配列のようなプロパティに格納されているプロパティ値が DOM ノードであることに注意を払う必要があります。

    例えば、配列要素を文字列表示で一覧する jQuery("element'sName").join() メソッドではエラーとなります。DOMノードは直接文字列に変換できないからでしょう。また $("p").toString()では"[object Object]"しか返されません。

    $("p") を例に取れば、$("p")[num] で、オブジェクトに格納されている取得対象ノードが取得出来ますが、この場合、例えば FireFox では [object HTMLParagraphElement]と、FireBugでは <p> と表示されます。

    具体的にノードの「値」を表示させたい場合には、まずオーソドックスに行うならば、

    $("p")[0].nodeName、$("p")[0].firstChild

    等の DOM プロパティを利用することにより、タグ名や内容等を取得することになります。

    しかしこれでは折角 jQuery を使ってコードを簡略化する意味がありません。そこで、jQuery() ではインスタンスオブジェクトを取り出す様々な方法が提供されています。まず単純に DOM Nodes を取り出すメソッドは get() です。

    例えば $("p").get() によって jQuery("p") インスタンスを取り出すことが出来ます。但し配列として、と言う制限付きであり、かつその要素は String ではないので alert("$('p').get()") などで表示させることは出来ません。

    その場合でノードの内容を見ようとすれば、例えば $("p").eq(n-1).text() により、n 番目の p タグ内の文字情報を取得することが出来ます。

     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が開きます。

    201012082037
    200909060759
    200903060031
    200903012228
    200902250127
    200902220958
    200902220217
    200807241813
    200807202245
    200807050116
    200804011946
    200803120931
    200803100150
    200803092205
    200803060108
    200802180015
    200802100133
    200801230043
    200801180637
    200712031806
    FC2 Management