Naoki23
@Naoki23 (早稲田 直輝)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

jQueryのクリックイベントがまとめて動作してしまう。

アプリの概要

アタック25のようなパネルゲームです。
パネルをクリックした時に問題が表示され正解するとパネルの色が変わるといった感じです。

発生している問題

問題に一度間違ったあと別のパネルを選択し正解すると間違った時に選択したパネルの色も変わってしまいます。

ソースコード

javascript

"use strict";
$(function () {
  var PANEL_ROW = 5;
  var PANEL_COLUMN = 5;

  var nowPlayer = 0;
  var colors = ["red", "blue", "green", "white"];

  var panelState = [];
  for (var row = 0; row < PANEL_ROW; row++) {
    panelState.push([]);
    for (var col = 0; col < PANEL_COLUMN; col++) {
      panelState[row].push(null);
    }
  }

  /* パネルの表示を更新 */
  function updatePanel() {
    var html = '';
    for (var row = 0; row < panelState.length; row++) {
      html += '<div class="panel-row">';
      for (var col = 0; col < panelState[row].length; col++) {
        var color = panelState[row][col] || "gray";
        var panelNum = (row * PANEL_COLUMN + col + 1);
        html += '<div class="panel ' + color + '">' + panelNum + '</div>';
      }
      html += '</div>';
      if (row < panelState.length - 1) html += '<br>';
    }
    $("#attack25-panel").html(html);
  }

  /* パネルの状態を変更 */
  function setPanel(number, color) {
    var dx = [0, 1, 1, 1, 0, -1, -1, -1];
    var dy = [-1, -1, 0, 1, 1, 1, 0, -1];

    var number = number - 1;
    var nowRow = Math.floor(number / PANEL_COLUMN);
    var nowCol = number % PANEL_COLUMN;
    panelState[nowRow][nowCol] = color;

    var changeVec = [];

    for (var i = 0; i < 8; i++) {
      var x = nowCol + dx[i];
      var y = nowRow + dy[i];
      var cnt = 0;
      while (x < PANEL_COLUMN && x >= 0 && y < PANEL_ROW && y >= 0 && panelState[y][x] != null) {
        cnt++;
        if (panelState[y][x] == color) {
          changeVec.push({ to: i, num: cnt - 1 });
          break;
        }
        x += dx[i]; y += dy[i];
      }
    }
    changeVec.forEach((vec) => {
      var x = nowCol;
      var y = nowRow;
      for (var i = 0; i < vec.num; i++) {
        x += dx[vec.to]; y += dy[vec.to];
        panelState[y][x] = color;
      }
    });

    updatePanel()
    panelCount()
    if (checkPanel()) {
      $('#endModal').modal('show');
    }
  }

  /* 現在のプレイヤーを表示 */
  function setPlayer() {
    $("#nowPlayer").html("NowPlayer:" + (nowPlayer + 1));
  }

  /* 次のプレイヤーに更新 */
  function updatePlayer() {
    nowPlayer += 1;
    if (nowPlayer >= 4) nowPlayer = 0;
    $("#nowPlayer").html("NowPlayer:" + (nowPlayer + 1));
  };

  /* 各色のパネルの数をカウントして表示を更新 */
  function panelCount() {
    var cnt = [0, 0, 0, 0];
    for (var i = 0; i < 5; i++) {
      for (var j = 0; j < 5; j++) {
        if (panelState[i][j] == null) continue;
        var colorNumber;
        for (colorNumber = 0; colorNumber < 4; colorNumber++) {
          if (panelState[i][j] == colors[colorNumber]) break;
        }
        cnt[colorNumber]++;
      }
    }
    var redCnt = 'red:' + cnt[0];
    var blueCnt = 'blue:' + cnt[1];
    var greenCnt = 'green:' + cnt[2];
    var whiteCnt = 'white:' + cnt[3];
    $('#redCnt').html(redCnt);
    $('#blueCnt').html(blueCnt);
    $('#greenCnt').html(greenCnt);
    $('#whiteCnt').html(whiteCnt);
  }

  /* パネルが埋まっているかを判定 */
  function checkPanel() {
    for (var i = 0; i < 5; i++) {
      for (var j = 0; j < 5; j++) {
        if (panelState[i][j] === null) return false;
      }
    }
    return true;
  }

  /* 問題用の関数 */
  function query(num, color) {
    var aryQuery = [];
    var queryArea = $("#queryModal");
    aryQuery.push(
      {
        question: '次のうち、海に面していない県はどれ?',
        answer: ['岐阜', '愛知', '三重', '静岡']
      }
    );
    var query_cnt = 0;

    queryArea.find('.modal-body').text(aryQuery[query_cnt]['question']);

    var success = aryQuery[query_cnt]['answer'][0];
    var aryHoge = arrShuffle(aryQuery[query_cnt]['answer'].concat());

    var html = '';

    $.each(aryHoge, function (key, value) {
      //正解の場合はdata属性を付与する
      if (success === value) {
        html += '<button type="button" class="btn" data-true="1">' + value + '</button>';
      }
      else {
        html += '<button type="button" class="btn">' + value + '</button>';
      }
    });
    queryArea.find('.modal-footer').html(html);
    $('#queryModal').modal('show');

    queryArea.on('click', '.modal-footer button', function () {
      //画面を暗くするボックスを表示(上から重ねて、結果表示中は選択肢のクリックやタップを封じる
      if ($(this).data('true')) {
        //正解の処理 〇を表示
        setPanel(num, color);
        $('#queryModal').modal('hide');
        updatePlayer();
      }
      else {
        $('#queryModal').modal('hide');
        updatePlayer();
      }
    });
  }

  /* 配列をシャッフル */
  function arrShuffle(arr) {
    for (var i = arr.length - 1; i > 0; i--) {
      var j = Math.floor(Math.random() * (i + 1));
      var tmp = arr[i];
      arr[i] = arr[j];
      arr[j] = tmp;
    }
    return arr;
  }

  setPlayer();
  panelCount();
  updatePanel();

  /* パネルをクリックしたときの処理 */
  $(document).on('click', '.panel', function () {
    var num = Number($(this).text());
    var number = num - 1;
    var nowRow = Math.floor(number / PANEL_COLUMN);
    var nowCol = number % PANEL_COLUMN;
    if (panelState[nowRow][nowCol] === null) {
      query(num, colors[nowPlayer]);
    }
  });

});

