如何在点击事件导致失焦时触发JavaScript的失焦事件?

33

我遇到过这个问题几次,之前用过的解决方案让我不太满意。

我有一个输入框,带有模糊事件来验证其内容,并且有一个按钮,点击该按钮会填充输入框。问题是,点击按钮会触发输入框的失焦事件和按钮的点击事件,因此由按钮插入的内容不是被验证的内容。

请参见http://jsfiddle.net/jakecr/dL3K3/

我知道这是正确的行为,但我想不到一个干净的方法来解决这个问题。


你的问题不清楚?你的问题是什么,期望的解决方案是什么? - Chinmayee G
基本上,一个用户操作会引发两个事件,这两个事件按顺序触发,但第二个事件的动作依赖于第一个事件的动作。我考虑使用setTimeout来延迟第一个事件,但这似乎不是一个理想的解决方案。 - Jake
我以相当一般的形式提出了这个问题,因为在不同的情况下我已经遇到了这个问题几次。 - Jake
无论你做什么,如果焦点从一个元素转移到另一个元素,第一个元素的“blur”事件将在第二个元素的“focus”或“click”之前被触发。也许你应该使用“change”事件?这样它可能会在按钮的“click”之前进行评估,然后再次进行评估?(未经测试) - clarkf
6个回答

46

在工作中我们遇到了类似的问题。最后我们发现,mousedown事件会在blur事件之前触发,这样你就可以在验证之前设置内容,甚至可以使用标志完全取消验证过程。

查看我使用你的示例创建的这个fiddle - http://jsfiddle.net/dL3K3/31/

$(function(){
    var flag = true;
    $('input').blur(function(){
        if(!$(this).val() && flag){
            alert("Grrr, It's empty");
        }
    });

    $('button').mousedown(function(){
        flag = false;
    });    
    $('button').mouseup(function(){
        flag = true;
        $('input').val('content').focus();
    });

});

-编辑-

正如@mclin建议的那样,使用mousedown事件和preventDefault将防止失焦行为。

而且你可以保留原来的click事件不变 -

http://jsfiddle.net/dL3K3/364/

$(function(){
    $('input').blur(function(){
        if(!$(this).val()){
            alert("Grrr, It's empty");
        }
    });

    $('button').mousedown(function(e){
        e.preventDefault();
    });    
    $('button').click(function(){
        $('input').val('content');
    });

});

这个解决方案可能会更加清晰,适用于大多数情况,但请注意如果使用它,您将会阻止当前焦点元素的preventDefault和blur事件,但对于大多数用例来说,这不应该是一个问题。


mousedown事件可能会导致提交,而不考虑验证。请参见此处:http://stackoverflow.com/questions/43011387/mousedown-still-submitting-form-when-it-should-not - DragonFire
@DragonFire 这与此问题或mouswdown无关。您的代码正在异步运行,而您正在将其视为同步代码。 - Yoni Jah
非常好的解决方案,鼠标按下事件已经完成了这项工作。基本上,我想在失焦事件之前做一些事情,所以鼠标按下适合我。谢谢。 - Muneer Khan

26

我有比Yoni Jah更好的解决方案:

在按钮的鼠标按下事件上调用preventDefault。因此onBlur事件永远不会发生,所以你可以在点击事件中自由地做任何事情。

这样做更好,因为它不会通过在鼠标弹起而不是在鼠标按下时执行操作来改变单击行为,这可能会让人感到意外。


这是最好的解决方案。正是我在寻找的。 - Tamzin Blake

9
这可能也会有帮助:
设置一个定时器,在模糊事件触发按钮点击事件之前延迟执行。
// Namespace for timer var setTimer = { timer: null }

    $('input,select').blur(function () {
        setTimer.timer = setTimeout(function () {
           functionCall();
        }, 1000);
    });

    $('input,select').focus(function () {
        setTimer.timer = setTimeout(function () {
            functionCall();
        }, 1000);
    });

    $("#btn").click(function () {
        clearTimeout(setTimer.timer);
        functionCall();
    });

这是一个聪明的解决方案。 - Teoman shipahi

2
将输入验证逻辑分离成自己的函数,在模糊事件自动调用之后,通过单击事件在修改输入框中的值后调用。 这样做虽然会让你的验证逻辑被调用两次,但我没有看到其他更好的解决方法,除非进行更多的更改。
使用jQuery的示例:
$('input').blur(function() {
    validate(this);
});

$('submit').click(function() {
   //modify the input element
   validate(inputElement);
});

var validate = function(obj) {
   // validate the object
}

并不是我想要的解决方案,因为模糊事件仍然会被触发,但我认为无法完全做到我想要的。 - Jake

0
有时候在鼠标抬起事件中获取子对象很有用,但是父级希望在失焦时隐藏其子元素。我们可以取消隐藏子元素的操作,等待鼠标抬起事件发生后再强制触发失焦事件。 js
var cancelHide = false;

$('.my-input').blur(function() {
    if (!cancelHide) {
        $('.my-item').hide();
    }
});

$('.my-input').click(function() {
    $('.my-item').show();
});

$('.my-item').mousedown(function() {
    cancelHide = true;
});

$('.my-item').mouseup(function() {
    var text = $(this).text();
    alert(text);
    cancelHide = false;
    $('.my-input').blur();
});

html

<input type="text" class="my-input"/>
<div class="my-item">Hello</div>
<div class="my-item">Lovely</div>
<div class="my-item">World</div>

CSS


.my-item {
    display:none;
}

https://jsfiddle.net/tic84/on421rxg/

单击列表项仅会在鼠标弹起时发出警报,尽管父元素试图在模糊时隐藏它们


-1

诀窍在于不要使用模糊和点击事件,因为它们总是按照模糊先,点击后的顺序执行。相反,您应该检查具有焦点的元素何时发生了更改,因为这只会在单击后发生。

我认为我对此有一个优雅的解决方案:

var alreadyValidated = 'true';

setInterval(function(){
    if(document.activeElement.id != 'validation-field-id'){
        if(alreadyValidated=='false'){
            alreadyValidated = 'true';
            validate(document.activeElement);       
        }
    }
    else {
        alreadyValidated = 'false';
    }
}, 200);

现今的现代浏览器都支持document.activeElement。


2
你应该使用 truefalse 而不是字符串,因为 Boolean('false') === true - Jake
7
“优雅的解决方案”和setInterval不应在同一篇文章中提及。 - aaronsnoswell

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