🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

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

Q&A

解決済

2回答

1438閲覧

変数から値が展開されるタイミングについて

slimat

総合スコア57

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

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

0グッド

0クリップ

投稿2019/12/15 15:26

編集2019/12/16 00:01

こんにちは.

addEventHandlerを使用して, 特定のボタン(回答ボタン1-4)にイベントハンドラ(checkTheAnswer関数を数値1-4を実引数として呼び出すコードを含む)ー①を追加したいです.
現状, handleEventメソッドに①を追加しているのですが, イベントハンドラ内の変数xがfor文終了後インクリメントされて5となり, どの回答ボタンを押してもcheckTheAnswer(5)が呼び出されてしまいます.
どうしたら回答ボタン番号に合うように, checkTheAnswer関数呼び出しの引数を1-4にできるのでしょうか.

また, このような動きに(5になってしまう)なる仕組みは何と検索するれば解説ページを見れるのでしょうか.

html

1<!DOCTYPE html> 2<html> 3<head> 4 <base target="_top"> 5 <script src="https://code.jquery.com/jquery-3.1.1.js"></script> 6</head> 7<body> 8 <p id="qNum">おはよう</p> 9 <p id='qSentence'>こんにちは</p> <!-- 問題文を取得 --> 10 <ol> 11 <li id='q1'></li> <!-- 選択肢1を表示 --> 12 <li id='q2'></li> 13 <li id='q3'></li> 14 <li id='q4'></li> 15 </ol> 16 17 <input type="button" id="Button1" value="1"> 18 <input type="button" id="Button2" value="2"> 19 <input type="button" id="Button3" value="3"> 20 <input type="button" id="Button4" value="4"> 21 22 <p id='CorrectOrMiss'></p> 23 <input type="button" id="next" value="次に進む"> 24 25 <script> 26 var countObj = { 27 count: 0, 28 qNumBefore: 0, 29 countPlus: function() {++this.count;}, 30 four: 0 31 }; 32 33 var closer = function(){ 34 let b = 0; 35 return function(plusOr){ 36 if (plusOr == 1){ 37 return ++b; 38 }else{ 39 return b; 40 } 41 }; 42 }(); 43 44 var checkTheAnswer = async function (clickedNum){ // 回答ボタンがクリックされたときに, 正解・不正解を表示 45 for (var l = 1; l <= 4; ++l){ // 回答ボタンの無効化 46 document.getElementById("Button" + l).style.opacity = 0.3; 47 document.getElementById("Button" + l).onclick = ""; 48 } 49 await google.script.run.withSuccessHandler(dispCorrectOrMiss).withFailureHandler(failedAccess).withUserObject(countObj.count, 'answerButtonClick').answerButtonClick(clickedNum, countObj.count); 50 if (countObj.qNumBefore !== countObj.count){ // 同じ問題番号で2回以上回答ボタンを押せない 51 countObj.qNumBefore = countObj.count; 52 try { // nextボタンを有効化 53 document.getElementById("next").style.background = '#0000ff'; 54 document.getElementById("next").addEventListener("click", nextQuestion, {once:true}); 55 } catch(e){ 56 document.getElementById("Button4").insertAdjacentHTML("afterend","<p>error → hello.html → イベントハンドラの追加時 → " + e.message + "</p>"); 57 } 58 document.getElementById("Button4").insertAdjacentHTML("afterend","<p>functionality added to 次へ進む</p>"); 59 } 60 } 61 62 async function nextQuestion() { // 次の問題文を取得 63 countObj.countPlus(); 64 try { // 問題文の取得 65 await google.script.run.withSuccessHandler(setQuestion).withFailureHandler(failedAccess).withUserObject(countObj.count, 'QAInfo').QAInfo(countObj.count); 66 } catch (e) { 67 document.getElementById("Button4").insertAdjacentHTML('afterend','<p>error → hello.html → 問題文など取得時 → ' + e.message + '</p>'); 68 } 69 document.getElementById("Button4").insertAdjacentHTML('afterend','<p>countObj.four = ' + countObj.four + '</p>'); 70 for (var x = 1; x <= 4; ++x){ 71 document.getElementById("Button" + x).style.opacity = 1; 72 try { // 回答ボタンの有効化 73 document.getElementById("Button" + x).addEventListener("click", {tmp: (() => {countObj.four = x;})(), handleEvent: function handleEvent(event){checkTheAnswer(`${x}`);}}); // 回答ボタンの有効化 74 } catch(e) { 75 document.getElementById("Button4").insertAdjacentHTML('afterend','<p>error → hello.html → 回答ボタン有効化時 → ' + e.message + '</p>'); 76 } 77 } 78 countObj.four = x; 79 document.getElementById("Button4").insertAdjacentHTML('afterend','<p>countObj.four = ' + countObj.four + '</p>'); 80 } 81 82 function setQuestion(data, d){ 83 $("#qNum").html('問題' + d); 84 $("#qSentence").html(data[0][0]); 85 for (var j=1; j<=4; ++j){ 86 $("#q" + j).html(data[0][j]); 87 } 88 } 89 90 function dispCorrectOrMiss(CorrectOrMiss){ 91 if (CorrectOrMiss == 1){ 92 $("#CorrectOrMiss").html('正解'); 93 } else { 94 $("#CorrectOrMiss").html('不正解'); 95 } 96 } 97 98 function failedAccess(numQ, functionName){ 99 document.getElementById("Button4").insertAdjacentHTML('afterend','<p>missed to call' + functionName + numQ + '</p>'); 100 } 101 102 try { 103 let count = 0; 104 do { 105 // document.getElementById("Button4").insertAdjacentHTML('afterend','<p>1であれば問題なし = ' + closer(0) + '</p>'); 106 nextQuestion(); 107 } while (count == 10); 108 } catch (k) { 109 document.getElementById("Button4").insertAdjacentHTML('afterend','<p>error → hello.html → do while文中→ ' + k.message + '</p>'); 110 } 111 </script> 112</body> 113</html> 114

宜しくお願い致します.

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

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

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

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

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

guest

回答2

0

必要最小限のミニマムコードで実装を試みてから、本番コードに適用する事をお勧めします。

回答する側からしても、コード全体を解読して、修正を求められるのは困ります…。
コード全体を解読する時間はないので、質問文の要点のみに留めますが、

現状, handleEventメソッドに①を追加しているのですが, イベントハンドラ内の変数xがfor文終了後インクリメントされて5となり,

handleEvent を使っているのですから、listenerオブジェクトのプロパティに格納して下さい。

JavaScript

1function handleEvent (event) {console.log(this.i); } 2 3for (var i = 0; i < 5 ; i++ ) { 4 document.addEventListener('click', {i: i, handleEvent: handleEvent}, false); 5}

Re: slimat さん

投稿2019/12/16 03:41

編集2019/12/16 11:47
think49

総合スコア18189

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

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

slimat

2019/12/17 03:53 編集

ご回答ありがとうございます. ご提示頂いたコードをhtmlファイルに埋め込み, 以下のhtmlファイルを作成しました. ```html <html> <head> </head> <body> <input type="button" id="Button1" value="1"> <input type="button" id="Button2" value="2"> <input type="button" id="Button3" value="3"> <input type="button" id="Button4" value="4"> <script> function handleEvent (event) {console.log(this.i); } for (var i = 1; i < 5 ; i++ ) { document.addEventListener('click', {i: i, handleEvent: handleEvent}, false); } </script> </body> </html> ``` この結果は, どのボタンを押しても1から4までの数字が出力されました. ここで, getElementById("Button" + i)を追加すると, 押したボタンに対応する数字が出力されました. 後者でも動くので, documentなどのオブジェクトだけでなく, 一般的なオブジェクト(今回のボタンなど)もイベントを受け取る対象となりうるということでしょうか. documentオブジェクトのプロパティにhandleEventを設定するべき理由は何なのでしょうか.
guest

0

ベストアンサー

let使えば早いけど、GASで使えたっけ。使え無い場合は即時関数で。

js

1for ( var i = 0; i < 5 ; i++ ) { 2 ( function( ii ) { 3 // 4 } )( i ); 5}

【JavaScriptで即時関数を使う理由 - Qiita】
https://qiita.com/katsukii/items/cfe9fd968ba0db603b1e

【即時関数のメリットと主な用途|もっこりJavaScript|ANALOGIC(アナロジック)】
http://analogic.jp/immediate-function/

投稿2019/12/15 15:34

kei344

総合スコア69596

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

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

slimat

2019/12/15 15:58

ご回答ありがとうございます. letで無事にいけました. ご提示頂いたページを読みたいと思います.
macaron_xxx

2019/12/16 00:23

少しだけ捕捉です。 > let使えば早いけど、GASで使えたっけ。使え無い場合は即時関数で。 GASでletは使えませんが、本件はGASではなく、クライアントのブラウザ上で実行されるJavaScriptなので、letが使えるかはクライアントに依存します(=ほぼ使える)
kei344

2019/12/16 01:34

To: macaron_xxxさん 質問タグにGASがあるので・・・。
macaron_xxx

2019/12/16 02:36

GAS上でWebサイトを構成しているので、GASはGASなんですが、実行部分は~という感じです。 後学者のための捕捉というイメージです。
slimat

2019/12/17 10:58

質問があるのですが, letを使用すればいけるということは, スコープが問題だということでしょうか.
kei344

2019/12/17 12:53

はい。
slimat

2019/12/18 00:40 編集

お返事ありがとうございます. レキシカルスコープで考えると, varを用いてもforに入る時にxに1-4の数値が入るようになっているので, letではうまくいく理由がわからないのですが, どのようになっているのでしょうか.
kei344

2019/12/18 04:43

var は直近の関数スコープに縛られるため、forを抜けてもそのスコープ内であれば値が保持されます。forを抜けた後のiを、for内で定義した関数がそのまま使用することになります。i < 5ならiは5にしかなりません。 letはforのブロック内にスコープが束縛されるので、for内で定義した関数は定義したタイミングにあるiを使用することになります。
slimat

2019/12/18 05:05

for内の変数xが参照するxの値がforの外のxの値になるですか. 例えば, forループ時にxの値が3であれば checkTheAnswer(`${x}`)のxのところに3が埋め込まれそうですけれども, そうなのですか...
kei344

2019/12/18 05:07

関数は実行される時に値を参照します。
slimat

2019/12/18 05:16

関数とはhandleEvent関数のことでしょうか.
kei344

2019/12/18 05:18

JavaScriptの関数。
slimat

2019/12/18 05:25

どの回答ボタンを押してもcheckTheAnswer(5)が呼び出されてしまうのはhandleEvent関数が実行される度に関数スコープ変数xの値を参照しにいっているということでしょうか.
kei344

2019/12/18 05:35

その質問は既にした「関数は実行される時に値を参照します。」というコメントではなく何か別のことを聞いていますか?また、実際の挙動についてはブラウザのコンソールで確認できるのでしてください。
slimat

2019/12/18 05:45

関数は実行されるときに値を参照します ということについてです. handleEvent内の変数xはhandleEvent関数が実行される度に関数スコープ変数xの値を参照しにいっているということでしょうか.
kei344

2019/12/18 06:01

実際の挙動についてはブラウザのコンソールで確認できるのでしてください。
slimat

2019/12/18 06:44

実際に確認してみました. 変数はメモリへの参照・レキシカルスコープ・変数宣言時に使用するキーワードによるスコープを組み合わせて考えていたところ動作が理解できました. 長い間ご丁寧にありがとうございました.
slimat

2019/12/18 06:48

あと, 関数内の変数はその関数が実行される時に値を参照する も必須でした.
slimat

2019/12/19 05:58 編集

ガベレージコレクションいうものに回収されるタイミングも考えるべき要素でした.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問