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

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

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

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

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

Q&A

解決済

2回答

3652閲覧

insertAdjacentHTMLで追加したbutton要素にclickイベントで正誤判定を出来るようにしたい

tegeken

総合スコア13

JavaScript

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

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

0グッド

0クリップ

投稿2021/05/17 02:31

前提・実現したいこと

javascriptでクイズアプリを作っています。
実装したいのは配列に設定した問題のanswersの数に応じて選択肢が自動で増減し、正解、不正解の判定が出来るようにしたいです。

発生している問題・エラーメッセージ

選択肢の数を4に固定し、htmlファイル内にbuttonも4に固定していたときは、正誤判定とアラートが出ていたのにanswersの数を取得してhtmlにbottonをループで表示させるとアラートが出なくなりました。

該当のソースコード

javascript

1const quiz = [ 2 { 3 question: 'ゲーム市場、最も売れたゲーム機は次の内どれ?', 4 answers: [ 5 'スーパーファミコン', 6 'プレイステーション2', 7 'ニンテンドースイッチ', 8 'ニンテンドーDS', 9 'ゲームボーイ' 10 ], 11 correct: 'ニンテンドーDS' 12 }, { 13 question: '糸井重里が企画に関わった、任天堂の看板ゲームと言えば?', 14 answers: [ 15 'MOTHER2', 16 'スーパーマリオブラザーズ3', 17 'スーパードンキーコング', 18 '星のカービィ' 19 ], 20 correct: 'MOTHER2' 21 }, { 22 question: 'ファイナルファンタジーⅣの主人公の名前は?', 23 answers: [ 24 'フリオニール', 25 'クラウド', 26 'セシル' 27 ], 28 correct: 'セシル' 29 } 30]; 31 32 33const quizLength = quiz.length; 34let quizIndex = 0; 35let score = 0; 36 37// 定数の文字列をHTMLに反映させる 38const $button = document.getElementsByTagName('button'); 39const buttonLength = $button.length; //ボタンの数 40const parentNode = document.getElementById('js-items'); 41 42// クイズの問題文、選択肢を定義 43const setupQuiz = () => { 44 document.getElementById('js-question').textContent = quiz[quizIndex].question; 45 let buttonIndex = 0; 46 while(buttonIndex < quiz[quizIndex].answers.length){ 47 48 parentNode.insertAdjacentHTML( 49 'beforeEnd', 50 '<div class="m-2"><button type="button" id="js-btn-1" class="btn btn-primary">Primary</button></div>' 51 ); 52 53 $button[buttonIndex].textContent = quiz[quizIndex].answers[buttonIndex]; 54 buttonIndex++; 55 } 56} 57 58setupQuiz(); 59 60const clickHandler = (e) => { 61 if(quiz[quizIndex].correct === e.target.textContent){ 62 window.alert('正解!'); 63 score++; 64 } else { 65 window.alert('不正解!'); 66 } 67 68 quizIndex++; 69 70 if(quizIndex < quizLength) { 71 // 問題数がまだあればこちらを実行 72 setupQuiz(); 73 } else { 74 // 問題数がもうなければこちらを実行 75 window.alert('終了!あなたの正解数は' + score + '/' + quizLength + 'です!'); 76 } 77 78}; 79 80// ボタンをクリックしたら正誤判定 81 82let handlerIndex = 0; 83while (handlerIndex < buttonLength) { 84 $button[handlerIndex].addEventListener('click', (e) => { 85 clickHandler(e); 86 }); 87 handlerIndex++; 88} 89

html

1<!doctype html> 2<html class="no-js" lang=""> 3 4<head> 5 <meta charset="utf-8"> 6 <title></title> 7 <meta name="description" content=""> 8 <meta name="viewport" content="width=device-width, initial-scale=1"> 9 10 <link rel="manifest" href="site.webmanifest"> 11 <link rel="apple-touch-icon" href="icon.png"> 12 <!-- Place favicon.ico in the root directory --> 13 14 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> 15 16 <meta name="theme-color" content="#fafafa"> 17</head> 18 19<body> 20 21 <div class="container"> 22 23 <div class="jumbotron mt-5"> 24 <div class="d-flex justify-content-center"> 25 <div id="js-question" class="alert alert-primary" role="alert"> 26 A simple primary alert—check it out! 27 </div> 28 </div> 29 30 <div id="js-items" class="d-flex justify-content-center"> 31 <!-- <div class="m-2"> 32 <button type="button" class="btn btn-primary">Primary</button> 33 </div> --> 34 35 36 </div> 37 </div> 38 </div> 39 40 <script src="app.js"></script> 41</body> 42 43</html>