HTML

<!DOCTYPE html>
<html>

<head>
  <title><%= title %></title>

  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
    integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous" />
  <link rel="stylesheet" href="/stylesheets/style.css" />
  <link rel="stylesheet" href="/stylesheets/panel.css">

  <link rel="preconnect" href="https://fonts.gstatic.com">
  <link href="https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c&display=swap" rel="stylesheet">

  <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</head>

<body>
  <div class="header text-center">
    <h1><%= title %></h1>
  </div>

  <!--パネル-->
  <div class="main mt-3 text-center">
    <a href="/" class="btn btn-primary" role="button">TOP PAGE</a>
    <h2 class="mt-3" id="nowPlayer"></h2>
    <div class="mt-3">
      <div id="attack25-panel"></div>
    </div>
  </div>

  <!--プレイヤーのカード-->
  <div class="playerTiles card-columns mt-3">

    <div class="playerTile card text-center redCard">
      <h2>Player1</h2>
      <p id="redCnt"></p>
    </div>

    <div class="playerTile card text-center blueCard">
      <h2>Player2</h2>
      <p id="blueCnt"></p>
    </div>

    <div class="playerTile card text-center greenCard">
      <h2>Player3</h2>
      <p id="greenCnt"></p>
    </div>

    <div class="playerTile card text-center whiteCard">
      <h2>Player4</h2>
      <p id="whiteCnt"></p>
    </div>

  </div>

  <!-- 試合終了モーダル -->
  <div class="modal fade" id="endModal"" tabindex=" -1" role="dialog" aria-labelledby="endModalTitle"
    aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="endModalTitle">試合終了</h5>
        </div>
        <div class="modal-body">
          もう一度対戦しますか?
        </div>
        <div class="modal-footer">
          <a class="btn btn-primary" href="/page2" role="button">もう一度対戦</a>
          <a class="btn btn-secondary" href="/" role="button">トップページへ</a>
        </div>
      </div>
    </div>
  </div>

  <!-- 問題モーダル -->
  <div class="modal fade" id="queryModal" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="queryModalTitle"
    aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="queryModalTitle">問題</h5>
        </div>
        <div class="modal-body">
          問題文
        </div>
        <div class="modal-footer">
        </div>
      </div>
    </div>
  </div>

  <script src="/javascripts/game.js"></script>

  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
    integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
    crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"
    integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
    crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"
    integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
    crossorigin="anonymous"></script>
</body>

</html>

 

関数の処理順を変更してみたりしましたが改善しませんでした。
私では手詰まりの状態です。
読みづらいコードだと思いますがおねがいします。

0

1Answer

見た感じだと、ここでquery(num, color)の実行のたびにイベントリスナーが登録されるように思えます。

    queryArea.on('click', '.modal-footer button', function () {
      //画面を暗くするボックスを表示(上から重ねて、結果表示中は選択肢のクリックやタップを封じる

単純な解決方法としてはfind(selector)を使って

    queryArea.find('.modal-footer button').on('click', function () {
      //画面を暗くするボックスを表示(上から重ねて、結果表示中は選択肢のクリックやタップを封じる

とすれば上手く行くと思います。

1Like

Comments

  1. @Naoki23

    Questioner

    解決しました!ありがとうございます!

Your answer might help someone💌