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

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

ただいまの
回答率

91.02%

  • JavaScript

    13824questions

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

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

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 390

kamayla

score 4

前提・実現したいこと

ここに質問したいことを詳細に書いてください
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/ツール等のバージョンなど)

より詳細な情報

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • kei344

    2017/11/03 18:38

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

    キャンセル

  • karamarimo

    2017/11/04 02:00

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

    キャンセル

回答 1

checkベストアンサー

+5

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

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

 本題

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

<!DOCTYPE html>
<meta charset="utf-8">
<title>Test</title>
問題: <div id="quest"></div>
<script src="../common/jquery-3.2.1.js"></script>
<script>
var data = [
    ["<p>日本の","<em>首都</em>","は?</p>", "東京"],
    ["<p>3時10分の","<em>20分前</em>","は?</p>", "2時50分"],
    ["<p>終わりです</p>", "<p>終わりです</p>", "<p>終わりです</p>", null],
];
var count = 0;
var str = "";
// ここから---------
// 問題表示
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)+"問目");

    // テキストボックスでエンターを入力したらいイベント発生
    $("#ans").keypress(keyhandle);
}

// キーイベント処理
function keyhandle(e){
    if ( e.which == 13 ) {
        if($(this).val()==data[count][3]){
            alert("正解");
            count++;
            $("#ans").off();
            start();
        }else{
            alert("不正解");
        }
    }
}

start();
// ここまで---------
</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などを使う方法を提案してみます。

<!DOCTYPE html>
<meta charset="utf-8">
<title>Test</title>
問題: <div id="quest"></div>
<div id="ansline" style="display:none">= <input type="text" id="ans"></div>
<script src="../common/jquery-3.2.1.js"></script>
<script>
  var data = [
    ["<p>日本の","<em>首都</em>","は?</p>", "東京"],
    ["<p>3時10分の","<em>20分前</em>","は?</p>", "2時50分"],
    ["<p>終わりです", "終わりです", "終わりです</p>", null],
  ];
  var count = 0;
  var str = "";
  // 問題表示
  function start(){
    for(let i = 0; i<3; i++){
      str += data[count][i] + " ";
    }
    $("#quest").html(str);
    str="";
    $("h2").html('第'+(count + 1)+"問目");
    if (null === data[count][3]) {
      $("#ansline").hide();
    } else {
      $("#ansline").show();
    }
  }
  start();

  // テキストボックスでエンターを入力したらいイベント発生
  $("#ans").keypress(function(e){
    if ( e.which == 13 ) {
      if ($(this).val()==data[count][3]) {
        alert("正解");
        count++;
        $(this).val("");
        start();
      } else {
        alert("不正解");
      }
    }
  });
</script>

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

 補足

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

こうするのは面倒で、

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

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

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

アクセシビリティ

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/07 11:59 編集

    > (余談)質問の仕方について
    心から同意します。

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

    キャンセル

  • 2017/11/08 12: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で答えを動的に表示する仕組みに変更します。

    キャンセル

  • 2017/11/08 13:06

    > https://msdn.microsoft.com/ja-jp/library/bb250448.aspx
    ちゃんとした公式のまとめがあったのですね。知りませんでした。
    私自身、イベントハンドラは全部外しておくくらいしかしていなかったので参考になります。
    特に「クロスページリーク」はそれで防げないのが怖いですね。

    > jQuery(v1系)ではIEのメモリリークパターン対策コードが実装されており、jQueryが上手いように対応してくれるので、メモリリークパターンを考慮する必要がありませんでした。
    > v2系以降はIE8-が未対応となった為、この対策コードは取り除かれています。
    jQueryってそんな凝ったことをしてくれていたのですね。それならちゃんとjQueryを使っている限りは大丈夫そうです。

    > DOMノードを動的に挿入しても適切にマークアップされていれば、特に問題はないと思います。
    > 「見かけない=非推奨」はちょっと強引な論理展開のように思いました。
    それは本当にその通りでした。
    個人的にHTMLが文字列としてJavascriptに出てくるのが嫌なためにinnerHTMLを毛嫌いしているもので、つい無理な押し付けをしてしまいました。
    正しく使えば大量の要素の置き換えなど優秀なパフォーマンスを得られることもあるとのことで、うまく付き合っていきたいと思いました。

    > アクセシビリティの観点で述べるなら、「出しゃばらないJavaScript」の実装にするのが好ましいと私は思います。
    アクセシビリティの観点で言えばそうするのが良いでしょうね。
    答えを隠しておきたいのなら他にやりようがないと思ってしまいましたが、サーバサイドという手がありましたね。
    (クライアントサイド)Javascriptの質問はその中でなんとかすることしか考えられない自分の視野の狭さに気付かされました。

    参考になるコメントをありがとうございました。

    キャンセル

  • 2017/11/14 00:53

    質問が舌ったらずで申し訳ありませんでした。

    今後はHTML部分も記載し、明確にご質問できるように努めます。

    問題の原因に関するご説明、とても丁寧でわかりやすかったです。

    ありがとうございました。

    キャンセル

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

  • ただいまの回答率 91.02%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 受付中

    jQueryのタッチイベントがスマホ環境でのみ動作しない

    以下コードを用いて、 ボタンをクリックすることで、メニューにopenというクラスをつける処理をjQueryを用いて、行おうとしています。 chrome環境では、動作するのですが、

  • 受付中

    JavaScriptでのValueセット

    お世話になります。 つたないコードで申し訳ありませんが、 JavaScriptの処理がうまくいかず悩んでおります。 どこをどう直せば良いのか、アドバイス頂けると幸いです。 ●何

  • 解決済

    【JavaScript】プリミティブ型である文字列値とStringオブジェクトについて

    掲題の件について質問です。 プリミティブ型である文字列値とStringオブジェクトについてですが、 値がどのように格納されているのかご教示下さい。 プリミティブ型 var st

  • 解決済

    IE9以降でEnterキー押下時にTabキーと同様のフォーカス移動を行うスクリプト

    初めて質問させていただきます。 現在作成中のWebサイトでIE8以前・IE9以降で同様の動作になるよう 作成中なのですが、表題の部分で詰まってしまいました。 問題は<input>

  • 解決済

    javascript 配列

    jsで1から10までの整数をランダムで30個ぐらい出して それぞれ何個ずつ出たのかを表示したいのですが、 たとえばランダムで1,4,3,3,7と出た場合 1=1 2=0 3=2

  • 解決済

    記入したすべての値を取りたいのですが....

    お世話になっております、石井と申します。 今、inputで打ち込まれた文字列をすべてunicodeで出したいのですが、やり方がわかりません。たぶんすごく初歩的なものだと思いますが助

  • 受付中

    ボタンの非アクティブ化について

    以下のページで記載されているサンプルをもとに、送信ボタンを未入力時に非アクティブ化するコードを作成中なのですが、IE11などで現れるinput要素内の×印をクリックした際にinpu

  • 受付中

    電卓について

    電卓を作っています。 キーボードからアルファベットを入力したとき Textboxにアルファベットが入力できないようにしたいです。 もしくは、数字だけ入力出来ないようにした

同じタグがついた質問を見る

  • JavaScript

    13824questions

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