一から勉強させてください( ̄ω ̄;)

最下級エンジニアが日々の学びをアウトプットしていくだけのブログです。

jQueryのイベント処理について (onとliveを比較してみたり)

今回は、jQueryのイベント処理について書きたいと思います。

この前、何気なくjQueryでクリックイベントを登録しようとしたら、それが全然効かなくてくっそハマってしまいました。。

その時の復習(復讐)のための記事でございます!!


まずハマってしまった時のコードをちょっと改造して簡単なコードを書きました。

p要素をクリックしたら新しいp要素が追加されて、その追加されたp要素をクリックするとまた新しいp要素が、、、て感じの動きを試みています。


こちら(失敗コードです)↓

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<style type="text/css">
			p { background:blue; font-weight:bold; cursor:pointer;
                             margin-top:10px; padding:5px;color:#fff; }
			p.over { background: #ccc; }
  		</style>
	</head>
	<body>
		<p>ここをクリック!</p>
		<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
		<script type="text/javascript">

		//onメソッド→新しいpにはイベント適用されない

		$("p").on("click", function(){
	 	    $(this).after("<p>新たなp要素もクリック!</p>");
		 });

		</script>
	</body>
</html>


できたサンプル↓
http://jsfiddle.net/danimal141/RAmM7/2/embedded/result/


このように元のp要素をクリックしたら、ちゃんと新しくp要素が追加されるのですが、新しいp要素にはそのクリックイベントが反映されていません (;゚д゚)


なぜ!!!
たしか昔,liveメソッドで同じようなことした時はうまくいった(気がする)のに!
なんか仕様変わりましたか?


てなわけで、次にjQueryのバージョンを1.7.1にして、liveメソッドを使って書きなおしてみました。
(jsの部分のみ掲載)

<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
    <script type="text/javascript">
        $("p").live("click", function(){
            $(this).after("<p>追加されたp要素</p>");
	});
 </script>


できたサンプル↓
http://jsfiddle.net/d_animal141/GMsWS/2/embedded/result/


ふむ。うまく動きます。
ちゃんと将来的に追加される要素も含めてイベントが登録されているようです。


じゃあonは?
onメソッドで正しい動きを実現するにはどうすれば良い?


そこで何かヒントはないかと、サイトや本を漁りまくってたら関係ありげな文章をいつも使ってる本で見つけました↓


clickメソッドやonメソッドで登録したイベントは対応する要素で有効となります。
よって兄弟要素や親要素をクリックしても処理は実行されません。これは登録後、追加した要素でも同様です。


ふむ。今回の場合は新たに追加されるp要素は元々あったp要素の兄弟要素に該当します。
つまり上記の文章の通りだとすれば、新しく追加されたp要素にはイベントが登録されないのも理解できます。

じゃあ新しく追加されるp要素にもイベントを登録する方法はどうすれば良いんすか…??


さらに本を読み進めていると、またしても良い感じの文章を見つけました↓


JQueryのイベントはバブリングフェーズで実行されるようになっています。よって親要素へ登録すると既存の子要素はもちろん、これから追加する子要素にもクリック処理が有効となります。これは登録した要素が違う種類であっても同様です。


なるほど (゚∀゚)

JavaScriptのイベントはたしかキャプチャリングフェーズとバブリングフェーズの2種類があったはず。
(キャプチャリング→ターゲット→バブリング)


バブリングフェーズっていうのはそのうちの「イベントターゲットからDOMツリーを上にたどっていく方のフェーズ」のことですな。


今回の場合でいうと、

p要素の親要素にイベントを登録しておけば、新しいp要素がクリックされた時にもバブリングして、p要素の親要素にたどり着いたトコでうまいことイベント処理が適用されると!!


イメージ図(雑)↓

元pにイベント登録した場合

元p(イベント有効)
新p(イベント無効)→→→→親→→→→親→、、、
新p(イベント無効)


p要素の親にイベント登録した場合

元p
新p →→→→親(イベント有効)→→→→親→、、、
新p


ためしにp要素をdiv id="wrap"で囲んで作り直してみました↓

//HTML
<div id="wrap">
    <p>ここをクリック!</p>
</div>


//JS

$("#wrap").on("click",function(){
    $(this).append("<p>新たなp要素もクリック!</p>");
});


できたサンプル↓
http://jsfiddle.net/d_animal141/RAmM7/5/embedded/result/


ふむ。とりあえず思い通りの動きにはなったみたいです。


でもまだ疑問が残ります。。


なぜliveメソッドは初めからうまくいってたんですか?


これも今世紀最高に素晴らしい記事を書いて下さっている方がいらっしゃったので、理解出来ました↓


http://jser.info/post/3307464125


なるほど (゚∀゚)


今回の場合だと、

onメソッドは指定したセレクタに対してイベントを設定している。

一方、

liveメソッドは$(document)に対してイベントを設定しバブリングで上がってきたターゲット要素がCSSセレクタと一致するかを見てイベントを実行してる。


だから上手く動いたのですね!!!

そういえば昔、どこかの誰かが「liveは処理が遅くなるから使うのやめておけ」って言ってたような気もします。
それもこういう事があるからなのですね。


onメソッドの場合でも、delegateと同じように第二引数にセレクタを指定する事ができるので、

$(document).on("click","p",function(){
     $(this).after("<p>新たなp要素もクリック!</p>"
});


って$(document)をセレクタにして書いたほうがよりスマーズに処理できそうです。


まあ$("#wrap")みたいなセレクタを指定してたらあんま関係なさそうですが、

例えば$("p")をセレクタに指定するようなことがあったら、jQueryはいちいち存在するすべてのp要素のjQueryオブジェクトを生成してしまうので、処理が遅くなります。

状況に応じて、適切なセレクタ指定をする意識が大切なのだと思います。


長くなってしまいましたが、今回は楽勝だとナメてかかってたイベント処理にめちゃめちゃハマリ、おかげでイベント処理の基礎を見直すきっかけとなりました。


こういう基礎を積み重ねて、もっともっと成長していきたいもんです。


小さなことからコツコツと。