「これはヤバかったな」という遅刻エピソード

jQueryを学習中の者です。
現在、パララックス効果のサンプルコードを参考にしているのですが、理解できずに困っています。
その内容は各ブロック要素をeachでループさせ、ループの中でウインドウのスクロールイベントを登録しコールバック関数で細かい設定をしていくというものです。
理解できないというのは、以前 javasctiptでのfor文による要素へのイベント登録で「イベントでも渡す関数は参照のみです。新たに関数を自動で作成してくれるような事は決してありません。」と説明が有り、コールバック関数内に変数を使うとイベント発生時に変数を参照した時、ループの最後で格納した値になるので、この事は理解し注意もするようにしていました。
しかし、今回のサンプルコードではforとeachの違いはあるけれど、コールバック関数に変数を使っているのに、その変数が別々の値を持っています。
最初はjQueryとjavascriptの挙動の違いかなと思い、以前のfor文のコードを下の様にjQueryで書き替えても結果は一緒でした。
取り留めの無い文章になってしまいましたが、どうして下の2つコードが違う結果になるのかを解り易く説明していただけませんでしょうか。
<table border="1">
<tr><td></td><td></td>...</tr>
...
</table>
<script>
(1)//これだと、駄目…どのtdのイベントでも最後のtdが反応
$(function(){
for(var i =0 ,len=$('td').length;i < len; i++){
  var td=$('td').eq(i);
  td.mouseover(function(){ td.css('background-color','red');});
  td.mouseout(function(){ td.css('background-color','');});
 }
});

(2)//これだとOK…ちゃんと各tdが反応する。
$(function(){
 $('td').each(function(index,elem){
  var td=$(elem);
   td.mouseover(function(){ td.css('background-color','red');});
   td.mouseout(function(){ td.css('background-color','');});
 });
});
</script>

*今回の質問はどう対処したら良いのかという類ではありません。
両方とも変数 td にjqueryオブジェクトを格納して、同じ形式でイベント登録しているのに違う結果になるという事が壁に成ってます。
ネットでいくら調べても構文の説明やサンプルコードは溢れていますが、この様な事例に触れた物が皆無です。
forとeachの違いなんて基本的なことで恥ずかしいのですが、お願いします。

A 回答 (2件)

01function A(){


02 var x = 0;
03
04 function B(){
05   var x = 2;
06   console.log(x);
07 }
08
09 function C(){
10   console.log(x);
11 }
12
13 x = 1;
14 B();
15 C();
16 x = 3;
17 B();
18 C();
19}

B/C関数を定義・・・4行目.9行目
B/C関数を実行・・・14行目.15行目.17行目.18行目

さて、各関数のxは何が表示されるでしょうか?

B関数は、A関数のxとはB関数のxを使用します。
対してC関数はxの宣言が無いので、A関数のxを使用します。

その為
14行目のB関数は2を表示
15行目のC関数は1を表示(13行目で1を入れている)
17行目のB関数は2を表示
18行目のC関数は3を表示(16行目で3を入れている)

これを理解したら、質問の内容に戻って(2)を少し分解してみましょう。

$(function){
 function A(index, element){
  var td=$(elem);

  function B1(){ td.css('background-color','red'); }
  function B2(){ td.css('background-color','');}

  td.mouseover(B1);
  td.mouseout(B2);
 }
 
 $('td').each(A);
};

B1/B2関数のtdはA関数のtdが使われるのは理解できると思います。
A関数は$.each関数で何度も実行されますが、
B1/B2関数で使われるtdは各A関数が実行した時のtdなので、正しく動くわけです。

eachではなく、以下のようにA関数を実行したと考えると分かりやすいかもしれません。
var $td = $('td');
A(0, $td[0]);
A(1, $td[1]);
    • good
    • 0
この回答へのお礼

再度の回答と詳細な説明、ありがとうございます。
回答の読み初めで、変数のスコープは分かってるんだけどな…と生意気な事を思ってしまいましたが、その後の分解式で目から鱗が落ちました。
そうですよね、forは要素の数だけ処理を繰り返してるだけ、eachは関数の実行を繰り返してる。
今まではeachを使っても取得や設定だけで、jQueryで初めてeachによる関数の実行に触れたので、思い込みで視界が狭くなっていたと反省してます。
これで、先に進めます。
ありがとうございました。

お礼日時:2015/01/26 01:35

変数は関数の先頭で宣言されたものとして扱われます。



よって、(1)は以下と同等です。

$(function(){
var td;
for(var i =0 ,len=$('td').length;i < len; i++){
  td=$('td').eq(i);
  td.mouseover(function(){ td.css('background-color','red');});
  td.mouseout(function(){ td.css('background-color','');});
 }
});

問題の関数が実行される時、forループは回り後っているので、tdの中身は最後のtd
そのため(1)はうまく動きません。

詳しく知りたい場合は、javascriptの変数スコープについて調べると良いです。
    • good
    • 0
この回答へのお礼

回答、ありがとうございます。
おしゃる通りで(1)のコードの結果は正しいというか、自分の理解に沿ったものになります。
しかし、理解できないのは(2)のコードの結果です。
イベント登録している変数tdとコールバックの関数式のtdとの結び付きはループ処理内では出来ていないと、認識しているのですが、何故か(2)では各セルでコールバック関数が反映しちゃてるんですよね。
ループ処理でもforとeachは結果が違う物なのでしょうか?

お礼日時:2015/01/25 19:11

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


おすすめ情報