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

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

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

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

Q&A

解決済

1回答

3668閲覧

keypressボタンが1回目は機能するのに、2回目に機能しなくなる。

kamayla

総合スコア12

JavaScript

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

0グッド

1クリップ

投稿2017/11/03 09:20

###前提・実現したいこと
ここに質問したいことを詳細に書いてください
javascriptで以下の問題が発生しました。

###発生している問題・エラーメッセージ
start関数で問題及びテキストボックスを表示されており、そのテキストボックに値を入力しエンターを押すとイベントが発生し、正解してる場合はカウンターがインクリメントされ、次の問題及びテキストボックスが表示されるようにしているのですが、一問目はkeypressイベントが発生するのですが、二問目から全くイベントが発生しなくなってしまいました。

ご教示よろしくお願いいたします。

エラーメッセージ エラーは見当たりません。 ###該当のソースコード // 問題表示 function start(){ for(let i = 0; i<3; i++){ str += data[count][i] + " "; } str += '= <input type="text" id="ans">' $("#quest").html(str); str=""; $("h2").html('第'+(count + 1)+"問目"); } start(); // テキストボックスでエンターを入力したらいイベント発生 $("#ans").keypress(function(e){ if ( e.which == 13 ) { if($(this).val()==data[count][3]){ alert("正解"); count++; start(); }else{ alert("不正解"); } } }); ###試したこと 課題に対してアプローチしたことを記載してください ###補足情報(言語/FW/ツール等のバージョンなど) より詳細な情報

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

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

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

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

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

kei344

2017/11/03 09:38

