diff --git a/packages/slate-react/src/plugins/before.js b/packages/slate-react/src/plugins/before.js
index 991ccf037..272fbe1cf 100644
--- a/packages/slate-react/src/plugins/before.js
+++ b/packages/slate-react/src/plugins/before.js
@@ -27,6 +27,7 @@ const debug = Debug('slate:before')
*/
function BeforePlugin() {
+ let activeElement = null
let compositionCount = 0
let isComposing = false
let isCopying = false
@@ -69,28 +70,34 @@ function BeforePlugin() {
if (editor.props.readOnly) return true
const { value } = change
- const focusTarget = event.relatedTarget
+ const { relatedTarget, target } = event
+ const window = getWindow(target)
- // focusTarget may be null when window itself being blurred
- // (eg. when changing tabs), or when user clicks on elements
- // without`tabindex` attribute.
- if (focusTarget) {
+ // COMPAT: If the current `activeElement` is still the previous one, this is
+ // due to the window being blurred when the tab itself becomes unfocused, so
+ // we want to abort early to allow to editor to stay focused when the tab
+ // becomes focused again.
+ if (activeElement == window.document.activeElement) return true
+
+ // COMPAT: The `relatedTarget` can be null when the new focus target is not
+ // a "focusable" element (eg. a `
` without `tabindex` set).
+ if (relatedTarget) {
const el = findDOMNode(editor)
- // The event should be ignored if the focus returns to the editor from an
- // embedded editable element (eg. an input element inside a void node).
- if (focusTarget == el) return true
- // when the focus moved from the editor to a void node spacer...
- if (focusTarget.hasAttribute('data-slate-spacer')) return true
+ // COMPAT: The event should be ignored if the focus is returning to the
+ // editor from an embedded editable element (eg. an element inside
+ // a void node).
+ if (relatedTarget == el) return true
- // or to an editable element inside the editor but not into a void node
- // (eg. a list item of the check list example).
- if (
- el.contains(focusTarget) &&
- !findNode(focusTarget, value).isVoid
- ) {
- return true
- }
+ // COMPAT: The event should be ignored if the focus is moving from the
+ // editor to inside a void node's spacer element.
+ if (relatedTarget.hasAttribute('data-slate-spacer')) return true
+
+ // COMPAT: The event should be ignored if the focus is moving to a non-
+ // editable section of an element that isn't a void node (eg. a list item
+ // of the check list example).
+ const node = findNode(relatedTarget, value)
+ if (el.contains(relatedTarget) && node && !node.isVoid) return true
}
debug('onBlur', { event })
@@ -323,6 +330,10 @@ function BeforePlugin() {
const el = findDOMNode(editor)
+ // Save the new `activeElement`.
+ const window = getWindow(event.target)
+ activeElement = window.document.activeElement
+
// COMPAT: If the editor has nested editable elements, the focus can go to
// those elements. In Firefox, this must be prevented because it results in
// issues with keyboard navigation. (2017/03/30)
@@ -407,6 +418,10 @@ function BeforePlugin() {
if (isComposing) return true
if (editor.props.readOnly) return true
+ // Save the new `activeElement`.
+ const window = getWindow(event.target)
+ activeElement = window.document.activeElement
+
debug('onSelect', { event })
}