当容器中含有下拉框时,jQuery的mouseleave事件会触发问题

13

我有两个容器 - 一个嵌套在另一个内部。当我悬停在父容器上时,我希望子容器出现。当我移开鼠标时,我希望子容器渐隐。问题是子容器中有一个包含“选择框”的表单。当用户选择选择框时,会意外触发mouseleave事件。

如何阻止选择框触发mouseleave事件?

您可以在此处查看我的工作代码:http://jsfiddle.net/rsturim/9TZyh/3/

以下是我的脚本摘要:

$('#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();              
});

编辑:在基于 WebKit 的浏览器中显示正常。在 Firefox 和 IE7-IE9 中出现错误。


只是选择框还是其他输入也包括在内? - Amit Gupta
我的错误——看来WebKit浏览器没问题(Chrome/Safari)。但是IE7/IE8/IE9和Firefox都出现了问题。 - rsturim
Amit Gupta:似乎只限于选择框。我添加了一个文本输入字段,它是正常的。 - rsturim
@rsturim:你已经有将近两年的时间来学习SO评论是如何工作的了! - Lightness Races in Orbit
9个回答

13
在大多数情况下,您应该只需检查事件目标是否为选择元素,仅在不是时继续进行。这似乎比已接受的解决方案更清晰,并且在我的情况下效果很好。
我修改了这个小Demo: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();
    }
});

1
这个回答比被采纳的那个好多了。我只想补充一下,在检查“select”后,应该添加“&& e.target.tagName.toLowerCase() != 'option' && e.target.tagName.toLowerCase() != 'input'”,以便考虑选择超出容器区域边界的下拉列表中的选项和从文本字段中选择预输入数据的情况。 - Jason Gray

8
$('#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();              
});

3

由于mouseleave和mouseenter事件是非标准的,因此可能会出现一些延迟。我唯一能建议解决这个问题的方法是使用一些技巧。这里是您代码的改进版本:http://jsfiddle.net/mPDcu/1/

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();   
    }
});

肯定只需要这样做 if ($selectOptionsContainer.find('select:hover, option:hover').length) 不要隐藏。 - user1850421

2

我在参与的一个项目中也遇到了同样的问题,其他答案对我没有起作用。我的巧妙解决方案是检查事件对象中的鼠标位置是否在父容器内。效果非常好!

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();              
});

您还可以检查浏览器版本,以避免在支持这种功能(如Chrome)的浏览器中执行此代码。


好的方法,但是当Dropbox列表比父容器区域更长时(没有测试),它可能不起作用。 - Chexpir

1

这部分解决了问题。 当选择框获得焦点时,解除 mouseleave 事件的绑定,失去焦点时再重新绑定。

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();
}

0

如果您不介意在一些旧浏览器中淡入淡出效果无法正常工作,您可以通过CSS快速实现:

#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;
}

0

你应该只检查当前元素是否是容器的后代。

如果是,则中止处理程序。

参见:jquery descendant

示例:

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

0

我最近遇到了一个类似的问题,涉及到一个嵌套在容器中的 <select> 标签,然后看到了这个问题。这是我最终采取的解决方案。

               $("#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');
                                    }

                            });
                    });

基本上,当您将鼠标悬停在容器上时,我们会收集其边界并开始检查鼠标是否在元素上方。当我们知道鼠标已经离开时,我们会解除mousemove监听器的绑定。
我想为您创建一个jsfiddle,但今天运行得太慢了!
希望这有所帮助。

0

这些人给了你一个可行的替代方案,但它也有一些错误。例如,如果你在组合框仍然打开的情况下退出外框,它不会淡出。我建议你使用一个更简单的替代方案,它也会修复那个错误。

与其考虑内部框的mouseleave事件,为什么不换个思路呢?我的意思是,离开内部框,也意味着进入另一个容器。所以你可以这样做:outerContainer.mouseenter(function(){ hideInnerBox() }); :-)

显然,为了达到这个目的,内部框不应该是外部框的子元素,即使在视觉上看起来是这样的(可以使用CSS定位来实现)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接