使用jQuery在失去焦点时执行操作,除非特定元素被点击。

35

这里有两个元素在起作用:

$('#myInput') // an input field for search
$('#myList') // a list to display search results

我希望在输入框失去焦点后隐藏列表,如下所示:

$('#myInput').blur(function() {
  $('#myList').hide();
});

这个方法运行良好,但是当点击列表项时会出现问题,因为模糊事件会在单击被注册之前触发并隐藏列表。目标是使列表在点击列表的任何部分时保持可见,即使这将导致输入模糊。

我该如何做到这一点?谢谢!

5个回答

18

你可以通过保留全局变量并使用setTimeout函数,等待200毫秒后检查这两个元素是否有焦点,来实现此操作。

var keepFocus = false;

function hideList(){
    if(!keepFocus){
        $('#myList').hide();
    }
}

$('#myInput').blur(function() {
    keepFocus = false;
    window.setTimeout(hideList, 200);
}).focus(function(){
    keepFocus = true;
});


$('#myList').blur(function() {
    keepFocus = false;
    window.setTimeout(hideList, 200);
}).focus(function(){
    keepFocus = true;
});

模糊和聚焦似乎无法与#myList一起使用,因为它不是一个输入。 - Justin Stayton
@Justin,这是什么?尝试在列表上删除focus方法,并将blur代码放置在您的列表click事件中。 - The Scrum Meister
这只是一个 div。不过我已经根据我的设置调整了你的代码,所以谢谢! - Justin Stayton
2
你不想用 keepFocus 变量污染全局命名空间。如果点击事件在 200 毫秒内没有触发,依赖 setTimeout 函数也是一个坏主意。 - Adam
由于它们都使用相同的函数,您可以使用变量并绑定它们,还可以利用“命名空间”来解决@Adam的问题:var retainFocus = { hide: fn()..., keep: false, onblur: function() { retainFocus.keep = false; setTimeout(retainFocus.hide, 200); }, onfocus: function() { retainFocus.keep = true; } },然后 $.each([$myList, $myInput], function(i,o) { o.blur(retainFocus.onblur).focus(retainFocus.onfocus); }); $myInput.blur - drzaus

16

我也面对过完全相同的问题,以下是我的解决方法。

我发现 blur() 的触发时间比 click() 早。

所以我尝试将 click() 更改为 mousedown(),并发现 mousedown()blur() 之前被触发。

要模拟 click(),你需要先触发 mousedown(),然后再触发 mouseup()

因此,在您的情况下,我会像这样做:

var click_in_process = false; // global

$('#myList').mousedown(function() {
    click_in_process = true;
});

$('#myList').mouseup(function() {
    click_in_process = false;
    $('#myInput').focus();

    // a code of $('#myList') clicking event

});

$('#myInput').blur(function() {
    if(!click_in_process) {
        $('#myList').hide();

        // a code of what you want to happen after you really left  $('#myInput')

    }
});

演示/示例: http://jsfiddle.net/bbrh4/

希望这能有所帮助!


1
如果在特定的浏览器或设备中事件的顺序不同怎么办?如果我没记错的话,这个顺序是没有定义的。 - Gherman
2
@German 在我当时使用的所有设备和浏览器上都能正常工作(我记得有Chrome/Firefox/Opera/IE/iOS/Android/WP)。也许现在有些变化了,但说实话我不太相信。但如果你有任何具体的例子,请告诉我,我会非常高兴知道。如果确实如此,我建议编写一个服务/助手/库,在其中可以根据当前浏览器或设备执行不同的逻辑。 - Pigalev Pavel
1
我没有任何不好的例子,只是特别谨慎。理论上,事件的顺序在标准中没有定义。 - Gherman
1
@German 你停止与一个对象的交互,然后开始与另一个对象的交互。因此,如果在一个对象上的blur()不会在另一个对象上的click()之前自动触发,那将非常奇怪。我怀疑这应该是不需要记录的。 - Pigalev Pavel
奇怪的事情总是时常发生。 - Gherman

11
你需要能够说“在列表获得焦点之前执行模糊(blur())操作”。
这个问题说明了如何检测元素是否获得焦点:使用jQuery测试输入框是否有焦点
然后你只需要这样做:
$("#myInput").blur(function () {
    if (!$("#myList").is(":focus")) {
        $("#myList").hide();
    }
});

4
似乎不起作用。document.activeElement显示body标签为“焦点”元素。我猜这不起作用是因为“焦点”的概念与输入字段固有地相关联。 - Justin Stayton

6
Pigalev Pavel的回答非常好。
然而,如果您想要更简单的解决方案,您可以在元素的“mousedown”中“prevent default”,以防止模糊事件发生。(因为阻止默认实际上意味着最终输入根本没有失去焦点!)
当然,这只是在您可以接受在div中阻止默认情况下。它确实有一些副作用,比如文本不再可选择。只要这不是问题,这将起作用。
我想,如果您按住鼠标在div上,将鼠标移出div,然后释放鼠标,它也不会触发“blur”事件。但在我的情况下,我也不太担心这个问题,因为点击始于目标div。

$("input").focus(function(){
 $(this).val("");
});

$("input").blur(function(){
 $(this).val("blur event fired!");
});

$("div").mousedown(function(e){
 e.preventDefault();
})
div{
  height: 200px;
  width: 200px;
  background: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input>
<div>
Click here to prevent blur event!
</div>


1
最好的方法是将事件处理程序附加到body元素,然后再将另一个处理程序附加到列表以停止事件传播:
$(body).click(function () {
    $("#myList").hide();
});

$("#myList").click(function (e) {
    e.stopImmediatePropagation();
});

这个函数监听 #myInput 外部的点击事件,并隐藏 #myList。同时,第二个函数监听 #myList 的点击事件,如果发生了点击,则阻止 hide() 函数触发。


4
忽略键盘。当字段被按Tab键切换出去时,模糊效果会触发。 - Jo P

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