简体   繁体   中英

Chrome (maybe Safari?) fires “blur” twice on input fields when browser loses focus

Here is an interesting jsfiddle.

In Firefox:

  1. Run the fiddle
  2. Click in text input
  3. Click somewhere else. Should say "1 blurs".
  4. Click in the text input again.
  5. ALT - TAB to another window. Fiddle should now say "2 blurs".

In Chrome, at step 5, it says "3 blurs". Two separate "blur" events are fired when the whole browser loses focus. This is of interest because it means that it's not safe to assume, in a "blur" handler, that the element actually had focus just before the event was dispatched; that is, that the loss of focus — the transition from "being in focus" to "not being in focus" — is the reason for the event. When two "blur" events are generated, that condition is not satisfied during the handling of the second event, as the element is already not in focus.

So is this just a bug? Is there a way to tell that a "blur" event is bogus?

The reason it is firing twice is because of window.onblur. The window blurring triggers a blur event on all elements in that window as part of the way javascript's capturing/bubbling process. All you need to do is test the event target for being the window.

var blurCount = 0;
var isTargetWindow = false;
$(window).blur(function(e){
    console.log(e.target);
    isTargetWindow = true;
});
$(window).focus(function(){
    isTargetWindow = false;
});
$('input').blur(function(e) {
    if(!isTargetWindow){         
       $('div').text(++blurCount + ' blurs');
    }
    console.log(e.target);
});

http://jsfiddle.net/pDYsM/4/

This is confirmed Chrome bug. See the Chromium Issue Tracker

The workaround is in the accepted answer.

Skip 2nd blur:

var secondBlur = false;
this.onblur = function(){
    if(secondBlur)return;
    secondBlur = true;
    //do whatever
}
this.onfocus = function(){
    secondBlur = false;    
    //do whatever
}

我在 Windows 7 上使用 Chrome 版本 30.0.1599.101 m,这个问题似乎已得到修复。

I am experiencing the same and the above posts make sense as to why. In my case I just wanted to know if at least one blur event had occurred. As a result I found that just returning from my blur function solved my issue and prevented the subsequent event from firing.

   function handleEditGroup(id) {
        var groupLabelObject = $('#' + id);
        var originalText = groupLabelObject.text();

        groupLabelObject.attr('contenteditable', true)
            .focus().blur(function () {
                $(this).removeAttr('contenteditable');
                $(this).text($(this).text().substr(0, 60));

                if ($(this).text() != originalText) {
                    alert("Change Found");
                    return; //<--- Added this Return.
                }
            });
    }

Looks like an oddity of angularjs gives a simpler solution when using ng-blur; the $event object is only defined if you pass it in:

ng-blur="onBlur($event)"

so (if you aren't using ng-blur on the window) you can check for:

$scope.onBlur = function( $event ) {
    if (event != undefined) {
       //this is the blur on the element
    }
}

This probably isn't what you want to hear, but the only way to do it seems to be to manually track whether the element is focused or not. For example ( fiddle here ):

var blurCount = 0;
document.getElementsByTagName('input')[0].onblur = function(e) {
    if (!e) e = window.event;
    console.log('blur', e);
    if (!(e.target || e.srcElement)['data-focused']) return;
    (e.target || e.srcElement)['data-focused'] = false;
    document.getElementsByTagName('div')[0].innerHTML = (++blurCount + ' blurs');
};
document.getElementsByTagName('input')[0].onfocus = function(e) {
    if (!e) e = window.event;
    console.log('focus', e);
    (e.target || e.srcElement)['data-focused'] = true;
};

Interestingly, I couldn't get this to work in jQuery ( fiddle here ) ... I really don't use jQuery much, maybe I'm doing something wrong here?

var blurCount = 0;
$('input').blur(function(e) {
    console.log('blur', e);
    if (!(e.target || e.srcElement)['data-focused']) return;
    (e.target || e.srcElement)['data-focused'] = false;
    $('div').innerHTML = (++blurCount + ' blurs');
});
$('input').focus(function(e) {
    console.log('focus', e);
    (e.target || e.srcElement)['data-focused'] = true;
});

You could also try comparing the event's target with document.activeElement . This example will ignore the alt+tab blur events, and the blur events resulting from clicking on Chrome's... chrome. This could be useful depending on the situation. If the user alt+tabs back into Chrome, it's as if the box never lost focus ( fiddle ).

var blurCount = 0;
document.getElementsByTagName('input')[0].onblur = function(e) {
    if (!e) e = window.event;
    console.log('blur', e, document.activeElement, (e.target || e.srcElement));
    if ((e.target || e.srcElement) == document.activeElement) return;
    document.getElementsByTagName('div')[0].innerHTML = (++blurCount + ' blurs');
};​
​

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM