Blocks V3 inline text editing: the caret jumps to the start of the field after the preview re-renders
Maintainers usually reply within 1 day
Nobody has claimed this yet.
- #575 by @wojtekpiskorz — closed without merging
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 52/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- javascript, php, react, wordpress
- Domain
- frontend, full-stack
Research direction
Start with assets/src/js/pro/blocks-v3/components/block-edit.js and jsx-parser.js, then trace how the fetched preview is rendered through acf.parseJSX(). The reproduction and selection offsets show the caret resets after React commits the updated text. Confirm the fix by repeating the Playwright steps and checking that typing after the preview fetch preserves the caret and selection.
Written by the indexing model from the issue text.
Description
Environment
- Secure Custom Fields 6.9.7, and
trunkat 31ead85 - WordPress 7.1.3 (wp-env), React 18.3.1 (the copy WordPress ships)
- Chromium (Playwright 1.56). The cause is in the DOM spec, so other browsers should behave the same. Only Chromium was tested.
Steps to reproduce
-
Register a Blocks V3 block whose template puts
acf_inline_text_editing_attrs()on an element. Minimal block (the field group is onetextfield namedtitle, location "Block is equal to acf/caret-repro"):block.json{ "apiVersion": 3, "name": "acf/caret-repro", "title": "Caret repro", "category": "text", "acf": { "blockVersion": 3, "mode": "preview", "renderTemplate": "render.php" } }render.php<h2 <?php echo acf_inline_text_editing_attrs( 'title' ); ?>><?php echo esc_html( get_field( 'title' ) ); ?></h2> -
Add the block to a post and give the title a value, e.g.
Accessible. -
Click into the title in the editor canvas, press End and type
AAA. -
Wait about 1 s, until the block preview has been fetched again (
acf/ajax/fetch-block). -
Type
BBB.
Expected
The title reads AccessibleAAABBB. The caret stays where you are typing.
Actual
The title reads BBBAccessibleAAA. When the new preview renders, the caret moves to offset 0 of the element, so the next keystrokes land at the start. This happens after every preview re-render: typing in the middle of the text, in a second inline field of the same block, or after emptying the field. A selected range is lost the same way.
Output of a Playwright script that does steps 3 to 5 and reads getSelection() in the canvas:
before "Accessible" { offset: 10 }
typed "AccessibleAAA" { offset: 13 }
after preview "AccessibleAAA" { offset: 0 }
final "BBBAccessibleAAA" { offset: 0 }
Cause
The browser edits the text of the contentEditable element directly, so React's virtual DOM still holds the text of the previous preview. Typing updates the field (the MutationObserver in BlockEditInner calls updateFieldValueFromContentEditable(), assets/src/js/pro/blocks-v3/components/block-edit.js lines 984 and 1025). After the 200 ms debounce (line 622), the block fetches the preview again, and setBlockPreviewHtml() (line 372) renders it through acf.parseJSX() (line 1496). The parser gives the element its text as a plain string child (jsx-parser.js lines 314 and 372 to 375).
The new string (AccessibleAAA) differs from the string React rendered last time (Accessible). So when React commits, it writes it into the text node, even though the DOM already shows that text. Setting nodeValue runs the DOM's "replace data" algorithm over the whole node, and that moves every selection boundary inside the node to offset 0.
Tracing the nodeValue setter in the canvas shows this is the only write, and it comes from React's commit:
Node.nodeValue = "AccessibleAAA" caret 13 -> 0
at Fc (wp-includes/js/dist/vendor/react-dom.min.js?ver=18.3.1.1:220:407)
at Fi (wp-includes/js/dist/vendor/react-dom.min.js?ver=18.3.1.1:173:141)
at Aa (wp-includes/js/dist/vendor/react-dom.min.js?ver=18.3.1.1:171:382)
...
The Blocks V3 sources say they are based on ACF PRO 6.7.0.2. The ACF changelog up to 6.8.10 lists no fix for this, so ACF PRO is probably affected too. That has not been checked against ACF PRO itself.
- Dominant language
- PHP
- Stars
- 136
- Forks
- 73
- Avg merge
- 18h 21m
- Merged PRs (30d)
- 21
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- No contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from WordPress/secure-custom-fields
-
Difficulty 1/5 Under an hour Newbie friendliness 86/100
WordPress/secure-custom-fields#589 ·
Maintainers usually reply within 1 day
-
Port ACF's schema select2 dropdown JS so GEO pickers get the styled dropdownMay be free again A pull request for this issue was closed without being merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
WordPress/secure-custom-fields#507 ·
Maintainers usually reply within 1 day
-
JS: _acf-compatibility.js depends on sloppy-mode semantics (undeclared global, arguments aliasing)May be free again A pull request for this issue was closed without being merged. Open[Type] Code Quality
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
WordPress/secure-custom-fields#460 ·
Maintainers usually reply within 1 day
-
Blocks: undo and redo don't work for SCF block fields, and get stuck at SCF blocksMay be free again A pull request for this issue was closed without being merged. Open
Difficulty 5/5 Over a week Newbie friendliness 35/100
WordPress/secure-custom-fields#577 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 1-2 days Newbie friendliness 52/100
WordPress/secure-custom-fields#576 ·
Maintainers usually reply within 1 day
All issues in WordPress/secure-custom-fields
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
getgrav/grav-plugin-sitemap#125 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
nikic/PHP-Parser#1184 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
api-platform/core#8835 ·
Maintainers usually reply within 2 days
-
14.3.x
Difficulty 1/5 1-3 hours Newbie friendliness 82/100