Gutenberg: Don't limit block initial focus to input fields

Created on 6 Dec 2018  路  5Comments  路  Source: WordPress/gutenberg

When inserting a block, if the block has input fields then the initial focus is set by default on the first input field, otherwise it's set on the block wrapper.

See focusTabbable() in BlockListBlock:

https://github.com/WordPress/gutenberg/blob/387bb77b0f21c73399f71445f85ad9db91750d7f/packages/editor/src/components/block-list/block.js#L141-L158

Note: "input fields" in this context are only:

  • <input> elements that support the text selection API
  • <textarea> elements
  • elements with contenteditable

See isTextField()

https://github.com/WordPress/gutenberg/blob/387bb77b0f21c73399f71445f85ad9db91750d7f/packages/dom/src/dom.js#L435-L455

However, the current implementation assumes the first tabbable element is always an "input field". This is true for the Gutenberg default blocks but it's not guaranteed to be true for custom blocks.

For example, what if the first tabbable is a button element?

screenshot 2018-12-06 at 15 39 58

Initial focus is set on the "input field" after the button, thus skipping an important part of the UI.

Same if the first tabbable element is, for example, one of:

  • a select element
  • a checkbox element
  • a radio button element
  • etc.: any tabbable element that is not an "input field" as defined by isTextField()

In all these cases, initial focus will be set on the wrong element.

I'd suggest to consider to remove the filtering by isTextField() to start with. I don't see a reason for it to be there, unless I'm missing something.

Also to consider: a way to give developers more control on initial focus. This could certainly be addressed separately.

Accessibility (a11y) [Feature] Writing Flow [Type] Bug

Most helpful comment

Would be great if that could be improved.

I have the situation that I use multiple TextControl fields in a custom block, followed by a InnerBlocks component that contains three blocks that contain InnerBlocks again. On inserting the block, the focus jumps to the first block in the last of the three inner block blocks, leading to the situation that the user needs to scroll up to see the top of the block (it is an address block that should give the user the option to add multiple website URLs, email addresses and phone numbers).

All 5 comments

In short: removing the line .filter( isTextField ) would be nice 馃檪

Tested a bit with nested blocks and seems to me things are not so simple.

When there are inner blocks, the logic should be adjusted, as right now inner blocks are:

  1. taken into account (and focused) when the block mounts
  2. ignored when the block updates

Imagine a block based on inner blocks. The block adds by default two editable fields, for example:

screenshot 2019-01-16 at 17 43 15

Because of 1, removing .filter( isTextField ) will focus the inner blocks container, which is the first focusable thing within the block. Instead, we'd want the first focusable form control / contenteditable to be focused.

Also, when adding a _second_ Q/A pair, I'm seeing focus goes to the answer. Expected: focus to go to the _first_ form field / editable i.e. the question.

For the inner blocks, see the original change in #10545

Fix #9212 which is causing the inner blocks to be selected when we select the parent.

Looking a bit more into this, seems the case of nested block is not so simple.

Basically, when inserting a block that initially inerts also some nested blocks, focusTabbable() runs when both the parent block and the child block mount.

This leads to unexpected results that actually depend on which elements are initially inside the parent and the child. For example, when inserting a block that has some default inner blocks:

Example 1:

  • parent with no input fields
  • child with 2 input fields
  • the first input of the child gets initial focus: this looks right because it's the first tabbable displayed in the blocks

Example 2:

  • parent with 1 input field
  • child with 2 input fields
  • the first input of the child gets initial focus: this looks wrong: the input of the parent should get focus

Also, we'd need to remove .filter( isTextField ) to get all the tabbables but this would take into account also the entire UI of the children.

The desired behavior would be:

  • get the tabbables only after the parent and all the initial inner blocks have mounted
  • exclude all the blocks UI (appender button, movers, etc.)
  • get all the tabbables within the block, not limited to input fields
  • focus the first one
  • when adding _new nested blocks_ the parent should be ignored and focus should go to the first tabbable of the new nested block
  • as a general fallback, focus the last inserted block wrapper

Would be great if that could be improved.

I have the situation that I use multiple TextControl fields in a custom block, followed by a InnerBlocks component that contains three blocks that contain InnerBlocks again. On inserting the block, the focus jumps to the first block in the last of the three inner block blocks, leading to the situation that the user needs to scroll up to see the top of the block (it is an address block that should give the user the option to add multiple website URLs, email addresses and phone numbers).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wpalchemist picture wpalchemist  路  3Comments

aaronjorbin picture aaronjorbin  路  3Comments

mhenrylucero picture mhenrylucero  路  3Comments

BE-Webdesign picture BE-Webdesign  路  3Comments

hedgefield picture hedgefield  路  3Comments