@@ -184,7 +184,6 @@ export class BlockListBlock extends Component {
184
184
// Remove and cancel deselect focus handlers
185
185
document . removeEventListener ( 'focus' , this . triggerDeselect , true ) ;
186
186
document . removeEventListener ( 'deselect' , this . debouncedDeselect ) ;
187
- this . wrapperNode . removeEventListener ( 'focusin' , this . cancelDeselect ) ;
188
187
this . debouncedDeselect . cancel ( ) ;
189
188
}
190
189
@@ -216,12 +215,6 @@ export class BlockListBlock extends Component {
216
215
const bindFn = isListening ? 'addEventListener' : 'removeEventListener' ;
217
216
document [ bindFn ] ( 'focus' , this . triggerDeselect , true ) ;
218
217
document [ bindFn ] ( 'deselect' , this . debouncedDeselect ) ;
219
-
220
- // Bind to the DOM reference of the rendered wrapper node, since React
221
- // synthetic event binding and focus normalization conflicts with our
222
- // own document event binding and therefore canceling occurs before the
223
- // deselect event is triggered (out of expected order).
224
- this . wrapperNode [ bindFn ] ( 'focusin' , this . cancelDeselect ) ;
225
218
}
226
219
227
220
setAttributes ( attributes ) {
@@ -258,9 +251,14 @@ export class BlockListBlock extends Component {
258
251
*
259
252
* @see https://developer.mozilla.org/en-US/docs/Web/Events/focus
260
253
*
261
- * @param {FocusEvent } event Focus event
254
+ * @param {FocusEvent } event Focus event
262
255
*/
263
256
triggerDeselect ( event ) {
257
+ // Only deselect when focusing outside current node
258
+ if ( this . wrapperNode . contains ( event . target ) ) {
259
+ return ;
260
+ }
261
+
264
262
const deselectEvent = new window . Event ( 'deselect' , {
265
263
bubbles : true ,
266
264
cancelable : true ,
@@ -271,10 +269,20 @@ export class BlockListBlock extends Component {
271
269
272
270
/**
273
271
* Cancels the debounced deselect. Debouncing allows focus within the block
274
- * wrapper to prevent deselect from occurring.
272
+ * wrapper to prevent deselect from occurring. Stops propagation to work
273
+ * around unreliable ordering of document-level event handlers.
274
+ *
275
+ * @param {Event } event Focus event
275
276
*/
276
- cancelDeselect ( ) {
277
+ cancelDeselect ( event ) {
277
278
this . debouncedDeselect . cancel ( ) ;
279
+
280
+ // Stop propagation for synthetic events, necessary because the order
281
+ // of the document-level focus deselect handler occurs after React's
282
+ // internal event hub's capture.
283
+ //
284
+ // See: https://github.com/facebook/react/issues/285
285
+ event . nativeEvent . stopImmediatePropagation ( ) ;
278
286
}
279
287
280
288
/**
@@ -471,6 +479,7 @@ export class BlockListBlock extends Component {
471
479
data-type = { block . name }
472
480
onTouchStart = { this . onTouchStart }
473
481
onClick = { this . onClick }
482
+ onFocus = { this . cancelDeselect }
474
483
{ ...wrapperProps }
475
484
>
476
485
< BlockDropZone index = { order } />
0 commit comments