試したこと

htmlにbuttonを書いておくとアラートが出るので、その差を調べましたが原因がわかりませんでした。
その上ループが周り余計なbuttonが出てきて2問目以降になると、answersの数分余計に表示されるようになりました。

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

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

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

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

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

guest

回答2

0

動的なDOM要素にイベントを追加するには
上位のDOM要素にaddEventListenerしておくとよいです

javascript

1<script> 2document.addEventListener('click',e=>{ 3 if(e.target.closest('.fuga')){ 4 console.log('fuga'); 5 } 6}); 7window.addEventListener('DOMContentLoaded', ()=>{ 8 document.querySelector('#hoge').insertAdjacentHTML('beforeEnd','<div class="fuga">fuga1</div><div class="fuga">fuga2</div>'); 9}); 10</script> 11<div id="hoge">hoge</div> 12

投稿2021/05/17 02:55

編集2021/05/17 02:56
yambejp

総合スコア116724

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

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

0

ベストアンサー

開発者ツールのコンソールにエラーが出ていますよね? エラーは重要なので、必ずチェックしましょう。

button要素がconst $button = document.getElementsByTagName('button'); の実行よりも後に生成されるため、変数 $button が空になり、addEventListener()が実行されていません。

動的に生成される要素にイベントリスナを付けるのは面倒なので、親ノードにイベントリスナを登録するのが定石です。

  • clickHandler は document から呼べばよいです。

js

1document.addEventListener('click', e => { 2 if (e.target.closest('button.btn.btn-primary')) 3 clickHandler(e); 4});
  • setupQuiz() 内の $button[buttonIndex].textContent の更新も変更する必要があります。直前に insertAdjacentHTML() で足したボタンをなんとか変数に入れてそれに対して .textContent を更新する必要があります。

2問目以降になると、answersの数分余計に

setupQuiz()while ループの前に parentNode の子供ノードを消しておく必要があります。

投稿2021/05/17 02:51

int32_t

総合スコア21695

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

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

tegeken

2021/05/18 02:43

ご回答ありがとうございました。 次回からコンソールエラーを確認するようにします。 またご指摘も詳しくありがとうございます。 最後の2周目以降のループのparentNodeの子供ノードを消すところがよくわかりませんでした。 parentNode.removeChild; 消し方はこれで良いのでしょうか? またループの前にとありますが、setupQuizのwhile文の外においても、同じようにボタンが表示されてしまいました。 ご指摘いただけると嬉しいです。
int32_t

2021/05/18 03:07

> parentNode.removeChild; > 消し方はこれで良いのでしょうか? 「removeChild;」だけでは何も消えません。1つの子供を消す関数なのに1つも子供を指定していないですし、関数なのに () がないからです。 すべての子供を消す方法はいろいろありますが、一番簡単なのは「parentNode.textContent = '';」です。
tegeken

2021/05/19 01:04

ご回答ありがとうございます。 「parentNode.textContent = '';」を調べると、一つずつ消す方法もありましたので以下の方法で試してみました。 「while(parentNode.textContent) { parentNode.removeChild(parentNode.firstChild) }」 こちらでもうまくいきました。 お陰でイベントリスナーとループごとの子ノードを消す方法を理解することができました。 ありがとうございました。
int32_t

2021/05/19 01:08

そのwhileループだと、子供が要素だけでテキストがない場合に子供が消えないのであまりよろしくありません。 while (parentNode.firstChild) { parentNode.firstChild.remove(); } か、ループなしで parentNode.textContent = ''; です。
tegeken

2021/05/20 00:58

ご回答ありがとうございます。 細部まで勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問