Отсебятина Статьи Проекты
Облако тегов
Web CMS CSS htaccess HTML Javascript MySQL Php Безопасность Мониторы Новостная лента Оптимизация Ошибки Разработка сайта Часы Юзабилити оптимизация ошибки

Борьба с onMouseout 2. Устранение мерцания

Отсебятина от 14 марта 2009 года.    Теги: Javascript HTML


Решение с event.stopPropagation()

stopPropagation() предотвращает распространение события на любом этапе (а их у нас три: 1 — захват [capturing], 2 — объект на цели [at target], 3 — всплытие [bubbling]). Рассмотрим метод на примере #2:
Ставим stopPropagation() на элемент div. Захват происходит только до него, дальше событие не распространяется.
Ставим stopPropagation() на элемент span. Захват происходит только до него, достигая цели событие не распространяется.
Ставим stopPropagation() на элемент div, указывая фазу для остановки. В качестве фазы выберем всплытие (bubbling). Захват происходит только до него, дальше событие не распространяется.

Исходные коды можно просмотреть непосредственно на исходных HTML страниц.


Кстати,

Можно остановить не только распространение события, но и его выполнение (смотрите второй листинг).


А теперь конкретно к решению нашей задачи.

В нашем случае происходит:

  1. Вводим курсор в div — срабатывает onMouseover (по At target) и вызывает show(true)
  2. Вводим курсор в textarea — на div срабатывает onMouseout (по At target) и вызывает show(false), но тут же на textarea срабатывает onMouseover (по Bubbling) и вызывает show(true)
  3. Выводим курсор из textarea — на textarea срабатывает onMouseout (по Bubbling) и вызывает show(false), и тут же сработает или не сработает на div onMouseover (по At target) c вызывает show(true). Все зависит от скорости движения мыши и обработки события.
  4. Выводим курсор из div — срабатывает onMouseout (по Bubbling) и вызывает show(false)
Рассмотрим пример, который выводит события в виде "bool => phase", где bool = true/false — включение/выключение, phase = 1/2/3:



div

Получилось:
true=>2 false=>2 true=>3 false=>3 true=>2 false=>2
Из результата тестирования мы видим, что нам необходимо убрать onMouseout bubbling (false=>3), чтобы textarea не пропадал. Bubbling onMouseover убирать не нужно, иначе textarea будет мигать из-за того, что происходит onMouseout родителя.
Чтобы отменить bubling, мы и применим event.stopPropagation() на onMouseout:
<textarea class="child" id="child" onmouseout="event.stopPropagation()">textarea</textarea>

А теперь посмотрим в действии:
div

true=>2 false=>2 true=>3 true=>2 false=>2


Минусы:
— Прописывать каждому вложенному элементу stopPropagation();
— Способ позволяет избавиться только от событий вложенных элементов, в то время как родительские срабатывают при перемещении курсора в тело дочернего элемента;
У родителя должен быть padding или у вложенного элемента margin. Чтобы элемент действительно исчезнул, нам нужно вывести курсор из родителя. Если вложенный элемент будет примыкать вплотную, то события родителя регистрироваться не будут, потому что функция-обработчик вложенного элемента не будет успевать отрабатывать до того, как будет вызвано событие родителя, поэтому последнее будет игнорироваться.

Вариант для автоматического блокирования событий у всех потомков div'a (назначения всем stopPropagation). Код вставляется либо после div'a, либо в onLoad body:
var childs = document.getElementById("parentdiv").childNodes; // где "parentdiv" нашего div'a
for (i = 0; i < childs.length; i++) {
 childs[i].addEventListener("mouseout", (function () { return function (ev) {ev.stopPropagation();}})(), false);
}


Решение с указанием точек входа и выхода

Как я уже говорил, объект event содержит ссылку на объект узла элемента (At target), с которого было вызвано событие. Помимо того, в Firefox event содержит ссылку и на второстепенный узел &mdash relatedTaget, который принял участие в вызове события. Напишем простую команду, которую вставим в функцию show:



document.getElementById("poker3").innerHTML += bool +"=>" + e.eventPhase + " " + e.relatedTarget.tagName+" " + e.target.tagName + "<br>";



div



Если двигаться по схеме: ввести курсор в div, а затем в textarea, после обратно, то мы увидим следующее:
true=>2 CENTER DIV
false=>2 TEXTAREA DIV
true=>3 DIV TEXTAREA
false=>3 DIV TEXTAREA
true=>2 TEXTAREA DIV
false=>2 CENTER DIV

Если посмотреть внимательно, то можно увидеть, что все нежелательные вызовы (onMouseout div, onMouseover textarea, onMouseout textarea, onMouseover div) исходят от textarea (что и не удивительно :) ), и ссылка на этот узел содержится в target, либо relatedTarget. Этим и можно воспользоваться:
function show(bool, e) { if (e.target.tagName == "TEXTAREA" || e.relatedTarget.tagName == "TEXTAREA") return false; document.getElementById("child").style.display = (!bool) ? 'none' : "block"; }

Или так:
function show(bool, e) { var obj = document.getElementById("child"); if (e.target == obj || e.relatedTarget == obj) return false; obj.style.display = (!bool) ? 'none' : "block"; }

Стоит обратить внимание на то, что имя тега элемента, в который вложен div, может быть такое же, как и у вложенного в div элемента:

div.parentNode.tagName == div.childNode[0].tagName

Поэтому стоит использовать второй вариант кода.

Посмотрим на примере:
div



Все работает. Никаких мерцаний в Firefox.

Минусы:
— Необходимо сверяться с каждым вложенным элементом для дальнейшего выполнения обработчика;
— У родителя должен быть padding или у вложенного элемента margin. (Подробнее)

Вариант для множества вложенных элементов:
<script type="text/javascript">
function show2(bool, e, obj) {
var childs = obj.childNodes;
for (i = 0; i < childs.length; i++) {
  if (childs[i] == e.relatedTarget || childs[i] == e.target)
  return false;
 }
 document.getElementById("child2").style.display = (!bool) ? 'none' : "block";
}
</script>
<center>
<div class="parent" onmouseover="show2(true, event, this)" onmouseout="show2(false, event, this)">
  <textarea class="child" id="child2">textarea</textarea>
    div
</div><br>
</center>


Как я уже говорил, мерцание наблюдается только в Firefox, но проблема существует во всех браузерах. В нашем случае, в других браузерах проблема неявна (а в других случая может быть очень заметна &mdash например, счетчик проходов курсора по элементу), поэтому избавляться от него во всех браузерах или нет — решать вам.



В IE вместо target и relatedTarget необходимо использовать event.srcElement, event.toElement и event.fromElement. И, хотя документация microsoft накладывает некоторые условия на эти свойства, они в нашем случае работают. И, кстати, в Opera, где способ Firefox не работает.
Оставить сообщение






Любое копирование должно сопровождаться ссылкой на сайт.
Если вам что-то не понравилось — сообщайте.
Кича Владимир
x
Мне не нравится этот сайт, удалить его