Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Opera/IE баг с очередью обработки события несколькими элементами (напр onmouseover) (https://javascript.ru/forum/events/2993-opera-ie-bag-s-ocheredyu-obrabotki-sobytiya-neskolkimi-ehlementami-napr-onmouseover.html)

no. 07.03.2009 06:13

Opera/IE баг с очередью обработки события несколькими элементами (напр onmouseover)
 
Нашел такой баг - если в ячейке таблицы есть внутри элементы, например, div, который занимает всю ячейку TD и для td задана функция onmouseover, а для div не задана нигде и никак, то в процессе обработки этой функции onmouseover в ней каким то чудом в качестве Event.srcElement фигурирует div хотя для него, повторюсь, не была никоим образом задана функция onmouseover. Этот баг есть и в ИЕ и в Опере, в опере он я бы сказал пожесче.
элементы кода:

так выглядит ячейка таблицы:
<td><div class="rc"><div class="dgr" onclick="f1(...);">X</div>123<br><b>Текст1</b><br>Текст2</div></td>

onmouseover всем ячейкам таблицы задает функция так (срабатывает при загрузке страницы):
в цикле пробегаем по всем ячейкам и

<цикл>
...
if(td.attachEvent) {
td.attachEvent('onmouseover', HighLightTD); td.attachEvent('onmouseout', HideTD);
}

if(td.addEventListener) {
td.addEventListener('mouseover', HighLightTD, true); td.addEventListener('mouseout', HideTD, true);}
...

Функции HighLigtTD и HideTD соотв подсвечивают или убирают подсветку с ячейки таблицы. Но не только. Подсветка/Хайд изначальна в Опере тоже глючили из-за описываемого бага. Но с этим я справился. Самый главный баг в том что функции HighLightTD/HideTD срабатывают и на наведение на внутренний div ячейки таблицы, хотя для них не устанавливались. Создается впечатление что это наследование внутренним дивом функции onmouseover ячейки таблицы td.

Ну для большей ясности

function HighLightTD(e)
{
if(!e) var e = window.event;
if(e.srcElement) td = e.srcElement; else td = e.currentTarget;

// !! для проверки делаем алерт
alert(td.tagName);
// и в этом алерте какимто чудом появится DIV хотя, повторюсь, на DIV эта функция не вешалась
...
}

итак:
<td onmouseover=...> <div> .. </div> </td>

наводим мышку на td сначала будет алерт с тэгом TD, а после сразуже с DIV

Помогите с этим разобраться, как исключить обработку события onmouseover для внутреннего div'a ячейки таблицы?

Да в Файрфокс всё прекрасно работает.

Zibba 07.03.2009 12:56

Почитайте статью про события мыши на этом сайте, там упоминается об этом.

Kolyaj 07.03.2009 13:24

Одну из самых нужных особенностей DOM назвать багом -- это сильно :)

no. 07.03.2009 14:27

Багом называю потому как привык к самой правильной и ожидаемой, в большинстве случаев, обработке ХТМЛ/ДОМ/Ява со стороны ФайрФокс. И в этом случае он работает ожидаемо и правильно ИМХО. Не подскажите конкретную статейку? Я в общем то знаком более-менее с этой темой, и перед созданием темы поискал похожее но ответов не нашел :\

Zibba 07.03.2009 14:36

Порядок срабатывания событий, а вообще было бы полезно всю статью прочитать для понимания механизма.

no. 07.03.2009 15:23

Уже прочитал и в общем-то этот механизм я знал (читал до этого на английском поэтому статья на русском + хорошо сделанные примеры более объяснили некоторые тонкости), НО! Это помогло понять более точнее природу БАГа в Опере, но всё же не то как этот баг победить. Баг тут, имхо, заключается в том что событие onmouseover родительского элемента срабатывает, затем срабатывает onmouseover дочернего и тут же - onmouseout родительского (такое впечатление) как будто бы событие дойдя до потомка (всплытие - bubble) ввело "мышь" в заблуждение, и она "подумала" что она уже ушла с элемента-родителя TD и запустился обработчик onmouseout. Это даже заметно - подсветка мелькает и тут же пропадает! Если сделать обычную подсветку бэкграунда родительского элемента, с учетом его текущего цвета (т.е. при подсветке (onmouseover) сохранять текущий цвет фона родителя, а потом его восстанавливать onmouseout) Вы увидите этот баг, т.к.
1. onmouseover - сделали цвет фона родителя color_new, сохранили цвет родителя в last_color
2. идет второй onmouseover, для внутреннего дива (как на этом этапе выявить что srcElement не тот что нам нужен и пропустить обработку функции? проверка srcElement.tagName как-то не помогает, это один важный вопрос, но его можно, при решении вопроса с подсветкой, обойти по-другому)
3. тут же срабатывает onmouseout для родителя (TD) - родителю вернули его старый цвет last_color

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

no. 07.03.2009 15:51

Еще раз подумав: самый главный вопрос
из статьи:
Цитата:

При установке обработчиков методами attachEvent/detachEvent this внутри обработчика всегда указывает на объект window и совершенно бесполезен.

Поэтому при использовании этих методов в библиотеках и фреймворках добавляется дополнительная "обертка для обработчика", устанавливающая правильный this.
Как эта обертка делается? что-то вроде того: el.attachEvent('event', function(e) { f1(e, el); }); ?

Андрей Параничев 07.03.2009 20:35

Цитата:

Сообщение от no.
Как эта обертка делается?

Я использую такую обертку:
/**
 * Объявляем и сразу вызываем функции,
 * чтоб не проверять поддержку методов
 * каждый раз:
 */
var addEventListener = function() {
	// Если браузер IE:
	if (window.attachEvent)
		// Возвращаем функцию для IE:
		return function(element, event, callback) {
			// Фиксуем callback, чтоб установить правильный
			// this, и передавать объект события первым параметром:
			callback.__callbackfix = function() {
				callback.call(element, window.event);
			};
			// Устанавливаем обработчик (не забываем про "on")
			return element.attachEvent("on" + event, callback.__callbackfix);
		}
	// Если браузер W3C-совместим:
	if (window.addEventListener)
		// Возвращаем функцию без фиксов:
		return function(element, event, callback) {
			// Последний параметр всегда false, для совместимости с IE:
			return element.addEventListener(event, callback, false);
		}
}();

var removeEventListener = function() {
	// Если браузер IE:
	if (window.detachEvent)
		return function(element, event, callback) {
			// Удаляем наш сохраненный в функции фикс:
			return element.detachEvent("on" + event, callback.__callbackfix);
		}
	// Если браузер W3C-совместим:
	if (window.addEventListener)	
		return function(element, event, callback) {
			// Удаляем без фиксов:
			return element.removeEventListener(event, callback, false);
		}
}();


Он избавляет от всех проблем несовместимости, кроме тех, которые связаны с различными свойствами объекта события.
Но теперь, по крайней мере, в IE будет возможность удалять callback с фиксом "прозрачно".

no. 08.03.2009 04:55

Всем спасибо вроде разобрался с проблемой.
Для оперы просто изменил проверку на srcElement и поставил его на второе место, а на первое место currentTarget, который у последней оперы есть (давно ли?). А вот для IE пришлось делать обертку по типу того что я писал и Андрей Параничев, и вставлять в функцию оригинальный элемент вызова функции, т.е. TD + помогла статья с замыканиями.
Тему вроде можно закрывать.


Часовой пояс GMT +3, время: 02:58.