снова о hover

в предыдущем посте я обещал рассказать о том, как можно с пользой применить метод .getDelayedHandlers() — избежать лишнего мельтешения на экране и быть более терпимым к ошибкам пользователя. Выполняя обещание, опишу метод .delayedHover(), который хотя и не применяется так широко, как «улучшенный hover», но всё равно нередко служит добрую службу.

сам по себе метод очень прост:

Element.Methods.delayedHover = 
function(element, handler, delay) {
    element = $(element);
    var delayed = handler.getDelayedHandlers(delay);

    var names = Prototype.Browser.IE 
        ? ['mouseenter', 'mouseleave'] 
        : ['mouseover', 'mouseout'];

    element.observe(names[0], delayed.handlers.over);
    element.observe(names[1], delayed.handlers.out);

    return delayed;
}

на мышиные события элемента навешиваются «отложенные» обработчики, созданные при помощи .getDelayedHandlers(). В результате исходный обработчик вызывается только тогда, когда есть уверенность, что mouseover или mouseout вызваны намеренно, а не из-за нетвёрдой руки пользователя. Применяется метод очевидным образом, например:

$('menu').delayedHover(function(hover){ 
    $('submenu')[hover ? 'show' : 'hide'](); 
});

впрочем, интереснее рассмотреть немного более сложный случай, когда какой-то причине условное «подменю» не вложено в «главное меню». Например, если вы используете один и тот же dom-объект для отображения подсказок к разным элементам страницы. Из соображений производительности неразумно каждый раз переносить его в новое место в дереве документа, гораздо быстрее просто абсолютно позиционировать его в новом месте. Но тогда мы столкнемся с другой проблемой — при наведении мыши на подсказку на исходном объекте случится mouseout, и подсказка исчезнет.

если взглянуть на ситуацию под другим углом, можно прийти к такому заключению: есть команда «показать подсказку», которая выполняется и при наведении на «непонятый» объект, и при наведении на саму подсказку. Есть противоположная команда, выполняющаяся при отводе мыши от объекта или подсказки. В нашем случае эти команды — delayed.handlers.over и delayed.handlers.out, причем они уже навешены на события исходного объекта. Осталось только назначать их событиям подсказки:

var sub = $('submenu');
$('menu').delayedHover(function(hover, delayed){ 
    sub[hover ? 'show' : 'hide'](); 
    var method = hover ? 'observe' : 'stopObserving';
    sub[method]('mouseover', delayed.handlers.over);
    sub[method]('mouseout',  delayed.handlers.out);
});

вуаля! С временны́ми параметрами по умолчанию (400мс/200мс) событие mouseout на исходном объекте не успеет произойти до того, как его отменит mouseover на подсказке. В результате подсказка никуда не исчезнет.

сходным образом .getDelayedHandlers() можно использовать для обнаружения даблкликов или эмуляции события input из грядущего html5.

да, конечно, этого же результата можно добиться и простыми setTimeout/clearTimeout, но предложенный метод мне кажется более наглядным.

Артемий Трегубенко,
, , ,

comments powered by Disqus