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

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

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

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

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

Править: Встреченный эта проблема снова, и наконец добрался до корня его. Не была проблема распространения событий вообще! См. мой ответ ниже для деталей.

19
задан Matt 5 January 2011 в 17:12
поделиться

2 ответа

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

Теоретически: Глава Энди Э услужливо обнаружил , что DOM2 сообщает, что событие должно продолжаться , потому что пузырьки должны быть основаны на исходном начальном состоянии дерева. Так что поведение большинства правильное, IE здесь сам по себе. Quelle сюрприз.

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

Эмпирические результаты для аспекта «продолжает пузыри»:

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

ОБНОВЛЕНИЕ: При тестировании 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 обработчиков (через прототип):

<!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>
25
ответ дан 30 November 2019 в 03:43
поделиться

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

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


Т.Дж. Комментарий Краудера побудил меня привести быстрый пример. Я был прав по обоим пунктам, он всплывает, и вы все еще можете получить ссылку на удаленный узел, используя event.target.

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


Как Т.Дж. обнаружил, что в IE это не так. Но спецификация событий DOM уровня 2 действительно определяет это как правильное поведение [выделено мной] :.

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

5
ответ дан 30 November 2019 в 03:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: