使用Vanilla JS将事件和功能附加到动态元素上

时间:2019-04-05 09:52:14

标签: javascript html css

我创建了具有12种难度的记忆卡游戏 ,我想通过创建新的动态元素来添加更多卡牌,并在遇到困难时将其添加到游戏容器中。

使用此代码,我希望新创建的元素具有flipCard()函数,当我单击它时它将翻转卡片。但是,这不起作用,每当我单击卡时,它给我一个错误

'TypeError: this.classlist is undefined'(flipCard函数内部错误)

game.addEventListener('click', function (e) {
     if (e.target) {
         flipCard();
    }
});

下面的代码将起作用,但不适用于动态元素。

 cards.forEach(card => {
     cardCount.push(card);
     card.addEventListener('click', flipCard);
 });

window.onload = function() {
  const game = document.querySelector('.game');
  const cards = document.querySelectorAll('.card');
  const scoreboard = document.querySelector('.score');
  const move = document.querySelector('.move');
  const difficulties = document.getElementById("difficulties");

  let isCardFlipped = false,
    lockBoard = false,
    firstCard,
    secondCard,
    score = 0,
    moves = 0,
    unflipTimeout = 1500,
    cardCount = [];



  difficulties.addEventListener("change", function() {
    resetGame();
  });



  function addCard() {
    for (i = 0; i < 4; i++) {
      game.innerHTML += "<div class='card'>" + "<img src='assets/image/default/bellsprout.svg' class='front-face'>" +
        "<img src='assets/image/default/pokeball.svg' class='back-face'>" + "</div>";
    }
  };

  // ATTACH CLICK EVENT AND flipCard() FUNCTION FOR EVERY CARD.
  // THIS DOESN'T WORK FOR DYNAMICALLY CREATED ELEMENT

  // cards.forEach(card => {
  //     cardCount.push(card);
  //     card.addEventListener('click', flipCard);
  // });

  // ATTACH CLICK EVENT AND flipCard() FUNCTION FOR EVERY CARD.
  // THIS WILL WORK FOR DYNAMICALLY CREATED ELEMENT,
  // BUT "this." INSIDE flipCard() function is not defined/initialize


  game.addEventListener('click', function(e) {

    if (e.target) {
      flipCard();
    }

  });



  // Difficulty Option
  (difficulty = () => {
    let selected = difficulties.options[difficulties.selectedIndex].value;

    if (selected === 'easy') {
      unflipTimeout = 1500;
    } else if (selected === 'hard') {
      unflipTimeout = 500;
   // ADD MORE CARD ONLY IF HARD DIFFICULTY IS SELECTED
      addCard();
    }
  })();

  // flip card when card is clicked 
  function flipCard() {
    if (lockBoard || this === firstCard) return;
    this.classList.add('flip');

    if (!isCardFlipped) {
      isCardFlipped = true;
      firstCard = this;

      return;
    }

    isCardFlipped = false;
    secondCard = this;

    checkForMatch();
  }

  // check if 2 selected card are match.
  checkForMatch = () => {
    // if matched disabled card function, unflip card if not matched.
    let isMatch = firstCard.dataset.pokemon ===
      secondCard.dataset.pokemon;

    isMatch ? disableCard() : unflipCard(unflipTimeout);
  }

  // disable flip card function
  disableCard = () => {
    moves++;
    score++;
    updateScore();

    firstCard.removeEventListener('click', flipCard);
    secondCard.removeEventListener('click', flipCard);
  }

  unflipCard = (timeout) => {
    lockBoard = true;

    moves++;
    updateScore();
    setTimeout(() => {

      firstCard.classList.remove('flip');
      secondCard.classList.remove('flip');

      resetBoard();
    }, timeout);
  }

  resetBoard = () => {
    [isCardFlipped, lockBoard] = [false, false];
    [firstCard, secondCard] = [null, null];
  }

  resetGame = () => {
    if (moves > 0) {
      var confirm = window.confirm("Changes will be applied after reset \n \n \t \t Reset The Game?");
      if (confirm === true) {
        score = 0;
        moves = 0;
        updateScore();

        for (var i = 0; i < cardCount.length; i++) {
          cardCount[i].classList.remove('flip');
        }

        alert('Game Restarted! \n \n \t Card Reshuffle.');
        shuffle();
      } else return;
    }
  }

  var shuffle;
  (shuffle = () => {
    cards.forEach(card => {
      let randomPos = Math.round(Math.random() * cardCount.length);
      card.style.order = randomPos;
    });
  })();

  updateScore = () => {
    scoreboard.innerText = score;
    move.innerText = moves;

    if (score === cardCount.length / 2) {
      alert("You Win!");
    }
  }
};
.scoreboard {
  display: flex;
  flex-flow: row nowrap;
  justify-content: space-around;
  align-items: center;
  padding: 2px;
  border-radius: 5px;
  background: #363636;
  color: #0a8ec2;
}

