質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

3回答

10138閲覧

JavaScriptにて、配列の要素一つ一つにイベントを実装したい

退会済みユーザー

退会済みユーザー

総合スコア0

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

4クリップ

投稿2015/09/08 06:54

編集2015/09/08 06:55

いつもお世話になっています
Javascriptでのイベント処理にてわからないことがありましたので、質問させて頂きました
サムネイルの様に、複数枚並んだ画像全てにイベント処理を実装したく、下記のようなコードを記述しましたが、上手くいきませんでした(show関数が実行されなかったり、iが配列の最大値の時のみ実行されたりします)

この記述がなぜ上手くいかないのか、どうすれば上手くいくのかを、教えて頂ければと思います
お手数ですが、よろしくお願い申し上げます

lang

1// 関数定義 2function show(x){ 3 console.log(x); 4} 5 6// イベント処理 7var img = document.getElementsByTagName('img'); 8 9for(var i = 0; i < img.length; i++){ 10 img[i].onclick = function(){ 11 show(i); 12 }; 13}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

click が呼び出された時点で呼び出される変数 ifor 文を通過した後に参照される為ですね。
変数 i を束縛する方法はいくつかありますが、handleEvent を使用する方法はどうでしょうか。
(JSFiddle にもサンプルをUPしました。)

HTML

1<img src="sample-1.jpg" alt="sample1"> 2<img src="sample-2.jpg" alt="sample2"> 3 4<script> 5(function () {i 6 function handleClick (event) { 7 console.log(this.x); 8 } 9 10 var imgs = document.getElementsByTagName('img'); 11 12 for (var i = 0, l = imgs.length; i < l; ++i) { 13 imgs[i].addEventListener('click', {x: i, handleEvent: handleClick}, false); 14 } 15}()); 16</script>

handleEventaddEventListener に備わった機能なので第一引数の event オブジェクトと併用できます。


(2015/09/08 19:14追記)
もし、IE8- にも対応させるならクロージャで変数束縛する方法があります。
(JSFiddleにサンプルUPしました。)

JavaScript

1(function () { 2 function createHandleClick (i) { 3 return function handleClick (event) { 4 console.log(i, event.type); 5 }; 6 } 7 8 function addEvent (element, type, listener) { 9 if (element.addEventListener) { 10 element.addEventListener(type, listener, false); 11 } else if (element.attachEvent) { 12 element.attachEvent('on' + type, listener); 13 } 14 } 15 16 function init () { 17 var imgs = document.getElementsByTagName('img'); 18 19 for (var i = 0, l = imgs.length; i < l; ++i) { 20 addEvent(imgs[i], 'click', createHandleClick(i)); 21 } 22 } 23 24 init(); 25}());

(2015/09/08 23:00追記)
data-* 属性で index を割り振っておくのも一つの手段だと思います。
(JSFiddleにサンプルUPしました)

HTML

