Почему селектор классов не создается при нажатии кнопки?
Ниже приведен 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, когда привязка событий и событие не связаны.
Есть два способа решить эту проблему.
Оберните код в
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
Переместить код в конец
<body>
, чтобы при выполнении скрипта загружались все элементы DOM.
Примечание: вы также можете использовать load
событие для выполнения кода скрипта после полной загрузки DOM, но это будет ждать загрузки всех ресурсов (изображения, фреймы и т. д.) на странице, что необязательно для привязки события к элементам. Для справки прочтите Разница между событиями DOMContentLoaded и Load
Почему тот же код в jsfiddle работает?
jsfiddle предоставляет четыре опции для того, где скрипт должен быть включен. Вы можете установить эту опцию на левой панели, под списком библиотек.
onLoad
: Это так же, какload
событие окна. Код обернут в обратный вызовload
событие так, функции / переменные не могут быть доступны извне. Ex. Из встроенных обработчиков HTML.onDomReady
: Это так же, какDOMContentLoaded
или жеready
jquery, вот и код завернутNo wrap - in Head
: Это означает, что код JS будет добавлен вhead
,No wrap
означает, что код не обернут ни в одну функцию, как вload
а такжеDOMContentLoaded
, Таким образом, определенные функции / переменные являются глобальными.No wrap - in Body
: Скрипт загружается в конце<body>
элемент.
Теперь, когда мы знаем, как скрипт ведет себя для каждой опции, в скрипте опция для местоположения скрипта установлена на onLoad
, так что код работает. Код будет работать для всех опций, кроме No wrap - in Head
,
Подробнее об этом читайте в документации по jsfiddle http://doc.jsfiddle.net/basic/introduction.html
Попробуйте переместить тег сценария и поместить его в самый конец тела. Теперь так, как есть, скрипт пытается запустить до загрузки элементов на странице. Перемещение тега script внизу тела обеспечит правильную загрузку всех HTML-элементов над ним.