DIV не перемещаются с помощью getElementsByClassName()

Я начал создавать игру на HTML, CSS и JavaScript. Я создал функцию, которая всякий раз, когда я нажимаю кнопку, должна начать движение всех красных DIV. Однако я продолжаю получать эту ошибку в консоли:

Uncaught TypeError: Не удается прочитать свойство «стиль» неопределенного. в index.html: 48

Из-за этой ошибки DIV не перемещаются. Я использовал этот код до сих пор:

function startGame() {
  var gameObj = document.getElementsByClassName("object");
  for (var i = 0; i < gameObj.length; i++) {
    var h = setInterval(function() {
      gameObj[i].style.transition = "1s";
      gameObj[i].style.left = Math.floor(Math.random() * 90) + "%";
      gameObj[i].style.top = Math.floor(Math.random() * 90) + "%";
    }, 500);
    document.getElementById("stop").addEventListener("click", function() {
      clearInterval(h);
    });
  }
}
header {
  position: absolute;
  width: 100%;
  height: 10%;
  background-color: blue;
  top: 0;
  left: 0;
}

.object {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  position: absolute;
  background-color: red;
  transition: 1s;
  top: 40%;
  left: 30%;
}

.butn {
  cursor: pointer;
  color: white;
  background-color: black;
  border: none;
  height: 50%;
  width: 5%;
}
<div class = "object" id = "object"></div>
<div class = "object" style = "top: 60%; left: 70%;"></div>
<header>
  <button onclick = "startGame()" class = "butn">Start</button>
  <button id = "stop" class = "butn">Stop</button>
</header>

Когда я использовал getElementById вместо getElementsByClassName, это сработало. Однако я не могу использовать getElementById(), так как есть несколько DIV. Я использую последнюю версию Chrome. Кто-нибудь знает, как я могу это исправить? Заранее спасибо!

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
0
71
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Переместите setInterval() на корневой уровень startGame()

function startGame() {
  var h = setInterval(function() {
    var gameObj = document.getElementsByClassName("object");
    for (var i = 0; i < gameObj.length; i++) {
      gameObj[i].style.transition = "1s";
      gameObj[i].style.left = Math.floor(Math.random() * 90) + "%";
      gameObj[i].style.top = Math.floor(Math.random() * 90) + "%";
    }
  }, 500);

  document.getElementById("stop").addEventListener("click", function() {
    clearInterval(h);
  });
}
header {
  position: absolute;
  width: 100%;
  height: 10%;
  background-color: blue;
  top: 0;
  left: 0;
}

.object {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  position: absolute;
  background-color: red;
  transition: 1s;
  top: 40%;
  left: 30%;
}

.butn {
  cursor: pointer;
  color: white;
  background-color: black;
  border: none;
  height: 50%;
  width: 5%;
}
<div class = "object" id = "object"></div>
<div class = "object" style = "top: 60%; left: 70%;"></div>
<header>
  <button onclick = "startGame()" class = "butn">Start</button>
  <button id = "stop" class = "butn">Stop</button>
</header>

Спасибо за ваш ответ! Это решило мою проблему! Однако, не могли бы вы сказать, почему это не работало раньше?

user14697373 18.12.2020 08:21

это потому, что внутри функции setInterval переменная i не будет работать в соответствии с областью действия функции переменной.

wangdev87 18.12.2020 08:34

Хорошо. Я просто новичок в JavaScript, поэтому я не знал об этом. Спасибо!

user14697373 18.12.2020 08:38

Это происходит потому, что вы используете setInterval в цикле for.

Здесь происходят две вещи -

  1. setInterval будучи асинхронным по своей природе, принимает конечное значение i во время выполнения, которое будет 2
  2. var быть переменной с областью действия функции не сохраняет значение внутри цикла for с областью действия блока

Чтобы решить эту проблему, вы можете обернуть setInterval функцией и передать значение i в качестве аргумента. Вот так -

<html>

<head>
  <style>
    header {
      position: absolute;
      width: 100%;
      height: 10%;
      background-color: blue;
      top: 0;
      left: 0;
    }
    
    .object {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      position: absolute;
      background-color: red;
      transition: 1s;
      top: 40%;
      left: 30%;
    }
    
    .butn {
      cursor: pointer;
      color: white;
      background-color: black;
      border: none;
      height: 50%;
      width: 5%;
    }
  </style>
</head>

<body>
  <div class = "object" id = "object"></div>
  <div class = "object" style = "top: 60%; left: 70%;"></div>
  <header>
    <button onclick = "startGame()" class = "butn">Start</button>
    <button id = "stop" class = "butn">Stop</button>
  </header>
  <script>
  function moveElem(gameObj, i) {
    var h = setInterval(function() {
       gameObj[i].style.transition = "1s";
       gameObj[i].style.left = Math.floor(Math.random() * 90) + "%";
       gameObj[i].style.top = Math.floor(Math.random() * 90) + "%";
    }, 500);
    document.getElementById("stop").addEventListener("click", function() {
       clearInterval(h);
    });
  }

  function startGame() {
    var gameObj = document.getElementsByClassName("object");
    for (var i = 0; i < gameObj.length; i++) {
      moveElem(gameObj, i);
    }
  }
  </script>
</body>

</html>

Это классическая ошибка, которую вы неизбежно совершаете при изучении Javascript. Следующие статьи помогут вам лучше понять -

  1. Закрытие - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
  2. Асинхронный — https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Асинхронный

Да, в этом суть. Но, похоже, i будет 2 вместо 3. Поскольку i++ позволит i стать 0, 1, 2, EOL (конец цикла), но setInterval является асинхронным, поэтому весь код станет gameObj[2], который не определен.

Remi Guan 18.12.2020 08:33

@CasimirCrystal Плохо, что ты прав, я думал, что видел три div, я отредактирую свой ответ

Rudraprasad Das 18.12.2020 08:35

Таким образом, Closure также является решением. Просто измените код OP на for (let i = 0)..., чтобы решить проблему. Что пройдет gameObj[0] и gameObj[1] в setInterval, в этом случае проблем не будет. @Шьямак Сет

Remi Guan 18.12.2020 08:36

Несмотря на то, что в настоящее время браузеры поддерживают ES2015 и выше, let все еще может вызывать проблемы совместимости для старых браузеров.

Rudraprasad Das 18.12.2020 08:38

Другие вопросы по теме