Подтвердить что ты не робот

Если вы удалите элемент DOM, все события, которые начинаются с этого элемента, продолжают пузыриться?

Какое поведение следует ожидать, если я удалю элемент DOM, который был использован для запуска пузырька события или чей дочерний элемент начал пузырь событий - будет ли он продолжать пузыриться, если элемент будет удален?

Например, скажем, у вас есть таблица и вы хотите обнаружить события щелчка на ячейках таблицы. Другая часть JS выполнила запрос AJAX, который, в конце концов, полностью заменит таблицу после завершения запроса.

Что произойдет, если я нажму на таблицу и сразу после замены таблицы успешным завершением запроса AJAX? Я спрашиваю, потому что я вижу какое-то поведение, когда события щелчка не кажутся пузырящимися - но его трудно дублировать.

Я смотрю событие на родительском элементе таблицы (вместо привязки события к каждому TD), и он просто не доходит до него.

РЕДАКТИРОВАТЬ: снова повторил эту проблему и, наконец, добрался до ее корня. Это не волнующий события вопрос! Подробнее см. Мой ответ ниже.

4b9b3361

Ответ 1

Эмпирически:. Это зависит от того, какой браузер вы используете; IE отменяет событие, все остальное (насколько я могу судить) продолжает его. См. Тестовые страницы и обсуждение ниже.

Теоретически: Andy E head нашел, что DOM2 говорит, что событие должно продолжаться, поскольку пузырьки должны основываться на начальном состоянии дерева. Поэтому поведение большинства правильное, IE здесь. Quelle удивляет.

Но: То, что связано с тем, что вы видите, - это еще один вопрос. Вы просматриваете клики на родительском элементе таблицы, и, как вы подозреваете, это очень редко, когда вы щелкаете по таблице, есть условие гонки с завершением Ajax, которое заменяет таблицу, и щелчок теряется. Это условие гонки не может существовать в интерпретаторе Javascript, потому что на данный момент Javascript в браузерах является однопоточным. (Рабочие потоки приходят, хотя — whoo hoo!) Но теоретически, щелчок может произойти и получить очередь в потоке пользовательского интерфейса, отличного от Javascript, в браузере, тогда ajax может заполнить и заменить элемент, а затем поставить в очередь Событие пользовательского интерфейса обрабатывается и не происходит вообще или не пузырится, потому что элемент больше не имеет родителя, который был удален. На самом деле это может сильно повлиять на реализацию браузера. Если вы видите его в каких-либо браузерах с открытым исходным кодом, вы можете посмотреть их источник для очередности событий пользовательского интерфейса для обработки интерпретатором. Но это другое дело, чем удаление элемента с помощью кода внутри обработчика событий, как показано ниже.

Эмпирические результаты для аспекта do-bubbling-continue:

Тестирование Chrome 4 и Safari 4 (например, WebKit), Opera 10.51, Firefox 3.6, IE6, IE7 и IE8. IE был единственным, кто отменил это событие, когда вы удалили элемент (и сделали это последовательно в разных версиях), ни один из остальных не сделал. Кажется, не имеет значения, используете ли вы обработчики DOM0 или более современные.

UPDATE: При тестировании IE9 и IE10 продолжают событие, поэтому IE несоблюдение спецификации останавливается в IE8.

Проверить страницу с помощью обработчиков DOM0:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
    font-family: sans-serif;
}
#log p {
    margin:     0;
    padding:    0;
}
</style>
<script type='text/javascript'>
window.onload = pageInit;

function pageInit() {
    var parent, child;

    parent = document.getElementById('parent');
    parent.onclick = parentClickDOM0;
    child = document.getElementById('child');
    child.onclick = childClickDOM0;
}

function parentClickDOM0(event) {
    var element;
    event = event || window.event;
    element = event.srcElement || event.target;
    log("Parent click DOM0, target id = " + element.id);
}

function childClickDOM0(event) {
    log("Child click DOM0, removing");
    this.parentNode.removeChild(this);
}

function go() {
}

var write = log;
function log(msg) {
    var log = document.getElementById('log');
    var p = document.createElement('p');
    p.innerHTML = msg;
    log.appendChild(p);
}