.scoreboard h1 {
  font-size: 1.5rem;
}

.game {
  width: 580px;
  height: 580px;
  margin: auto;
  display: flex;
  flex-wrap: wrap;
  perspective: 1000px;
  position: relative;
}

.game .card {
  position: relative;
  width: calc(25% - 10px);
  height: calc(33.333% - 10px);
  margin: 5px;
  transform-style: preserve-3d;
  transition: transform 500ms linear;
}

.game .card.active {
  transform: scale(0.97);
}

.game .card.flip {
  transform: rotateY(180deg);
}

.game .card .front-face,
.game .card .back-face {
  width: 100%;
  height: 100%;
  position: absolute;
  border-radius: 5px;
  padding: 20px;
  background: #363636;
  backface-visibility: hidden;
}

.game .card .front-face {
  transform: rotateY(180deg);
  backface-visibility: visible;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="assets/css/styles.css">
  <script src="assets/js/jquery.min.js"></script>
  <script src="assets/js/main.js"></script>
  <script src="assets/js/app.js"></script>
  <title>Memory Card Game</title>
</head>

<body>



  <section class="scoreboard">
    <h1>Score: <span class="score">0</span> <br> Moves: <span class="move">0</span></h1>
    <select id="difficulties" name="difficulties">
      <option value="default" disabled>Select Difficulty</option>
      <option value="easy" selected>Easy</option>
      <option value="hard">Hard</option>
    </select>
  </section>


  <main class="game">
    <div class="card" data-pokemon="rattata">
      <img src="assets/image/default/rattata.svg" alt="Rattata" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
    <div class="card" data-pokemon="rattata">
      <img src="assets/image/default/rattata.svg" alt="Rattata" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
    <div class="card" data-pokemon="meowth">
      <img src="assets/image/default/meowth.svg" alt="Meowth" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
    <div class="card" data-pokemon="meowth">
      <img src="assets/image/default/meowth.svg" alt="Meowth" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
    <div class="card" data-pokemon="mew">
      <img src="assets/image/default/mew.svg" alt="Mew" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
    <div class="card" data-pokemon="mew">
      <img src="assets/image/default/mew.svg" alt="Mew" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
    <div class="card" data-pokemon="mankey">
      <img src="assets/image/default/mankey.svg" alt="Mankey" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
    <div class="card" data-pokemon="mankey">
      <img src="assets/image/default/mankey.svg" alt="Mankey" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
    <div class="card" data-pokemon="venonat">
      <img src="assets/image/default/venonat.svg" alt="Venonat" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
    <div class="card" data-pokemon="venonat">
      <img src="assets/image/default/venonat.svg" alt="Venonat" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
    <div class="card" data-pokemon="psyduck">
      <img src="assets/image/default/psyduck.svg" alt="Psyduck" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
    <div class="card" data-pokemon="psyduck">
      <img src="assets/image/default/psyduck.svg" alt="Psyduck" class="front-face">
      <img src="assets/image/default/pokeball.svg" alt="Pokemon" class="back-face">
    </div>
  </main>

</body>

</html>

1 个答案:

答案 0 :(得分:0)

代替此:

if (e.target) {
   flipCard();
 }

执行此操作:

if (e.target) {
   flipCard.bind(this)();
 }

将此添加到点击监听器中。 this函数中的flipCard指向window