开发者

Problem with jQuery mouseleave firing when container has select box

I have a two containers -- one is nested inside of another. When I hover over the parent, I want the child container to appear. When I mouseout, I want the child container to fadeout. The problem I'm having is the child container has a form that contains a "select box". When the user selects the select box -- the mouseleave event is accidentally fired.

How can I stop the select box from tripping the mouseleave event?

You can see my working code here: http://jsfiddle.net/rsturim/9TZyh/3/

Here's a summary of my script:

$('#parent-container').live("mouseenter", function () {
    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function (e) {
    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().hide();              
});

edit: appears fine in WebKit-based browsers. F开发者_如何学JAVAails in Firefox and IE7-IE9.


In most cases you should simply be able to check to see if the event target was a select element, and only continue in the case that it wasn't. Seems much cleaner than the accepted solution, and worked well in my case.

I've modified the fiddle: http://jsfiddle.net/Dygerati/uj3ZC/5/

$('#parent-container').live("mouseenter", function() {
    var $this = $(this),
        $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function(e) {
    if(e.target.tagName.toLowerCase() != "select") {
        var $this = $(this),
            $selectOptionsContainer = $this.find('#child-container');
        $selectOptionsContainer.stop().hide();
    }
});


$('#parent-container').live("mouseenter", function () {
    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function (e) {

    /* Solution */
    if(e.relatedTarget == null) return;
    /************/

    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().hide();              
});


Since mouseleave and mouseenter events are non-standard you can get such lags here and there. The only method I can suggest to fix that is using some hacks. Here is http://jsfiddle.net/mPDcu/1/ improved version of you code.

var selectOpened = false;
$('#select-grind-type').click(function(e){
    selectOpened = !selectOpened;
    e.stopPropagation();
});
$('body').click(function(){
    if (selectOpened) {
        selectOpened = false;
    }
})
$('#parent-container').on("mouseenter", function() {
    var $this = $(this),
        $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function(e) {
    if (!selectOpened) {
        var $this = $(this),
            $selectOptionsContainer = $this.find('#child-container');
        $selectOptionsContainer.stop().hide();   
    }
});


I had the same problem in a project in which I am contributing, and the other answers didn't work fine for me. My tricky solution was to check if the mouse position inside the event object is inside the parent container. Works pretty good!

var layer = $('#parent-container'),
    layerPos = {};
$(layer).mouseenter(function () {
    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0, function(){
        layerPos.x = {
            min: $(layer).offset().left,
            max: $(layer).offset().left + $(layer).width()
        };
        layerPos.y = {
            min: $(layer).offset().top,
            max: $(layer).offset().top + $(layer).height()
        };
    });
})
$(layer).mouseleave(function(e) {
    // check if event mouse position is inside parent container
    var x_con = e.pageX >= layerPos.x.min && e.pageX <= layerPos.x.max;
    var y_con = e.pageY >= layerPos.y.min && e.pageY <= layerPos.y.max;
    if ( x_con && y_con ) {
        return false;
    }

    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().hide();              
});

You can also check the navigator version to avoid this code to execute in browsers that support this functionality like Chrome.


This partly solves the problem. Unbind the mouseleave event when the select box gains focus and bind again when it loses focus.

http://jsfiddle.net/9TZyh/5/

$('#parent-container').live("mouseenter", function() {
    var $this = $(this);
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave",focusOut);
$("#select-grind-type").live("focus",function(){
    $('#parent-container').die("mouseleave");
});
$("#select-grind-type").live("focusout change",function(){
    $('#parent-container').live("mouseleave",focusOut);
});
function focusOut(e) {
    var $this = $(this),
        $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().hide();
}


If you don't mind having the fade not working in some old browsers, you could do it with CSS quickly:

#parent-container {  }
#child-container {
    opacity:0;
    -webkit-transition:opacity 1s ease-in;
       -moz-transition:opacity 1s ease-in;
}
#parent-container:hover #child-container {{
    opacity:1;
    -webkit-transition:opacity 1s ease-out;
       -moz-transition:opacity 1s ease-out;
}


Those guys give you a working alternative, but it also has some bugs. For example, if you quit the outer box while the combobox is still opened, it won't fade out. I recommend you a much easier alternative that will also fix that bug.

Instead of thinking in the mouseleave event of the inner box, why don't you swap your mind to think in the other way around? I mean, leaving the inner box, also means entering in another container. So you can do outerContainer.mouseenter(function(){ hideInnerBox() }); :-)

Obviously for that purpose innerbox should not be a child of outerbox, even if visually it seems so (css positioning can be used to achieve it)


So I just ran into a similar issue with a <select> nested in a container and came across this question. Here's what I ended up doing.

               $("#container").mouseover(function(e){
                            var t = $(this);
                            t.addClass('active');
                            var offset = t.offset();
                            var xMin = offset.left;
                            var yMin = offset.top;
                            var xMax = xMin + t.innerWidth();
                            var yMax = yMin + t.innerHeight();

                            t.parent().mousemove(function(e){
                                    if(e.pageX < xMin || e.pageX > xMax-2 || e.pageY < yMin || e.pageY > yMax ){
                                            t.removeClass('active');
                                            // unbind this event
                                            $(this).unbind('mousemove');
                                    }

                            });
                    });

Basically, when you mouseover the container, we collect its bounds and start checking whether or not the mouse is over the element. When we know the mouse is gone, we unbind the mousemove listener.

I'd make a jsfiddle for you but its running so slow today!

Hope that helps.


You should only check if the current Element is a descendant of your container.

If so abort the handler.

See: jquery descendant

Example:

ContainerElement.mouseleave(function (e) {
if (ContainerElement.has(e.fromElement).length > 0) return;
// Do your Stuff
});
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