Очередной Конфликт Функций (Счетчик внутри счетчика)
У меня есть функция которая считает удары по монстру
Let hp = 10;
function hit(){
if (hp >=0) {
hp = hp - 1 ;
document.getElementById("result").innerHTML = hp;
}checkWin();}
Мне в определенный момент нужно сменить изображение в div. Я это делаю так
function checkWin() {
if (hp <= 0) {
document.getElementById('hp').textContent = 'You won!';
hp = 500;
counter = counter + 1 ;
document.getElementById("counter").innerHTML = counter;
}
if (counter===4 ) {
r1(); // Меняем картинку
}
}
Функция r1 реализована так
function r1() {
const div1 = document.querySelector('.monster1');
div1.className = 'monster2';
}
Но вот беда, когда функция checkWin() доходит до значения (counter===4 ) она действительно меняет значения монстра, а при следующем ударе (выполняя hit()) Консоль выдает Красноту Такого содержания
Ударь по врагу еще раз0.45b — копия.html:293 Uncaught TypeError: Cannot set property 'className' of null
at r1 (Ударь по врагу еще раз0.45b — копия.html:293)
at checkWin (Ударь по врагу еще раз0.45b — копия.html:261)
at magick (Ударь по врагу еще раз0.45b — копия.html:288)
at HTMLAnchorElement.onclick (Ударь по врагу еще раз0.45b — копия.html:232)
На строке 293 у меня стоит та самая функция r1 по смене монстра.
И прекрасно понимаю, почему браузер ругается - 'className' теперь другой, Ведь Я Поменял Его Через DOM!
Я наплодил данную матрешку (Функция Внутри Функции Внутри Функции) и теперь не знаю как сменить монстра корректно. Я думал уже о том, чтобы удалить функцию r1() после первого ее выполнения, через тот же DOM. Но может есть более корректные способы ?
В идеале условие должно выглядеть не - "выполнять каждый раз когда счётчик будет равен 4 (counter===4 )", а - "как только счётчик будет равен 4 выполнить функцию r1() один раз". Только как это сказать программе я не знаю..((
Ответы (2 шт):
Используйте другой способ поиска целевого объекта. Например так
function r1() {
const div1 = document.getElementById('monster');
div1.className = 'monster2';
}
Или так
function r1() {
const div1 = document.querySelector('.monster');
div1.classList.remove('monster1');
div1.classList.add('monster2');
}
А чтобы выполнить функцию ровно один раз достаточно не обнулять переменную counter
Вероятно, вам придется переписать код. Даже если решите проблему вызова функции, код изначально проблемный, т.к. рассчитан на двух монстров. Если их будет 15, у каждого - разные hp, окажется, что надо было для каждого монстра сделать свой объект с данными: ХП, урон, картинка и прочее.
Какой-то вариант на коленке (слова class, constructor пугают новичков, поэтому здесь они не использованы):
let kill_count = 0;
/***/
let monster_list = [
{ hp: 10, img: "https://gyazo.com/c9745975724e9e0f87c4cebe6462205e.png" },
{ hp: 20, img: "https://gyazo.com/710ce28f75e8a098fdf091e92bc482ba.png" },
];
let monster_hp = null;
let monster_elem = document.querySelector(".monster");
// Это решение вашей проблемы: Изначально сохранить ссылку на элемент
// в переменной, независимо от класса.
let monster_index = -1;
function monster_curr() {
// возвращает объект текущего монстра.
return monster_list[monster_index];
}
function monster_next() {
monster_index++;
let monster = monster_curr();
if (!monster) {
alert("Вы всех убили!");
document.body.textContent = "You Win";
}
monster_hp = monster.hp;
monster_elem.style.backgroundImage = `url('${ monster.img }')`;
}
function monster_display_dmg(num) {
// Для всплывающего урона. Не удержался =)
num = Math.round(num);
let dmg = document.createElement("div");
dmg.className = "dmg";
dmg.textContent = num;
dmg.style.top = 80 + Math.random() * 25 + "px";
dmg.style.left = 75 + Math.random() * 50 + "px";
monster_elem.appendChild(dmg);
setTimeout(function() {
dmg.style.transform = "translateY(-50px)";
dmg.style.opacity = 0;
}, 50);
setTimeout(function() {
monster_elem.removeChild(dmg);
}, 500);
}
/***/
monster_next();
let player_strength = 2;
document.getElementById("hit").addEventListener("click", function() {
if (monster_hp >= 0) {
monster_hp -= player_strength;
monster_display_dmg(-player_strength);
document.getElementById("result").textContent = monster_hp;
}
check_win();
});
function check_win() {
if (monster_hp > 0) return;
kill_count++;
monster_hp = monster_curr().hp * (kill_count % 4);
document.getElementById('hp').textContent = 'You won!';
document.getElementById("counter").textContent = kill_count;
if (kill_count > 0 && kill_count % 4 == 0) {
monster_next();
}
}
body {
display: flex;
}
.monster {
position: relative;
width: 200px;
height: 200px;
}
.dmg {
position: absolute;
transition: 0.8s;
font-size: 32px;
font-family: "Helvetica";
color: white;
text-shadow:
1px 1px #000,
1px -1px #000,
-1px 1px #000,
-1px -1px #000;
}
<div class="monster"></div>
<div>
<button id="hit">Удар!</button>
<div id="result"></div>
<div id="hp"></div>
<div>Убито: <span id="counter">0</span></div>
</div>
Здесь тоже есть проблема: Переключение через каждые 4 монстра. Это число тоже может меняться? Тогда можно было бы его тоже хранить в объекте монстра: Сколько таких же надо убить, чтобы вызывалось переключение на следующего. После каждого убитого уменьшать на 1, дойдет до нуля - переключить.