Переменная сама сбрасывается JS

Рейтинг: 0Ответов: 2Опубликовано: 21.03.2023

Я еще не очень опытный, поэтому решил обратиться сюда

У меня есть фрагмент кода:

let labels = document.getElementsByTagName('label');
let label,
input;

// Для каждого label создаем eventListener
for (let index = 0; index < labels.length; index++) {
    labels[index].addEventListener('click', () => {

        // 1. Получаем сам label
        label = labels[index];
        console.log(label);

        // 2. Получаем привязанный к нему input
        input = document.getElementById(label.getAttribute('for'));

        // 3. Меняем состояние input
        input.checked = !(input.checked); 
        
        // 4. Меняем фон
        label.style.backgroundColor = input.checked ? '#E95D00' : '#C4C4C4';
})  
}

Этот код отвечает за вот этот фрагмент HTML:

<div class="radio__container">
    <div class="radio__item">
        <input class="radio__button"type="radio" id="vk">
        <label for="vk">Вконтакте</label>
    </div>
                                
    <div class="radio__item">
        <input class="radio__button"type="radio" id="youtube">
        <label for="youtube">Youtube</label>
    </div>
    
    <div class="radio__item">
        <input class="radio__button"type="radio" id="rutube">
        <label for="rutube">Rutube</label>
    </div>
    
    <div class="radio__item">
        <input class="radio__button"type="radio" id="telega">
        <label for="telega">Telegram</label>
    </div>
    
    <div class="radio__item">
        <input class="radio__button"type="radio" id="ok">
        <label for="ok">Одноклассники</label>
    </div>
</div>
  1. Я кликаю на один label, предположим на "Вконтакте"
  2. Как и положено input.checked, относящийся <input class="radio__button"type="radio" id="ok">, принимает значение true из дефолтного для чекбокса false
  3. Я кликаю на этот блок снова и как и положено чекбокс принял значение false
  4. С этого момента начинается проблема. Сразу после выполнения предыдущего eventListener'а значение input.checkbox сбрасывается на true.

Я достаточно долго время потратил на это, пытаясь прийти к решению этой проблемы, изучая её в отладчике...Буду рад любой помощи!

Ответы

▲ 1Принят

Если очень коротко, то проблема Вашего кода заключается в том что Вы упустили из виду один важный момент — создание label с указанным for привязывает ее к конкретному элементу, что в свою очередь позволяет по клику на нее переключать значение опции. Т.е. у вас накладываются оба переключения, автоматическое и логическое. Стоит убрать аттрибут for все заработает:

let labels = document.getElementsByTagName('label');
let label, input;

// Для каждого label создаем eventListener
for (let index = 0; index < labels.length; index++) {
    labels[index].addEventListener('click', () => {

        // 1. Получаем сам label
        label = labels[index];

        // 2. Получаем привязанный к нему input
        input = label.previousElementSibling

        // 3. Меняем состояние input
        input.checked = !input.checked
        
        // 4. Меняем фон
        label.style.backgroundColor = input.checked ? '#E95D00' : '#C4C4C4';
})  
}
<div class="radio__container">
    <div class="radio__item">
        <input class="radio__button"type="radio" id="vk">
        <label >Вконтакте</label>
    </div>
                                
    <div class="radio__item">
        <input class="radio__button"type="radio" id="youtube">
        <label >Youtube</label>
    </div>
    
    <div class="radio__item">
        <input class="radio__button"type="radio" id="rutube">
        <label >Rutube</label>
    </div>
    
    <div class="radio__item">
        <input class="radio__button"type="radio" id="telega">
        <label >Telegram</label>
    </div>
    
    <div class="radio__item">
        <input class="radio__button"type="radio" id="ok">
        <label >Одноклассники</label>
    </div>
</div>

Замечу, что previousElementSibling находит предедущий элемент в разметке.

Но тут встает другой вопрос, писать код хочется по семантике. И тогда мы возвращаем аттрибут, но меняем подход работы с нашими элементами, т.е. работаем не конкретно с ними, а с данными, и этот подход я всегда советую использовать так как DOM крайне не надежен - например в приведенном коде можно кликнуть непосредственно на саму опцию (не на подпись) что создаст путаницу.

let state = {}

document.querySelector('.radio__container').onclick = function(event){
  event.preventDefault()
  if (event.target.tagName === 'LABEL'){
    const label = event.target
    const id = label.getAttribute('for')
    state[id] = !state[id]
    label.previousElementSibling.checked = state[id]
    label.style.backgroundColor = state[id] ? '#E95D00' : '#C4C4C4'
  }
}
<div class="radio__container">
    <div class="radio__item">
        <input class="radio__button"type="radio" id="vk">
        <label for="vk">Вконтакте</label>
    </div>
    <div class="radio__item">
        <input class="radio__button"type="radio" id="youtube">
        <label for="youtube">Youtube</label>
    </div>
    <div class="radio__item">
        <input class="radio__button"type="radio" id="rutube">
        <label for="rutube">Rutube</label>
    </div>
    <div class="radio__item">
        <input class="radio__button"type="radio" id="telega">
        <label for="telega">Telegram</label>
    </div>
    <div class="radio__item">
        <input class="radio__button"type="radio" id="ok">
        <label for="ok">Одноклассники</label>
    </div>
</div>

▲ 0

По клику у radio, действием по умолчанию можно только выставить checked. Т.к. вы не отключаете это действие, то срабатывает сначало ваш код, а потом действие по умолчанию. Как вариант, можете отключить это действие...

let labels = document.getElementsByTagName('label');
let label,
    input;

// Для каждого label создаем eventListener
for (let index = 0; index < labels.length; index++) {
    labels[index].addEventListener('click', (e) => {
        // 0. Сбрасываем действие по умолчанию
        e.preventDefault();

        // 1. Получаем сам label
        label = labels[index];
        console.log(label);

        // 2. Получаем привязанный к нему input
        input = document.getElementById(label.getAttribute('for'));

        // 3. Меняем состояние input
        input.checked = !(input.checked); 
        
        // 4. Меняем фон
        label.style.backgroundColor = input.checked ? '#E95D00' : '#C4C4C4';
    })  
}

Или, как вариант, вместо radio использовать checkbox