首页 > 解决方案 > addEventListener('click', function(), {once: true}) 在同一位置多次触发

问题描述

let doorLeft = document.getElementById("left");
let doorMiddle = document.getElementById("middle");
let doorRight = document.getElementById("right");
let resetButton = document.getElementById("reset");

let numberOfClicks = 0;

function incrementClicks() {
  numberOfClicks++;
  console.log('Number of clicks: ' + numberOfClicks)
}
/* -------------------------------------------------------------------------- */
/*                         handle click door only once                        */
/* -------------------------------------------------------------------------- */
const revealDoorColour = () => {
  doorLeft.addEventListener(
    "click",
    () => {
      incrementClicks();
    }, {
      once: true,
    }
  );
  doorMiddle.addEventListener(
    "click",
    () => {
      incrementClicks();
    }, {
      once: true,
    }
  );
  doorRight.addEventListener(
    "click",
    () => {
      incrementClicks();
    }, {
      once: true,
    }
  );
};

/* -------------------------------------------------------------------------- */

const reset = () => {
  doorLeft.style.backgroundColor = "paleturquoise";
  doorRight.style.backgroundColor = "paleturquoise";
  doorMiddle.style.backgroundColor = "paleturquoise";
  revealDoorColour();
  resetButton.innerHTML = "Let's play";
};

resetButton.addEventListener("click", function() {
  reset();
  numberOfClicks = 0;
});
.container.main {
  display: flex;
  justify-content: space-around;
  padding: 0 20%;
}

.door {
  height: 500px;
  width: 300px;
  margin: 10px;
}

#left {
  background-color: paleturquoise;
}

#middle {
  background-color: paleturquoise;
}

#right {
  background-color: paleturquoise;
}

.score {
  text-align: center;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
  <title>Door guesser</title>
  <script src="./js/main.js" async></script>
  <link rel="stylesheet" href="./css/style.css">
</head>

<body>

  <div class="container main">
    <div class="door" id="left">

    </div>

    <div class="door" id="middle">

    </div>

    <div class="door" id="right">

    </div>

  </div>

  <div class="container score">
    <div class="currentScoreGroup">
      <div>Current score: </div>
      <div id="currentScore">0</div>
    </div>

    <div class="bestScoreGroup">
      <div>Best score: </div>
      <div id="bestScore">0</div>
    </div>

    <button class="reset" id='reset'>Let's play </button>
  </div>

</body>

</html>

addEventListener 的 Extract 函数之后,我添加了 {once:true} 参数,该参数在大多数情况下似乎都可以正常工作。但是,当单击有问题的 div 时,它会随机触发多次(在相同的 x/y 坐标处)。这怎么可能?我在匿名函数中设置了一个断点,它多次运行它。有任何想法吗?

复制问题

  1. 点击让我们玩
  2. 点击任意卡片
  3. 点击让我们再玩一次
  4. 点击另一张卡片
  5. 现在您应该在控制台中看到 2 个条目(点击)

右门

标签: javascriptdom-eventsaddeventlistener

解决方案


onceaddEventListener的选项是这样工作的:

once
    A Boolean indicating that the listener should be invoked at most once after being added.
    If true, the listener would be automatically removed when invoked.

所以如果一个 div 没有被点击,它的处理程序仍然留在那里。当您再次单击播放时,您基本上会向 div 添加更多事件侦听器。为什么它随机被多次触发(在相同的 x/y 坐标)

要解决这个问题,只需在重置中删除事件侦听器

let doorLeft = document.getElementById("left");
let doorMiddle = document.getElementById("middle");
let doorRight = document.getElementById("right");
let resetButton = document.getElementById("reset");

let numberOfClicks = 0;

function incrementClicks() {
  numberOfClicks++;
  console.log('Number of clicks: ' + numberOfClicks)
}
/* -------------------------------------------------------------------------- */
/*                         handle click door only once                        */
/* -------------------------------------------------------------------------- */
const revealDoorColour = () => {
  doorLeft.removeEventListener("click", incrementClicks);
  doorMiddle.removeEventListener("click", incrementClicks);
  doorRight.removeEventListener("click", incrementClicks);
  //
  doorLeft.addEventListener("click", incrementClicks, { once: true });
  doorMiddle.addEventListener("click", incrementClicks, { once: true });
  doorRight.addEventListener("click", incrementClicks, { once: true });
};

/* -------------------------------------------------------------------------- */

const reset = () => {
  doorLeft.style.backgroundColor = "paleturquoise";
  doorRight.style.backgroundColor = "paleturquoise";
  doorMiddle.style.backgroundColor = "paleturquoise";
  revealDoorColour();
  resetButton.innerHTML = "Let's play";
};

resetButton.addEventListener("click", function() {
  reset();
  numberOfClicks = 0;
});
.container.main {
  display: flex;
  justify-content: space-around;
  padding: 0 20%;
}

.door {
  height: 500px;
  width: 300px;
  margin: 10px;
}

#left {
  background-color: paleturquoise;
}

#middle {
  background-color: paleturquoise;
}

#right {
  background-color: paleturquoise;
}

.score {
  text-align: center;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
  <title>Door guesser</title>
  <script src="./js/main.js" async></script>
  <link rel="stylesheet" href="./css/style.css">
</head>

<body>

  <div class="container main">
    <div class="door" id="left">

    </div>

    <div class="door" id="middle">

    </div>

    <div class="door" id="right">

    </div>

  </div>

  <div class="container score">
    <div class="currentScoreGroup">
      <div>Current score: </div>
      <div id="currentScore">0</div>
    </div>

    <div class="bestScoreGroup">
      <div>Best score: </div>
      <div id="bestScore">0</div>
    </div>

    <button class="reset" id='reset'>Let's play </button>
  </div>

</body>

</html>


推荐阅读