1<img src="sample-1.jpg" alt="sample1" data-i="1"> 2<img src="sample-2.jpg" alt="sample2" data-i="2"> 3 4<script> 5(function () { 6 function handleClick (event) { 7 var img = event.target || event.srcElement; // target要素を得る(Standard || IE8-) 8 console.log(img.getAttribute('data-i'), event.type); // data-*属性を参照 9 } 10 11 function addEvent (element, type, listener) { 12 if (element.addEventListener) { // Standard 13 element.addEventListener(type, listener, false); 14 } else if (element.attachEvent) { // IE8- 15 element.attachEvent('on' + type, listener); 16 } 17 } 18 19 function init () { 20 var imgs = document.getElementsByTagName('img'); 21 22 for (var i = 0, l = imgs.length; i < l; ++i) { 23 addEvent(imgs[i], 'click', handleClick); 24 } 25 } 26 27 init(); 28}()); 29</script>

img要素の親要素ノードでバブリングを利用してキャプチャすると更にスマートになります。

投稿2015/09/08 07:50

編集2015/09/08 14:00
think49

総合スコア18162

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2015/09/08 08:30

なるほど、for文を抜けた後でイベントが呼び出されるのが原因みたいですね 自分のJavaScriptに関する知識が浅いので、上記のコードをきちんと理解できているか不安なのですが handleEventを持つオブジェクトにiの値を保持しておくという認識で正しいでしょうか?
think49

2015/09/08 08:40

addEventListener の第二引数にオブジェクトが指定された場合、ハンドラ関数は handleEvent プロパティを参照し、this 値は第二引数のオブジェクト {x: i, handleEvent: handleClick} に束縛されます。 つまり、下記コードとほぼ等価です。 imgs[i].addEventListener('click', handleClick.bind({x: i, handleEvent: handleClick}), false); listener 関数内で this 値で event.currentTarget を参照しなければこの方法に伴う問題は発生しないと思われます。
退会済みユーザー

退会済みユーザー

2015/09/08 13:10

自分の知識が浅く、丁寧な回答をしていただいたにもかかわらず、あまり理解することができませんでした。申し訳ありません ただ 1.メソッドをオブジェクトで束縛するためには、bindする必要がある 2.handleEventプロパティを使うことで、addEvantListenerにオブジェクトを指定することができる 3.上記1・2のどちらでもオブジェクト固有のイベント処理を行うことができる かなと思いました もう少し理解を深めた上で、回答を見直したく思います
think49

2015/09/08 14:08

大雑把に説明すれば、「listener関数に引数を渡すのは現実的ではないので this 値に束縛して渡しましょう」という事です。 this 値の束縛として Function#call, Function#apply, Function#bind の挙動を確かめておくと理解できると思います。 (function () { console.log(this.x); // 1 }.call({x: 1})); 親コメントでクロージャ版、data-* 属性版コードも追記しましたのでよろしければ参考にして下さい。 私としては data-* 属性を使う手法がお勧めです。 index による割り振りは動的にimg要素が増えただけで変化する不安定なものなのでしっかりとした固定値を割り当てた方がぶれなくて良いと思います。 場合によっては、alt属性値やsrc属性値(画像ファイル名)にindex値に類する情報があるので、それらの属性値から正規表現でindex値を抽出するのも有です。
guest

0

どういう処理を行うかわかりませんが、
私なら次のように書きます。

ポイントは2つあるんですが、
1つ目は基本ですがimgタグの後にscriptタグを書く
2つ目はiの評価がクリック時に行われる書き方なのでbindでその時の値を渡しておく
って感じです。
それぞれshow関数が実行されない件と、iが配列の最大値で実行される件の対策です。
参考になると良いのですが。

<img src="image.jpg" width="20px"> <img src="image2.jpg" width="20px"> <script> // 関数定義 function show(x){ console.log(x); } // イベント処理 var img = document.getElementsByTagName('img'); for(var i = 0; i < img.length; i++){ img[i].onclick = show.bind(img[i], i) } </script>

投稿2015/09/08 07:24

notable

総合スコア415

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2015/09/08 08:04

すみません、説明が不足していました スクリプトは別ファイルとして記述しており、htmlファイル内に <img src="image.jpg" width="20px"> <img src="image2.jpg" width="20px"> のような記述がされているという設計です bindメソッドを使うのが肝みたいですね あまりbindメソッドを理解しきれていないのですが bindメソッドを使用した場合はimg要素一つ一つにshowメソッドが作られ、イベントとして登録されるのに対し 使用しない場合は同一のshowメソッドが参照されてしまうということでしょうか?
notable

2015/09/08 08:25

> スクリプトは別ファイルとして記述しており、htmlファイル内に > <img src="image.jpg" width="20px"> > <img src="image2.jpg" width="20px"> > のような記述がされているという設計です 念のためこちらにも返答するのですが、 別ファイルでも問題ないです。 ただし対象となるimgタグはスクリプトを読み込む前に記述する必要があります。 <img src="image.jpg" width="20px"> <img src="image2.jpg" width="20px"> <script src="hoge.js"></script> のような感じですね。 > bindメソッドを使用した場合はimg要素一つ一つにshowメソッドが作られ、イベントとして登録されるのに対し > 使用しない場合は同一のshowメソッドが参照されてしまうということでしょうか? はい。私の理解ではそうです。 さらに、使用しない場合はiが評価されるのが実行時(今回の場合はクリック時)なので、 show()のconsole.log(x)は毎回「2」が表示される動きになっていました。 bindを使うことによって引数を渡した状態にしておくことができるので、 今回はbindを使いました。
think49

2015/09/08 08:45

質問文のサンプルコードでは問題になりませんが、Function#bind で第二引数を指定すると、listener 関数の第一引数に存在した event オブジェクトを受け取れなくなる点に注意が必要です。 this 値の束縛に留めておけば、event オブジェクトも受け取れます。
notable

2015/09/08 09:30

think49さん いつも質問の内容のみに絞って考えているので、 こういったご指摘は助かります。 eventが必要な場合は単純に bind(this, i); に すれば取れる気がしますが、何か問題ありますか…? 質問になってしまってすみません。 もしご覧になられていたら回答いただけると助かります。 rin_kataさん そういえば私の書いたbindを使う方法はIE8以前では使えません。 対応すべきブラウザがIE9以降でない場合は別の実装にする必要があります。 その場合は色々大変なので rk7fd3s さんの仰るようにjQueryを使うのが手っ取り早いですね…
think49

2015/09/08 09:58

To: notableさん > eventが必要な場合は単純に bind(this, i); にすれば取れる気がしますが、何か問題ありますか…? Function#bind の第二引数で event が渡るべき第一引数を束縛しているので event を参照できません。 http://jsfiddle.net/m627kwag/2/ bind({i: i}) なら event を参照可能です。 http://jsfiddle.net/m627kwag/4/ IE8- を視野に入れるならクロージャで変数束縛が使えますが、そもそも論として index 束縛がどうかという気もするので data-* 属性に埋め込んでおくスタイルもありだと思いました。
退会済みユーザー

退会済みユーザー

2015/09/08 13:39

notableさん >使用しない場合はiが評価されるのが実行時(今回の場合はクリック時)なので、 show()のconsole.log(x)は毎回「2」が表示される動きになっていました。 なるほど、bindを使わないと同じshow()が参照されるから全部「2」になっちゃうみたいですね 丁寧な回答、ありがとうございます
notable

2015/09/09 00:01

think49さん JSFiddleまで用意いただいてありがとうございます。 ちょっと伝わってなかったかもしれないのですが、 handleClick.bind(imgs[i], i) を handleClick.bind(this, i) にして、 console.log(i, i.type); を console.log(i, this.event.type); にすると取れるなと思った次第です。 これ以上は元の質問の内容と外れるので回答いただかなくても大丈夫です。 丁寧に対応いただいてありがとうございました。 rin_kataさん 同じshow()が参照されるのは問題ないのですが、 show()が実行されたときのiの値が引数として渡ってくるのが 問題という意味でした。 for文が終わるタイミングとクリックされるタイミングでは 後者のほうが遅いので必ず「2」が出力される状態ということです。
guest

0

jQueryを使ったサンプルをあげます。

html

1<img class="thumbnail" src="image.jpg" width="20px"> 2<img class="thumbnail" src="image2.jpg" width="20px"> 3 4<script> 5$(document).ready(function(){ 6 // thumbnailクラスが付いているものすべてをjQueryオブジェクで取得 7 var $thumbnails = $('.thumbnail'); 8 9 // クリックイベントを定義 10 $thumbnails.on('click', function(e) { 11 // 実処理関数にjQueryオブジェクトで引き渡す 12 clickEvent($(this)); 13 }); 14 15 /** 16 * サムネイルクリックイベント 17 * param $n サムネイル画像タグのjQueryオブジェクト 18 * 19 **/ 20 var clickEvent = function($n) { 21 console.log($n.attr('src')); 22 }; 23}); 24</script>

コメントを充実させたので、少し長く見えますが、実コードはすごくシンプルになります。
jQueryの導入や賛否は置いておいて。。。。

投稿2015/09/08 09:01

rk7fd3s

総合スコア61

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2015/09/08 13:50

rk7fd3sさん すばらしいですね、for文で回す必要もないっていうw 他の方も指摘されていますが、各ブラウザへの対応等を考えると、jQueryの導入は有効な解決策だと思います 回答ありがとうございます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問