Почему селектор классов не создается при нажатии кнопки?

Ниже приведен HTML-код:

<!DOCTYPE html>
<html>

<head>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script type="text/javascript">
    var list = document.querySelector('ul');
    list.addEventListener('click', function(ev) {
        if (ev.target.tagName === 'LI') {
            ev.target.classList.toggle('done');
        }
    }, false);
    </script>
</head>

<body>
    <ul>
        <li>Buy milk</li>
        <li>Take the dog for a walk</li>
        <li>Exercise</li>
        <li>Write code</li>
        <li>Play music</li>
        <li>Relax</li>
    </ul>
</body>

</html>

ниже код CSS:

li {
  list-style-type: none;
  position: relative;
  margin: 2px;
  padding: 0.5em 0.5em 0.5em 2em;
  background: lightgrey;
  font-family: sans-serif;
}

li.done {
  background: #CCFF99;
}

li.done::before {
  content: '';
  position: absolute;
  border-color: #009933;
  border-style: solid;
  border-width: 0 0.3em 0.25em 0;
  height: 1em;
  top: 1.3em;
  left: 0.6em;
  margin-top: -1em;
  transform: rotate(45deg);
  width: 0.5em;
}

Выше HTML-код не создает class="done" при щелчке при запуске в браузере.

На скрипке тот же код работает нормально.

2 ответа

Решение

Как код включен в <head> он выполняется до содержания <body> загружен. Таким образом, элементы не найдены в DOM, когда привязка событий и событие не связаны.

Есть два способа решить эту проблему.

  1. Оберните код в DOMContentLoaded обратный вызов

    document.addEventListener('DOMContentLoaded', function () {
        var list = document.querySelector('ul');
        list.addEventListener('click', function (ev) {
            if (ev.target.tagName === 'LI') {
                ev.target.classList.toggle('done');
            }
        }, false);
    });
    

    Обновленная скрипка: https://jsfiddle.net/tusharj/pr2hw6c1/

    Узнайте больше о DOMContentLoaded: https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded

  2. Переместить код в конец <body>, чтобы при выполнении скрипта загружались все элементы DOM.

Примечание: вы также можете использовать load событие для выполнения кода скрипта после полной загрузки DOM, но это будет ждать загрузки всех ресурсов (изображения, фреймы и т. д.) на странице, что необязательно для привязки события к элементам. Для справки прочтите Разница между событиями DOMContentLoaded и Load


Почему тот же код в jsfiddle работает?

jsfiddle предоставляет четыре опции для того, где скрипт должен быть включен. Вы можете установить эту опцию на левой панели, под списком библиотек.

  1. onLoad: Это так же, как load событие окна. Код обернут в обратный вызов load событие так, функции / переменные не могут быть доступны извне. Ex. Из встроенных обработчиков HTML.
  2. onDomReady: Это так же, как DOMContentLoaded или же ready jquery, вот и код завернут
  3. No wrap - in Head: Это означает, что код JS будет добавлен в head, No wrap означает, что код не обернут ни в одну функцию, как в load а также DOMContentLoaded, Таким образом, определенные функции / переменные являются глобальными.
  4. No wrap - in Body: Скрипт загружается в конце <body> элемент.

Теперь, когда мы знаем, как скрипт ведет себя для каждой опции, в скрипте опция для местоположения скрипта установлена ​​на onLoad, так что код работает. Код будет работать для всех опций, кроме No wrap - in Head,

Подробнее об этом читайте в документации по jsfiddle http://doc.jsfiddle.net/basic/introduction.html

Попробуйте переместить тег сценария и поместить его в самый конец тела. Теперь так, как есть, скрипт пытается запустить до загрузки элементов на странице. Перемещение тега script внизу тела обеспечит правильную загрузку всех HTML-элементов над ним.

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