2016/09/02 15:51:22

イベントハンドラを変数に代入した場合の引数の扱いについて

目次(クリックするとジャンプします)
  • 1:イベントハンドラ
  • 1.1:イベントハンドラの例
  • 1.2:代入した場合のイベントハンドラ
  • 1.3:イベントハンドラに引数を持たせたい
  • 1.4:期待した動きにならない理由をさらに検証
  • 2:イベントハンドラを変数に代入し引数を持たせたい場合の記述
  • 2.1:関数を変数に代入
  • 2.2:引数を取る場合
  • 2.3:イベントハンドラの要件
  • 3:まとめ

イベントハンドラ

イベントハンドラの例

イベントが起きたとき実行する関数をイベントハンドラと呼ぶ。 jQueryを用いた例だが以下の様な書き方をします。

$("#hoge").hover(
function(){
   //何らかの処理
},
function(){
   //何らかの処理
});

上記ようにhoverの引数に直接イベントハンドラとなる関数を無名関数で記述する場合もありますが、複数の要素に同じイベントハンドラを割り当てるときなどは、いちいちイベントハンドラを直接記述していてはコードの可読性を損なってしまうし、記述ミスが起きやすいです。

代入した場合のイベントハンドラ

そんな時は関数を変数に代入して使いまわし、コードを短くする記述を用いるでしょう。すなわち以下の様な記述の方法です。

var func_a = function(){
     //何らかの処理
};

var func_b = function(){
     //何らかの処理
};

$("#hage").hover(func_a,func_b);
$("#hige").hover(func_a,func_b);
$("#huge").hover(func_a,func_b);
$("#hege").hover(func_a,func_b);
$("#hoge").hover(func_a,func_b);

イベントハンドラに引数を持たせたい

それではfunc_afunc_bに引数を持たせたい場合はどうだろうか。 以下の様な記述が考えられます。

var func_a = function(a){
     $(a).css("background-color","#000000");
};

var func_b = function(b){
     $(a).css("background-color","#ffffff");
};

$("#hoge").hover(func_a,func_b);

上記は#hoge要素にマウスが乗ったらバックグラウンドカラーを黒くし、マウスがはずれたら白くするという動作を期待するものです。

しかし残念ながらこの記述では期待した動作は得られないです。(ロード後にすぐ#hogeが真っ白になるかもしれない)

なぜ上記の記述だと期待した動作にならないのでしょうか?

期待した動きにならない理由をさらに検証

そこでよりわかりやすくするため、以下のコードを試していただきたいです。

var func_a = function(a){
     alert("func_a" + a);
};

var func_b = function(b){
     alert("func_b" + b);
};

$("#hoge").hover(func_a("ホバー!!"),func_b("ホバー??"));

このコードを実行すると、#hogeにマウスがホバーしていないにも関わらず、アラートが「func_aホバー!!」「func_bホバー??」と立て続けに出てしまうはずです。hoverイベントが起こっていないのに実行されてしまうのです。

これは関数定義にちょっとした落とし穴があります。実は以下の様に「関数を戻り値として定義」する必要があります。

var func_a = function(a){
     return function(){alert("func_a" + a);};
};

var func_b = function(b){
     return function(){alert("func_b" + b);};
};

イベントハンドラを変数に代入し引数を持たせたい場合の記述

関数を変数に代入

順を追って説明したいと思います。

まず関数を変数に代入するときの以下の記述。

var func_a = function(){
     //何らかの処理
};

この時、変数func_aに入っているのは(すこし語弊があるが)「functionの定義そのもの=関数そのもの」であると捉えてもらうと解かりやすいかもしれないです。もし引数を用いず関数を実行させる場合

func_a;

と記述することになりますが、これは

function(){
     //何らかの処理
};

と記述したのと同じ事だと捉えてもらって構わないです。

引数を取る場合

引数を取る場合、以下の様な定義になり、

var func_a = function(a){
     //何らかの処理
};

実際に関数を使う場合は、

func_a("hoge");

といった形になりますが、この時のfunc_aは(これもすこし語弊があるが)「functionを実行した結果」になります。つまりこの場合のfunc_aは「関数そのもの」ではなくなっているのです。

イメージとしては以下の様な感じです。置き換えてみると一目瞭然でしょう。

var func_a = function(a){
     var arg = a * 2;//←ここがそのまま置き換わるイメージ
     console.log(arg);
};
var func_a = function(b){
     var arg = b * 3;//←ここがそのまま置き換わるイメージ
     console.log(arg);
};

$("#hoge").hover(var arg = b * 3,var arg = a * 2);//←置き換わったイメージ

イベントハンドラの要件

ベントハンドラは関数である必要がありますので、関数以外をイベントハンドラ位置に記述しても、イベントにアタッチしたことにはならず期待した動作にはなりません。上記の記述はそれと同じことをやってしまっている事になります。var arg = b * 3などは式なので、イベントハンドラとして有効にならないのです。

以下が期待した動作をしてくれる記述です。

var func_a = function(a){
     return function(){//←ここがそのまま置き換わるイメージ
         var arg = a * 2;
         console.log(arg);
      };
};

var func_a = function(b){
     return function(){//←ここがそのまま置き換わるイメージ
         var arg = b * 3;
         console.log(arg);
      };
};

この記述であれば、関数を戻り値としていますので、イベントハンドラとして有効になります。 イメージとしては以下の様に記述したのと同じ事です。

$("#hoge").hover(
    function(){
       var arg = a * 2;
         console.log(arg);
    },
    function(){
       var arg = b * 3;
         console.log(arg);
    }
);

順を追っていけば理解できることですが、このことは意外と落とし穴となる箇所かもしれないです。

まとめ

イベントハンドラを使いこなせるようになると、やれることが非常に多くなります。しかし反面難しくなっていく部分でもあるので嵌ってしまう可能性も大です。

@MINOなどは幾度も嵌ってもう泣きそうです。今では自分から嵌りに行っているのではないかと思うほどです。がんばりたいです。