質問文のコードはそれぞれコードブロックで囲んでいただけませんか? ```(バッククオート3つ)で囲み、前後に改行をいれるか、コードを選択して「<code>」ボタンを押すとコードブロックになります。また、HTMLも提示してください。
karamarimo

2017/11/03 17:00

count, data, str の変数宣言が見当たりませんのでそれも追記してください。
guest

回答1

0

ベストアンサー

(余談)質問の仕方について

すでに追記依頼されている通り、このような質問をする際はHTMLと変数定義部分をつけてください。
もしかするとHTMLにあなたのものすごいアイデアが入っていたり、変数定義部分に多大な苦労をしてつくった問題文が含まれているのかもしれません。
しかし、答えが欲しいのであれば気軽に問題を見て答えられるようにすることはとても有用で、このサイトでは推奨されています。
今後は現在の問題が再現できるコードを新たに作って質問するくらいのことはしましょう。
面倒でしょうが、回答者もそんな苦労はしたくない人が多いのです。

本題

とりあえず、HTMLと変数定義部分を補い、さらに2問目以降もうまく動くようにしたコードを示します。

html

1<!DOCTYPE html> 2<meta charset="utf-8"> 3<title>Test</title> 4問題: <div id="quest"></div> 5<script src="../common/jquery-3.2.1.js"></script> 6<script> 7var data = [ 8 ["<p>日本の","<em>首都</em>","は?</p>", "東京"], 9 ["<p>3時10分の","<em>20分前</em>","は?</p>", "2時50分"], 10 ["<p>終わりです</p>", "<p>終わりです</p>", "<p>終わりです</p>", null], 11]; 12var count = 0; 13var str = ""; 14// ここから--------- 15// 問題表示 16function start(){ 17 for(let i = 0; i<3; i++){ 18 str += data[count][i] + " "; 19 } 20 str += '= <input type="text" id="ans">' 21 $("#quest").html(str); 22 str=""; 23 $("h2").html('第'+(count + 1)+"問目"); 24 25 // テキストボックスでエンターを入力したらいイベント発生 26 $("#ans").keypress(keyhandle); 27} 28 29// キーイベント処理 30function keyhandle(e){ 31 if ( e.which == 13 ) { 32 if($(this).val()==data[count][3]){ 33 alert("正解"); 34 count++; 35 $("#ans").off(); 36 start(); 37 }else{ 38 alert("不正解"); 39 } 40 } 41} 42 43start(); 44// ここまで--------- 45</script>

説明

元のコードの問題は、キー処理を設定する対象のinput要素がstart関数内のjQuery.fn.htmlによって問題ごとに作成されているにも関わらず、キーイベントのハンドラ設定が一度しか行われていないことにあります。

$("#ans").keypress(function(e){/* 処理 */});

と書くと、ansというidの要素でキーが押された時に/* 処理 */が実行されるようになると考えているのだと思いますが、そうではありません。
「ansというidの要素」ではなく、「このコードが書かれている場所でidがansである要素」にイベントハンドラは設定されます。
質問のコードでこれが書かれている場所は、はじめの問題を表示するためにstart関数が実行された直後です。
ここでansというidである要素、というのに該当するのは1問目の解答欄のみで、2問目以降の解答欄は「キーイベント処理の後で実行されたstart関数のあと」という別の場所のものです。
だから、2問目以降でキーイベントのハンドリングができないのです。

jQueryはCSSになれた人が使いやすいように宣言的的な書き方ができるという性質がありますが、それゆえに宣言的に書けると思ってしまうことがしばしばあります。これはよくある間違いなのでよく考えて理解し、乗り越えていってください。

おまけ

こうするのは面倒ですね。
また、解答欄を問題のついでに書き換わる部分に置くようなコードはあまり見かけません。
そこで、解答欄を出したり消したりするのにはjQueryのhide showなどを使う方法を提案してみます。

html

1<!DOCTYPE html> 2<meta charset="utf-8"> 3<title>Test</title> 4問題: <div id="quest"></div> 5<div id="ansline" style="display:none">= <input type="text" id="ans"></div> 6<script src="../common/jquery-3.2.1.js"></script> 7<script> 8 var data = [ 9 ["<p>日本の","<em>首都</em>","は?</p>", "東京"], 10 ["<p>3時10分の","<em>20分前</em>","は?</p>", "2時50分"], 11 ["<p>終わりです", "終わりです", "終わりです</p>", null], 12 ]; 13 var count = 0; 14 var str = ""; 15 // 問題表示 16 function start(){ 17 for(let i = 0; i<3; i++){ 18 str += data[count][i] + " "; 19 } 20 $("#quest").html(str); 21 str=""; 22 $("h2").html('第'+(count + 1)+"問目"); 23 if (null === data[count][3]) { 24 $("#ansline").hide(); 25 } else { 26 $("#ansline").show(); 27 } 28 } 29 start(); 30 31 // テキストボックスでエンターを入力したらいイベント発生 32 $("#ans").keypress(function(e){ 33 if ( e.which == 13 ) { 34 if ($(this).val()==data[count][3]) { 35 alert("正解"); 36 count++; 37 $(this).val(""); 38 start(); 39 } else { 40 alert("不正解"); 41 } 42 } 43 }); 44</script> 45

このコードはアクセシビリティ(あらゆる人に使えるようになっているかという点。ここではスクリーンリーダーや点字ディスプレイ利用者、キーボード以外の入力デバイスの利用者など)の観点から言うと良くありません。
think49さまの2017/11/08 12:00のコメントにある通り、
問題をHTMLに書いておくか、問題を隠したいならサーバサイドスクリプトなどページ遷移を伴う方法を代替として提供するかするのが良いです。

補足

think49さまの指摘もあり、確かに説明不足だと思ったので補足します。

こうするのは面倒で、

イベントハンドラの設定を毎回作り直した要素について行い直すのは、記述の位置がその要素を作った場所になることによって、コールバック地獄を悪化させる恐れがあります。これが「面倒」なことです。

また、解答欄を問題のついでに書き換わる部分に置くようなコードはあまり見かけません。

フォームを隠しておきたいときにinnerHTMLやjQuery.fn.htmlでフォームを出すのはそう見かけないと思います。
アニメーションを入れるため、とか、tabindexを適切に設定してアクセシビリティを向上させたいから、とか、理由はいろいろあるのでしょう。
あまり見かけないコードは読む人(未来の自分を含む)を混乱させるので、よくある書き方をした方が良いのではないでしょうか。

アクセシビリティ

hide/showはdisplay属性をいじります。これで要素を出したり隠したりすると、スクリーンリーダーがうまく対応しないということが考えられると思います。最近ではaria-hidden属性を使ってスクリーンリーダー向けにも隠された要素と明示することが広まりつつあると思います。
とはいえ、キーイベントを使った回答受付やjQuery.fn.htmlを使った問題文の更新という作りが、そういった方々への対応を考えたものとなっていません。

(いくらか編集しています。(あとから読む方へ)編集内容は投稿日時の下、編集日時のところにリンクがあるはずです。)

投稿2017/11/06 16:18

編集2017/11/08 04:22
miimou

総合スコア74

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

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

think49

2017/11/07 02:59 編集

> (余談)質問の仕方について 心から同意します。 > こうするのは面倒で、(今ではあまり無いはずですがかつては)メモリリークのリスクなどがあったので、 > 解答欄を問題のついでに書き換わる部分に置くことは一般的ではありません。 > 解答欄を出したり消したりしたいならjQueryのhide showなどを使う方がマシでしょう(アクセシビリティの観点では(略))。 この部分が理解できなかったので、差し支えなければ補足して頂けないでしょうか。
think49

2017/11/08 03:00

To: miimou さん > さらに、この際に古い要素のイベントハンドラを外し忘れて、DOMオブジェクトとJavascriptオブジェクトを共に含む循環参照ができてしまうと、メモリリークを起こすという現象が昔のIEではあったと聞いています。 私が読む限りでは、DOM/JavaScriptの循環参照パターンには合致していないように思います。 関数 keyhandle からDOMノードを参照可能になるとメモリリークパターンとなりますが、参照可能な変数は ECMAScript 管轄のオブジェクトしかありません。 動的にノード挿入する場合には https://msdn.microsoft.com/ja-jp/library/bb250448.aspx の「図 3. DOM 挿入順序リーク モデル」に気を付ける必要がありますが、該当コードではDOMノード挿入後にイベント定義しているので、この問題もありません。 > jQueryはキャッシュなどで参照が絡んでいるので、このリスクがあると思いました。 jQuery(v1系)ではIEのメモリリークパターン対策コードが実装されており、jQueryが上手いように対応してくれるので、メモリリークパターンを考慮する必要がありませんでした。 v2系以降はIE8-が未対応となった為、この対策コードは取り除かれています。 > フォームを隠しておきたいときにinnerHTMLやjQuery.fn.htmlでフォームを出すのはそう見かけないと思います。 DOMノードを動的に挿入しても適切にマークアップされていれば、特に問題はないと思います。 「見かけない=非推奨」はちょっと強引な論理展開のように思いました。 アクセシビリティの観点で述べるなら、「出しゃばらないJavaScript」の実装にするのが好ましいと私は思います。 コンテンツ(質問文/答え)はHTML上にあり、JavaScript-Off環境でも適切に表示されるのがベターです。 - 答えの表示をサーバサイドスクリプト/CSSで代替する - 答えを初めから全て表示しておく な実装にしてから、JavaScriptで答えを動的に表示する仕組みに変更します。
miimou

2017/11/08 04:06

> https://msdn.microsoft.com/ja-jp/library/bb250448.aspx ちゃんとした公式のまとめがあったのですね。知りませんでした。 私自身、イベントハンドラは全部外しておくくらいしかしていなかったので参考になります。 特に「クロスページリーク」はそれで防げないのが怖いですね。 > jQuery(v1系)ではIEのメモリリークパターン対策コードが実装されており、jQueryが上手いように対応してくれるので、メモリリークパターンを考慮する必要がありませんでした。 > v2系以降はIE8-が未対応となった為、この対策コードは取り除かれています。 jQueryってそんな凝ったことをしてくれていたのですね。それならちゃんとjQueryを使っている限りは大丈夫そうです。 > DOMノードを動的に挿入しても適切にマークアップされていれば、特に問題はないと思います。 > 「見かけない=非推奨」はちょっと強引な論理展開のように思いました。 それは本当にその通りでした。 個人的にHTMLが文字列としてJavascriptに出てくるのが嫌なためにinnerHTMLを毛嫌いしているもので、つい無理な押し付けをしてしまいました。 正しく使えば大量の要素の置き換えなど優秀なパフォーマンスを得られることもあるとのことで、うまく付き合っていきたいと思いました。 > アクセシビリティの観点で述べるなら、「出しゃばらないJavaScript」の実装にするのが好ましいと私は思います。 アクセシビリティの観点で言えばそうするのが良いでしょうね。 答えを隠しておきたいのなら他にやりようがないと思ってしまいましたが、サーバサイドという手がありましたね。 (クライアントサイド)Javascriptの質問はその中でなんとかすることしか考えられない自分の視野の狭さに気付かされました。 参考になるコメントをありがとうございました。
kamayla

2017/11/13 15:53

質問が舌ったらずで申し訳ありませんでした。 今後はHTML部分も記載し、明確にご質問できるように努めます。 問題の原因に関するご説明、とても丁寧でわかりやすかったです。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問