セレクトのid「hoge0」を変更すると「クリック0」
セレクトのid「hoge1」を変更すると「クリック1」
としたいのですが、どちらも「クリック1」になります。
いろいろ調べた結果、クロージャーでできそうな気がしますが、
やり方がわかりません。
<select id="hoge0"> <option value="1">1</option> <option value="2">2</option> </select> <br> <select id="hoge1"> <option value="3">3</option> <option value="4">4</option> </select> <script> for(i=0;i<2;i++){ var piyo = document.getElementById('hoge' + i); piyo.addEventListener('click', function() { alert('クリック' + i);}, false); } </script>
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
ベストアンサー
イベント駆動の洗礼へよおこそ
原因はJavaScriptのイベントとして登録された関数が何時発火するかにまだ理解が追いついて居ないからですね。
JavaScriptはイベント置き場というものがあり、
そこに「達成条件」と「実行して欲しい関数」をセットで登録します。
イベント登録を受け付けたという情報だけ持って次の行へ進みます。
JavaScriptの既存処理を全て完了し、暇になったら「達成条件を満たしたイベントはないかな?」と巡回を繰り返し、達成したイベントに紐付いている関数を実行します。
(詳しくはイベントループで調べてください)
JavaScript
1for (var i = 0; i < 2; i++) { 2 setTimeout(function(){ 3 console.log(i + 1); 4 }, 0); 5} 6// 3 7// 3
今回はsetTimeout(fn, 0);
として0ミリ秒後に実行するようにイベント登録を行いました。
しかし、イベント登録を挟んでいる為に、イベント登録を行いfor文を抜ける方が優先されます。
なので変数iのカウンターが終了条件を満たす2になってから、ようやく各関数が作動します。
関数が作動したときに改めて変数iを確認すると…
for文を処理するのに増えてforループを抜けた時の値である2
を読み取る事が可能です。
結果、それに1を追加して3を画面表示することになりました。
解決策
使ってしまう
後で参照するから駄目なんですよ。
その場で使ってしまうのが一番です。
即時実行関数で包むのがわかりやすいでしょう。
JavaScript
1for (var i = 0; i < 2; i++) { 2 (function(index){ 3 setTimeout(function(){ 4 console.log(index + 1); 5 }, 100); 6 })(i); 7} 8// 1 9// 2
他にも関数はFunction.prototype.bindで
引数を設定しながら関数発火だけを待たせるということも可能です。
JavaScript
1for (var i = 0; i < 2; i++) { 2 setTimeout(console.log.bind(null, i + 1), 100); 3} 4// 1 5// 2
letを使う
let文はES2015で追加された変数宣言時の構文です。
letをfor文の中で使った場合、毎ループで変数スコープを作り保持されます。
変数iが1の時にイベント登録された関数が変数iを呼び出すと、
ループを既に抜けてしまっておりi=2になっているにも関わらず、
確実に1を取り出す事が可能になります。
同様の問題で悩んだりハマる開発者がそれだけ多かったということですね。
※IE11はletというキーワードを使っての変数宣言には対応していますが、このブロック絡みの仕様は対応しきれておらず動きません。
クロージャーでできそう
同期処理の中でやってるので動きますね。
ちょっと試してみましょうか。
JavaScript
1// クロージャ版 2for (var i = 0; i < 2; i++) { 3 setTimeout((function(){ 4 var index = i + 1; 5 return function(){ 6 console.log(index); 7 } 8 })(), 100); 9} 10// 1 11// 2
動きました。
ですがまぁ、クロージャーは覚える必要はそんなにないと思います。
毎回こんな煩わしいこと書いてられませんしね。
投稿2019/03/05 03:10
総合スコア21158
0
for文が好きでない方のために一応別解
※IE polyfill追記しました
javascript
1<script> 2//IE polyfill 3if(!Array.prototype.fill){ 4 Array.prototype.fill=function(v){ 5 for(var i=0;i<this.length;i++) this[i]=v; 6 return this; 7 } 8} 9window.addEventListener('DOMContentLoaded', function(e){ 10 Array(2).fill(null).forEach(function(i,j){ 11 document.querySelector('#hoge'+j).addEventListener('click', function(e) { 12 alert('クリック' + j); 13 }); 14 }); 15}); 16</script> 17<select id="hoge0"> 18 <option value="1">1</option> 19 <option value="2">2</option> 20</select> 21<br> 22<select id="hoge1"> 23 <option value="3">3</option> 24 <option value="4">4</option> 25</select> 26<br> 27<select><--対象外--> 28 <option value="5">5</option> 29 <option value="6">6</option> 30</select>
投稿2019/03/05 00:41
編集2019/03/05 03:03総合スコア114814
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/03/05 03:06
2019/03/07 06:44
0
いわゆるクロージャーなら
javascript
1 piyo.addEventListener('click', (function (a) { return function () { alert('クリック' + a)}})(i), false); 2
そういうのは、document で監視するべき。一行で、
javascript
1document.addEventListener('click',e=>alert(e.target.id),!1);
投稿2019/03/04 16:24
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
let
とブロックでいいと思います。
js
1for(i=0;i<2;i++){ 2 var piyo = document.getElementById('hoge' + i); 3 { 4 let j = i; 5 piyo.addEventListener('click', function() { alert('クリック' + j);}, false); 6 } 7}
投稿2019/03/04 16:05
総合スコア36087
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/03/07 06:40