javascriptjqueryjquery-eventsjquery-deferred

Can Deferred object chains be used on event handlers?


I'm somewhat new to Javascript / jQuery (but not to programming), so please excuse any ignorance on this matter. I've already looked at this question and this one, but neither one solved my problem.

My scenario: I have a slideshow written in Javascript / jQuery. In this slideshow, I would like show the controls (play button, backward, forward) when the user hovers over the slide and hide them when the mouse leaves the slide area. The buttons show and hide as expected, but when I hover over one of the buttons (which are also over the slideshow) it disappears (which I know is because of "mouseleave" on the slide).

I tried adding hover event handlers to my buttons to change a global boolean so that the slideshow controls will only be hidden if that boolean is false (meaning the "mouseenter" for one of the buttons wasn't fired). I then registered the "mouseenter" event before the "mouseleave", but it didn't work.

Here's the problem: Despite the "mouseenter" event for the button being fired before the "mouseleave" event for the slide, the event handler callbacks are executed in the opposite order. I'm not entirely sure why this is happening, but I know that it has to do with how hover events are handled in Javascript.

I read a little bit about Deferred objects and I'm wondering if this might be the right way to go about this?

The syntax is incorrect, but I'm thinking something along the lines of:

function isMouseOverButton() {
    if(mouseEnteredButtonEvent()) {
        window.isOverButton = true;
    } else {
        window.isOverButton = false;
}

function hideControls() {
    if(!window.isOverButton) {
        //hide buttons
    }
}

$.when($("#slide").mouseleave()).then(isMouseOverButton()).then(hideControls());

Is there anyway to combine event handlers and deferred objects like this? Or, is there a better way than deferred objects?

EDIT: I just came across this, but I would prefer not to use a plugin to accomplish this.


Solution

  • Nope, this is not the way to do it!

    When the mouse enters the slides, you show the controls, and when the mouse leaves the slides, you hide the controls, except if the mouse enters the controls.

    To do this you'll use a small timeout and check if the mouse entered the controls before they are hidden away.

    var timer;
    
    $('#slide').on({
        mouseenter: function() {
            clearTimeout(timer);
            $('.controls').show();
        },
        mouseleave: function() {
            timer = setTimeout(function() { //don't hide them right away
                $('.controls').hide();
            }, 400); // set a timeout
        }
    });
    
    $('.controls').on({
        mouseenter: function() {
            clearTimeout(timer);  // if the mouse entered the controls, 
        },                        // clear the timeout, keeping the controls visible,
        mouseleave: function() {
            timer = setTimeout(function() {
                $('.controls').hide();
            }, 400);
        }
    });
    

    FIDDLE

    A much easier way to do the same thing, would be to place the controls inside the #slide element in the HTML, that way they won't trigger the mouseleave event, but that's not always possible, or convenient.