</script>
</head>
<body><div>
<div id='parent'><div id='child'>click here</div></div>
<hr>
<div id='log'></div>
</div></body>
</html>

Проверить страницу с помощью обработчиков attachEvent/addEventListener (через Prototype):

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
    font-family: sans-serif;
}
#log p {
    margin:     0;
    padding:    0;
}
</style>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js'></script>
<script type='text/javascript'>
document.observe('dom:loaded', pageInit);
function pageInit() {
    var parent, child;

    parent = $('parent');
    parent.observe('click', parentClick);
    child = $('child');
    child.observe('click', childClick);
}

function parentClick(event) {
    log("Parent click, target id = " + event.findElement().id);
}

function childClick(event) {
    log("Child click, removing");
    this.remove();
}

function go() {
}

var write = log;
function log(msg) {
    $('log').appendChild(new Element('p').update(msg));
}
</script>
</head>
<body><div>
<div id='parent'><div id='child'>click here</div></div>
<hr>
<div id='log'></div>
</div></body>
</html>

Ответ 2

Да, он должен продолжать распространяться. События не имеют реальной привязки к событию, которое они запускали, за исключением свойства target. Когда вы удаляете элемент, внутренний код, распространяющий событие, не должен иметь никакого "осознания", которое исходный элемент ушел из видимого документа.

В стороне от использования removeChild не удаляется элемент сразу, он просто отделяет его от дерева документов. Элемент должен удаляться только/мусор, если нет ссылок на него. Поэтому возможно, что элемент все равно можно было бы ссылаться через свойство event.target и даже повторно вставлен перед сбором мусора. Я еще не пробовал, так что это просто спекуляция.

<ч/" > T.J. Комментарий Crowder заставил меня принять решение о том, чтобы выбить быстрый пример. Я был прав по обоим пунктам, он пузырится, и вы все равно можете получить ссылку на удаленный node с помощью event.target.

http://jsbin.com/ofese/2/

<ч/" > Как T.J. Обнаружено, что это не так в IE. Но спецификация DOM Level 2 Events определяет ее как правильное поведение [emphasis mine]:.

События, которые обозначаются как пузыри, сначала будут проходить с тем же потоком событий, что и события, не связанные с пузырьками. Событие отправляется на целевую EventTarget, и все обнаруженные там прослушиватели событий запускаются. События Bubbling затем запускают любые дополнительные прослушиватели событий, которые следуют за родительской цепочкой EventTarget вверх, проверяя наличие прослушивателей событий, зарегистрированных на каждом последующем EventTarget. Это восходящее распространение будет продолжаться вплоть до Документа. EventListeners, зарегистрированные в качестве захватчиков, не будут запущены на этом этапе. Цепочка EventTargets от целевой цели до вершины дерева определяется до начальной отправки события. Если в процессе обработки событий происходят изменения, дерево событий будет происходить на основе исходного состояния дерева.

Ответ 3

Прошло довольно много времени с тех пор, как я изначально разместил этот вопрос. Хотя ответ T.J.Crowder был очень информативным (как и Энди Э), и сказал мне, что он должен работать, я продолжал видеть проблему. Я отложил это в течение некоторого времени, но пересматривал его сегодня, когда снова столкнулся с той же проблемой в другом веб-приложении.

Я играл с ним некоторое время, и я понял, как дублировать проблему каждый раз (по крайней мере, в FF3.6 и Chrome 8). Проблема заключалась не в том, что пузырь события отменялся или терялся при удалении элемента DOM. Вместо этого проблема заключается в том, что если элемент изменяется между mousedown и mouseup, 'click' не срабатывает.

В Mozilla Development Network:

Событие click возникает, когда пользователь нажимает на элемент. Событие click произойдет после событий mousedown и mouseup.

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

Дополнительное тестирование (см. пример на jsfiddle) показывает, что если один клик, удерживает кнопку нажатой и ждет изменения элемента DOM, и затем отпускает кнопку, мы можем наблюдать (в контексте jQuery live):

  • Событие 'click' не срабатывает
  • Событие "mousedown" запускается для первого node
  • Событие "mouseup" запускается для обновленного node

EDIT: Протестировано в IE8. IE8 запускает mousedown для первого node, mouseup для обновленного node, но на самом деле fire 'click', используя обновленный node как источник события.