JQueryでイベントブロッキング

前回の jQueryで一回だけイベント - なんたらノート 第二期 でちょっとツメが甘かったので、もうちょっと具体的に言うと、ということで、イベントブロッキングを考えます。

たとえば、ユーザのクリックによって、アニメーションエフェクトが起こったとき、同じものをもう一度クリックできたりすると、イベントが二重に発生して、エフェクトがちょっと不恰好になることがあります。アニメーション効果が完了するまで、イベントをブロックしておいて、演出が終わったら再度クリックできるようになる、というのを作ってみましょう。

function showinfobox() {
  var q = $(this);
  q.unbind('click', showinfobox);
  $('#infobox').show(function() { q.click(showinfobox); });
  return false;
}
$('#clickme').click(showinfobox);

あまりスマートじゃないですね。関数が使われ方、つまり「何にアサインされるか」、を意識しすぎです。
どのように使われるかが固定されるていて、それが1回しか使われないようなものは、名前を持った再利用可能な「関数定義」である必要はありません。というか、むしろ再利用できないほうがいいです。

これを関数リテラルにできるでしょうか。

$('#clickme').click(function() {
  var q = $(this);
  var f = ... //関数自身へのポインタが欲しいけど、この関数には名前がないから参照できない!?
  q.unbind('click', f);
  $('#infobox').show(function() { q.click(f); });
  return false;
});

関数を指す変数がないと、ブロックのためのunbindも、再開のための再bindもできませんね。例の、カレント呼び出しコンテキストのthisみたいな感じで、「この関数」を指す変数がないか... あります。Javascript予約語、argumentsに入っています。

$('#clickme').click(function() {
  var q = $(this);
  var f = arguments.callee;
  q.unbind('click', f);
  $('#infobox').show(function() { q.click(f); });
  return false;
});

うん、実用的。さらに、このクリックイベントの中が条件付きになる予定が全くないなら、もっと短く、こうなります。

$('#clickme').one('click', function() {
  var q = $(this);
  var f = arguments.callee;
  $('#infobox').show(function() { q.one('click', f); });
  return false;
});