Проблемы
OP 1 (он же O RIGINAL P oster - он же @ NIKHIL CHANDRA ROY ) использование ( неправильное использование? ... злоупотребление? ) из setInterval()
- это просто средство для переключения "состояния" ( ex. .ok
и .rds
) из .box
Я предполагаю.
Относительно неправильных представлений OP относительно (или, возможно, неспособности должным образом объяснить) следующего утверждения:
document.onclick = null; // important idea
Насколько я понимаю, OP обеспокоен тем, что document
обнаружит событие щелчка и вызывать обработчик событий, тем самым вызывая два вызова обработчика событий в быстрой последовательности. Один раз на document
, а другой на .control
- отсюда и неправильное понимание того, что setInterval()
был полезен в этой конкретной ситуации.
OP обрабатывает обработчики событий как обычные анонимные функции (именно поэтому OP создает две отдельные функции для изменения двух отдельных «состояний» и борьбы с , когда происходит звонок и когда вызов не должен ).
Стандартная анонимная функция
Анонимные функции определяются и вызываются при разборе. Когда браузер замечает скобки ( например. function()
), он интерпретирует его как: "Запустите эту функцию СЕЙЧАС " ).
const nodeRef = () => {...
/* or */
var nodeRef = function() {...
Обработчик событий
Обработчики событий - это функции, которые ждут, пока не произойдет зарегистрированное событие, прежде чем запустить . Обратите внимание на сходство, которое они разделяют:
//~~~~ Defined (aka Named)
domNode.onclick = functionName;
/* or */
domNode.addEventListener('click', functionName);
function functionName(eventObject) {...
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
//~~~ Anonymous
domNode.onclick = function(eventObject) {...
/* or */
domNode.addEventListener('click', function(eventObject) {...
Когда инициируется зарегистрированное событие, вызывается обработчик события, поэтому включаются только круглые скобки, когда определен обработчик события. Обратите внимание, что определенный обработчик событий не имеет круглых скобок ()
, потому что их определения отображаются в верхней части.
Также по умолчанию обработчики событий передают объект события . Всегда включайте его в качестве первого параметра при определении обработчика события. Путем явной передачи объекта события (как правило, e
, event
, evt
...) обработчик события может получить доступ к свойствам объекта события, таким как event.type
, event.currentTarget
и event.target
.
event.currentTarget
- это объект (обычно HTMLElement ), который зарегистрирован в событии (it "слушает" для определенного события c). Он также упоминается как this
в определении связанного с ним обработчика событий. event.target
- это объект, с которого произошло зарегистрированное событие. Например, если была нажата пятая кнопка из ста идентичных кнопок, event.target
будет точно ссылаться на эту конкретную кнопку.
Надеемся, что такое обработчик события и что он делает немного яснее. Кроме того, насколько необходим объект события. В следующем разделе описываются фазы событий, в которых подробно описывается невидимая цепочка событий, происходящих в дереве DOM .
<!DOCTYPE html><!--document------------------?-->
<html><!-----------document.documentElement--?-->
...
<body><!--------document.body-------------?-->
<form id="ui"><!--document.forms.ui-----➕--HTMLFormElement Interface
---event.currentTarget---➕--Registered to click event
---this------------------?-->
<button class='ctx'><!--event.target--⏬-->
CLICKED!
</button>
<fieldset class='box ok'><!--event.target.nextElementSibling--?-❙-?-->
</fieldset>
</form>
</body>
</html>
? Фаза захвата
- напр. Пользователь нажимает
button.ctx
и становится event.target
event.target
родитель - event.currentTarget
(ie form
) - Путь события:
document
, html
, body
, form
. - Путь (и текущая фаза захвата) заканчивается, когда родитель источника события (он же
event.target
) был достигнут. - Эта фаза редко используется специально разработчиком и обычно не учитывается (за некоторыми исключениями, такими как ключевые события)
? Целевая фаза
- ex. После завершения пути от
document
до родительского элемента (ie document.forms.ui
) button
целевая фаза начинается - После определения
event.target
элемент, расположенный сразу после него (ie event.target.nextElementSibling
), изменит "состояние" (.box.ok
ИЛИ .box.rd
). - Обработчик событий останавливает распространение (ie останавливает продолжение фазы барботирования), когда целевая фаза завершена или
.ctx
не нажата. Это желаемое поведение достигается с помощью конечного оператора: event.stopPropagation()
. - Путь к событию имеет только одну ссылку на
event.target
. - Этот этап является наиболее важным, поскольку он выделяет нажатых кнопки, даже если их было тысяча.
Pha Фаза барботирования
- напр. Обычно после целевая фаза завершена, путь обратный.
- Поскольку там, где нет других целей, бессмысленно продолжать цепочку событий, поэтому
event.stopPropagation()
предотвращает дальнейшее продвижение события click вверх по дереву DOM. - Кстати, это также исключает элементы / объекты-предки, такие как
document
, из события щелчка.
Рассмотрим шаблон программирования под названием Делегирование событий .
Преимущества
Несколько целевые элементы 3 (он же event.target
) - ex. a <button>
пользователь нажал) могут быть указаны и исключены нецелевые элементы 4 ( ex. document
или body
, которые могут помешать и помешать правильному поведению, если щелчок в любом месте вызовет обработчик события) .
Зарегистрировать только один элемент для обработки один или несколько целевых элементов одновременно .
К целевым элементам относятся также те, которые динамически добавляются в будущем.
Требования
Anc Элемент estor, который объединяет все te . Это может быть объект верхнего уровня, например document
или window
, но лучше назначить или создать элемент, максимально приближенный к te . Чем ближе, тем менее вероятно будет ошибочное поведение (например, двойные вызовы обработчиков событий).
Зарегистрировать событие для указанного элемента предка. Существует три способа регистрации элемента в событии, используя обычный JavaScript.
Событие свойства события
Мой личный фаворит, потому что он краткий
document.forms.ui.onclick = ctrl;
EventListener
В общем случае наиболее рекомендуемый способ - единственное существенное различие между ними заключается в том, что третий необязательный параметр будет использовать вместо этого фазу захвата фазы пузырьков по умолчанию, передавая true
.
document.forms.ui.addEventListener('click', ctrl, true);
Событие атрибута события
Этот способ обработки события так же стар, как грязь и его использование не рекомендуется из-за множества ограничений. Я включил его только ради полноты, НЕ РЕАЛИЗУЙТЕ ЭТУ ТЕХНИКУ .
<form id='ui' onclick="ctrl(e); return false">
<button class='ctx' type='button'>THIS IS LAME!</button>
<fieldset class='box ok'></fieldset>
</form>
При определении обработчика событий, установите sh следующее:
Убедитесь, что текущий event.target
(помните, что фазы событий не простаивают) не event.currentTarget
. Как только это установит sh, сузьте возможности далее, используя управляющие или троичные операторы.
...
if (event.target !== this) {
if (event.target.classList.contains('ctx')) {...
...
Используя узкий критерий и используя event.stopPropagation()
, исключить все остальное просто.
...
} else {
event.stopPropagation();
}
}
event.stopPropagation();
}
Демо
const ui = document.forms.ui;
ui.onclick = ctrl;
function ctrl(event) {
const clicked = event.target;
if (clicked !== this) {
if (clicked.classList.contains('ctx')) {
const box = clicked.nextElementSibling;
box.classList.toggle('ok');
box.classList.toggle('rd');
} else {
event.stopPropagation();
}
}
event.stopPropagation();
}
.ctx {
font-size: 1.5rem;
cursor: pointer;
}
.box {
width: 200px;
height: 200px;
background: red;
border: 0;
transition: 1s;
}
.rd {
border-radius: 50%;
}
.ok {
border: 10px dotted blue;
}
<form id='ui'>
<button class="ctx" type='button'>Control</button>
<fieldset class="box ok"></fieldset>
</form>
Сноски
1 В сообществе SO (не Конечно, в отношении SE в целом) OP используется взаимозаменяемо между O riginal P oster (участник, задающий вопрос) или O riginal P ost (сам вопрос).
2 Иногда термины обработчик событий , прослушиватель событий и callback могут использоваться для ссылки на функцию, вызываемую после событие происходит. Для целей этого ответа это верно. Более подробное объяснение см. В этом сообщении .
3 Термин целевые элементы ( te ) я придумал для ссылки на элементы в DOM, которые имеют общего предка и могут быть потенциальным event.target
, возможным благодаря хорошо продуманному HTML / CSS и JavaScript, настроенным для эффективного делегирования событий.
4 Нецелевые элементы ( nte ) - это ad-ho c термин для описания элементов, которые должны быть исключенным из реагирования на определенные события по разным причинам. Обычно nte являются элементами / объектами-предками event.currentTarget
, такими как document
, body
, et c. Хотя бывают моменты, когда высокоуровневые узлы в дереве DOM хорошо работают как event.currentTarget
, лучше всего сохранять event.currentTarget
как можно ближе к te , насколько это возможно.
Ссылки
Следующие ссылки охватывают другие аспекты, которые я не охватил полностью, чтобы избежать перегрузки информацией (ретроспективно, я считаю, что я потерпел неудачу).
HTMLFormElement
const ui = document.forms.ui;
.nextElementSibling
clicked.nextElementSibling;