LoginSignup
18
17

More than 5 years have passed since last update.

jQueryのコールバックの中でthisを使うメソッドを呼びたい

Last updated at Posted at 2014-10-31

いいタイトルが思いつかなかったのですが、javascriptの method.call の使い方がようやく分かったので備忘録。

tl;dr

//インフォメーション用のDomのテキストを変更するメソッド
function chMsg(text){...}

$('input.responsible')
.on('click', function(){ chMsg('click'); })
.on('change', function(){ chMsg('update!'); })
.on('input', function(){ chMsg('edit...'); });

みたいなことがしたいときに、chMsgthisを渡すには、callを使って以下のように書けば良さそうです。

function chMsg(text){
  $(this).parent().find('.infomation').text(text);
}

$('input.responsible')
.on('click', function(){ chMsg.call(this, 'click'); })
.on('change', function(){ chMsg.call(this, 'update!'); })
.on('input', function(){ chMsg.call(this, 'edit...'); });

経緯

※面倒くさいので、ここからはCoffeeScriptで記述

「テキストフィールドの入力内容をチェックしたい。間違ってるとテキストフィールドが赤枠になるとか」
「やりましょう」

$(text_field).on 'change', ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
  else
    $self.addClass('no-good')

「これだと入力後じゃなくて入力と同時にチェックしてよ」
「たしかに」

$(text_field).on 'input', ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
  else
    $self.addClass('no-good')

「あー。入力後にチェック失敗したなら、何処かにメッセージ出すと親切だよね」
「ふむ」

$(text_field).on 'input', ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
  else
    $self.addClass('no-good')
.on 'change', ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
    $self.parent.find('span').text('いいよ!')
  else
    $self.addClass('no-good')
    $self.parent.find('span').text('おこだよ')

(クラスを付けたり消したりするところは、外に出したいな…)

inputValid ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
  else
    $self.addClass('no-good')

$(text_field)
.on 'input', inputValid
.on 'change', ->
  inputValid
  if myValidate this.value
    $self.parent.find('span').text('いいよ!')
  else
    $self.parent.find('span').text('おこだよ')

と、ここまで来ると、changeイベントの時に外に出したinputValidthisでテキストフィールドの値を取得できていないことによる不都合が置きます。

いくつかのjQueryプラグインを参考にすると、以下のように記述して、サブメソッドにイベントのthisを渡しているようでした。

sub_method = ->
 # thisを使った処理

$(targets).on 'change', ->
  # thisを渡すためにeachを使う
  $(this).each sub_method

このような書き方だとChrome / Firefoxでは動作するのですが、何故かIE8ではjQuery内部でエラーが起きて動かない…jQuery内部まで見て原因を調べるのも手間なので別の方法を探りました。

試しにjQuery.fn.eachについて調べてみると以下のようになっています。
(元のコードはjavascriptです)

# jQuery.fn.each
(callback, args)->
  jQuery.each( this, callback, args )

# jQuery.each
(obj, callback, args) ->
  value = undefined
  i = 0
  length = obj.length
  isArray = isArraylike(obj)

  # (※1) argsがある場合の処理..今回は関係ない
  if args
    if isArray
      while i < length
        value = callback.apply(obj[i], args)
        break  if value is false
        i++
    else
      for i of obj
        value = callback.apply(obj[i], args)
        break  if value is false

  # A special, fast, case for the most common use of each
  else
    if isArray
      while i < length
        value = callback.call(obj[i], i, obj[i]) # ←これだ!
        break  if value is false
        i++
    else
      for i of obj
        value = callback.call(obj[i], i, obj[i])
        break  if value is false

  # return
  obj

なるほど。eachでは結局、callをつかってコールバックを動かしているようですね。
というわけで、以下のように書いてみます

inputValid ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
  else
    $self.addClass('no-good')

$(text_field)
.on 'input', inputValid
.on 'change', ->
  inputValid.call(this)  # thisを渡して動かす
  if myValidate this.value
    $self.parent.find('span').text('いいよ!')
  else
    $self.parent.find('span').text('おこだよ')

ちゃんとIE8でも動きました。よく考えたら、eachと同じことをしているのに、なんで動いたのか謎です。ループなどの他の部分でひっかかっていたのかな…?

ところでjQuery.fn.eachに第二引数を渡せたんだ

上で引用しているjQuery.eachのコードだと、第二引数にargsがある場合、jQuery.eachの(※1)の部分が呼ばれるようです。

$('li').each (a,b,c)->
  console.log this #=> <li ...>, <li ...>, <li ...>,...
  console.log a #=> 0,1,2,...
  console.log b #=> <li ...>, <li ...>, <li ...>,...
  console.log c #=> undefined , undefined , undefined ...


$('li').each (a,b,c)->
  console.log this #=> <li ...>, <li ...>, <li ...>,...
  console.log a #=> 'hello', 'hello', 'hello', ...
  console.log b #=> 100,100,100,...
  console.log c #=> Window, Window, Window,...
, ['hello', 100, this]

使いドコロが分かりません…
古いバージョンと互換性を保つために残っている使い方なのかとも思ったのですが、どうもjQueryの内部でjQuery.fn.loadというAjax関連のメソッドで第二引数を活用しているっぽいです。
jQueryのAPIドキュメントには第二引数を渡す使い方は書かれていないので、非推奨なのかな…?

なおjQueryの軽量クローンであるZeptoでは、第二引数をしたりはできなさそう。

参考

18
17
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
17