0
Fork 0
mirror of https://github.com/fastmail/Squire.git synced 2024-12-22 15:23:29 -05:00

Enforce sane focus/blur events.

Because focus/blur events are fired synchonously, browsers can get confused if
UI code starts focusing other elements while inside a focus/blur handler, and
end up firing events in the wrong order which can cause infinite loops.

This change ensures we only get a focus/blur event when they really are
happening, and you always get one then the other.
This commit is contained in:
Neil Jenkins 2016-06-10 11:02:24 +10:00
parent 84ae8a05f5
commit f593d8ac04
4 changed files with 46 additions and 8 deletions

View file

@ -1300,7 +1300,7 @@ var afterDelete = function ( self, range ) {
node = parent;
parent = node.parentNode;
}
// If focussed in empty inline element
// If focused in empty inline element
if ( node !== parent ) {
// Move focus to just before empty inline(s)
range.setStart( parent,
@ -2310,6 +2310,7 @@ function Squire ( root, config ) {
this._events = {};
this._isFocused = false;
this._lastSelection = null;
// IE loses selection state of iframe on blur, so make sure we
@ -2502,8 +2503,26 @@ var customEvents = {
};
proto.fireEvent = function ( type, event ) {
var handlers = this._events[ type ],
l, obj;
var handlers = this._events[ type ];
var isFocused, l, obj;
// UI code, especially modal views, may be monitoring for focus events and
// immediately removing focus. In certain conditions, this can cause the
// focus event to fire after the blur event, which can cause an infinite
// loop. So we detect whether we're actually focused/blurred before firing.
if ( /^(?:focus|blur)/.test( type ) ) {
isFocused = isOrContains( this._root, this._doc.activeElement );
if ( type === 'focus' ) {
if ( !isFocused || this._isFocused ) {
return this;
}
this._isFocused = true;
} else {
if ( isFocused || !this._isFocused ) {
return this;
}
this._isFocused = false;
}
}
if ( handlers ) {
if ( !event ) {
event = {};

File diff suppressed because one or more lines are too long

View file

@ -42,6 +42,7 @@ function Squire ( root, config ) {
this._events = {};
this._isFocused = false;
this._lastSelection = null;
// IE loses selection state of iframe on blur, so make sure we
@ -234,8 +235,26 @@ var customEvents = {
};
proto.fireEvent = function ( type, event ) {
var handlers = this._events[ type ],
l, obj;
var handlers = this._events[ type ];
var isFocused, l, obj;
// UI code, especially modal views, may be monitoring for focus events and
// immediately removing focus. In certain conditions, this can cause the
// focus event to fire after the blur event, which can cause an infinite
// loop. So we detect whether we're actually focused/blurred before firing.
if ( /^(?:focus|blur)/.test( type ) ) {
isFocused = isOrContains( this._root, this._doc.activeElement );
if ( type === 'focus' ) {
if ( !isFocused || this._isFocused ) {
return this;
}
this._isFocused = true;
} else {
if ( isFocused || !this._isFocused ) {
return this;
}
this._isFocused = false;
}
}
if ( handlers ) {
if ( !event ) {
event = {};

View file

@ -110,7 +110,7 @@ var afterDelete = function ( self, range ) {
node = parent;
parent = node.parentNode;
}
// If focussed in empty inline element
// If focused in empty inline element
if ( node !== parent ) {
// Move focus to just before empty inline(s)
range.setStart( parent,