捕获div失去焦点事件

3

我有一个主div(父元素),里面有一个输入框(子元素)和2个其他可点击的div(子元素)。

我想要捕获主div的焦点事件 - 不是在点击或聚焦输入框或其他2个可点击的div时。我使用jQuery设置了一个事件处理程序来捕获所有元素的focusin focusout事件。

当我点击输入框时,我看到的是:

  1. 首先聚焦在输入框上
  2. 然后聚焦在主div上。

如果我点击其他可点击的div:

  1. 首先触发输入框的focusout事件
  2. 然后是主div和其他div获得焦点,主div获得focusin事件。

我不希望在点击其他可点击的div时主div失去焦点。

我该如何实现呢?我想在失去焦点时验证输入,但不想在点击其他div时验证。

目前为止,这就是我的代码:Fiddle

HTML:

<div id="main">
    <input id="i" type="text" />
    <div id="Div2" class="icons" style="background-color:blue; right: 25px;" onclick="log('Clear Clicked');"></div>
    <div id="Div1" class="icons" style="background-color:red;" onclick="log('DD Clicked');"></div>
</div>

CSS :

* {
    -webkit-box-sizing: border-box;
    /* Safari/Chrome, other WebKit */
    -moz-box-sizing: border-box;
    /* Firefox, other Gecko */
    box-sizing: border-box;
    /* Opera/IE 8+ */
}
#main {
    top: 50px;
    left: 200px;
    position: absolute;
    border: 1px solid #00bfff;
    width: 250px;
    height: 27px;
}
input {
    border: none;
    width: 248px;
    height: 25px;
    position: absolute;
    z-index: 200;
}
.icons {
    text-align:center;
    border:1px solid blue;
    height: 23px;
    width: 23px;
    position: absolute;
    top: 2px;
    right: 2px;
    z-index: 99999999999999999999999;
    background-position: center;
    background-repeat: no-repeat;
}

jQuery :

$("#main").focusin(function () {
    log("Main div got focused");
});

$("#i").focusin(function () {
    log("input got focused");
});

$("#Div2").focusin(function () {
    log("dropdown div got focused");
});

$("#Div1").focusin(function () {
    log("clear div got focused");
});

$("#main").focusout(function () {
    log("Main div lost focused");
});

$("#i").focusout(function () {
    log("input lost focused");
});

$("#Div2").focusout(function () {
    log("dropdown div lost focused");
});

$("#Div1").focusout(function () {
    log("clear div lost focused");
});

function log(msg) {
    //window.console.log(msg);
    $("body").append("<p>" + msg + "</p>");
}

需要帮助和指导


1
据我理解,您想在主 div 失去焦点时验证此字段?为什么不在单击其他元素时创建验证函数呢? - Marko Vasic
#Div1#Div2有什么用?它们是用来作为某种按钮吗?还是用来传达验证状态的图标? - Jonathan Wilson
只有一个元素可以获得焦点。 - Mohammad Kermani
5个回答

21

这里有一个最好的解决办法,因为我之前也遇到了同样的问题 ^_^

事件中有一个属性:"relatedTarget"

relatedTarget 将提供此事件的下一个元素

所以,如果下一个元素不是您的框或您的框内的任何内容,请触发 focusout 事件。

但首先,您必须使您的 <div> 元素可聚焦,您需要像这样在 div 上添加 tabindex='-1'

<div id="main" tabindex='-1'>

这个脚本很短:

        $("#main, #main *").blur(function(e){
            if(!$(e.relatedTarget).is("#main, #main *")){
                //focus out.
            }
        });

当光标离开 #main 区域后,焦点将失去并进入其中,但此时您可以自由操作。

这与您的要求略有不同,但我猜这可能是您想要的。如果是这样,代码将非常干净。


根据我的经验,relatedTarget在所有最新的浏览器中都不起作用,即在IE10/11和Firefox和Chrome以及Safari(在Mac机器上)中。虽然在Firefox中可以找到目标元素-e.originalEvent.explicitOriginalTarget,在Chrome中可以找到-e.relatedTarget,在IE10/11中可以找到document.activeElement。只有当其他元素像输入、按钮等时,e.relatedTarget和e.originalEvent.explicitOriginalTarget才能起作用,对于divs则无效,而document.activeElement适用于大多数元素,无论是输入还是div。在Safari中以上都不起作用。 - Goutham ܢܢ
奇怪的是,当我点击 div 元素内部的 input 元素时,此解决方案会生成一个 focusout 事件! - Spartak Lalaj

1

基于这个被接受的答案 有没有一种方法可以在focusOut中使用event.preventDefault?如果没有,为什么?

$(document).on('mousedown', function (event) {
        target = event.target;
        main = document.getElementById("main");
        input = document.getElementById("i");

        if (target == input) return true;

        parent = event.target.parentNode;

        if (parent == main) {
            event.preventDefault();  //prevent default DOM action
            event.stopPropagation();   //stop bubbling
            return false;   // return
        }
});

1
好的,看起来你正在构建某种输入小部件。我看到 #main div 是小部件的外层容器,输入是用于输入文本的,然后你有两个其他的 div 作为按钮或其他功能。你想在用户尝试退出小部件时验证输入的值,并通过监听 #main 上的 focusout 来捕获此事件。这样做不会起作用,因为 div 不是可以获得焦点的元素。请参见 这里 的答案以获取更多信息。
我可以通过一个小实验向您证明您的 div 无法获得焦点:
如果您在输入框的 focusinfocusout 监听器中都放置 e.stopPropagation(),您将看到您的主 div 实际上从未聚焦或失去焦点;它只是从输入框冒泡上来的 focusinfocusup 事件。
因此,这意味着我们必须从另一个角度解决您的问题。
让我们通过一个简短的用户故事来描述小部件失去焦点的含义:
1. 用户单击输入框/#main/#Div1/#Div2 - 小部件获得焦点 2. 用户单击输入框(如果尚未单击)并键入一些文本 - 小部件不会失去焦点 3. 用户单击#main中的某个位置 - 小部件不会失去焦点 4. 用户单击#Div1,然后单击#Div2 - 小部件不会失去焦点 5. 用户单击页面上其他地方 - 小部件失去焦点 -> 进行验证
现在我们知道了哪些状态下的哪些事件应该导致运行验证。
首先,让我们使用布尔变量来跟踪小部件的“焦点”状态: var isFocused = false; 小部件开始处于未聚焦状态,并在任何地方单击#main或其子元素时或以某种方式聚焦输入框(可以通过使用键盘进行Tab切换)时变为聚焦状态:
$("#main").on('click',function(){
  isFocused = true;
});
$("#i").on('focus',function(){
  isFocused = true;
});

小部件失去焦点的唯一时机是:a) 当它获得焦点,并且 b) 用户在页面上的其他位置点击时。
$(document).on('click',function(){
  if(isFocused){
    isFocused = false;
    //kick-off the validation check!
  }
});

但是默认情况下,所有事件都会向上冒泡到DOM树,因此在#main内进行多次点击将会冒泡到document.body并触发验证检查。为了防止这种情况发生,在#main的点击处理程序中调用stopPropagation来停止点击事件的冒泡:

$("#main").on('click',function(e){
  isFocused = true;
  e.stopPropagation();
});

就是这样!

我希望我对你的需求理解正确。

这里有一个可运行的示例代码


那是一个不错的答案,但是这里有几个问题。在我的表单中,并不是所有的点击事件都会冒泡到文档(使用 knockout,其中 bubble 被设置为 false),而且假设您在小部件下面有另一个控件(textinput),如果从小部件到下一个控件发生 tabout,则验证不会触发,因为这不是一个点击事件。 - Goutham ܢܢ

0

这是一个简单的方法(就我所理解的而言)

$('#Div1').click(function(){
    log('Clear Clicked');
   //$('#main').focusin();
    $('#i').focus();
});
$('#Div2').click(function(){
   log('DD Clicked');
     //$('#main').focusin();
    $('#i').focus();
});

这里有fiddle


通过您的解决方案,Goutham 的输入验证会在不应该运行时运行。 - Jonathan Wilson

0
另一种解决方法是在运行focusout处理程序之前添加一个小的setTimeout。
您可以轻松地列出要排除的元素白名单,并在它们获得focusin时清除超时。

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