Phenomenon
A known bug in WebKit-based browsers (Safari, Chrome) prevents focus from transferring correctly from contenteditable elements to non-editable elements. When attempting to programmatically move focus from a contenteditable element to another element, the focus may not transfer properly, causing the contenteditable element to continue accepting input.
Reproduction Steps
- Open Safari browser on macOS.
- Create a
contenteditableelement and another element (e.g., input field, button). - Focus the
contenteditableelement and type some text. - Programmatically attempt to transfer focus to the other element (e.g., by clicking a button that calls
targetElement.focus()). - Try typing after the focus transfer.
- Observe where the input goes.
Observed Behavior
- Focus Transfer Failure: Focus does not properly transfer from the
contenteditableelement. - Continued Input: The
contenteditableelement continues to accept input even after attempting to blur it. - Input Interception: User input goes to the
contenteditableelement instead of the intended target. - Blur Not Effective: Calling
blur()on thecontenteditableelement does not reliably remove focus.
Expected Behavior
- Focus should transfer correctly from
contenteditableto other elements. - The
contenteditableelement should stop accepting input after losing focus. - User input should go to the newly focused element.
Impact
- Focus Management Failure: Cannot reliably transfer focus away from contenteditable elements.
- Input Interception: User input continues to go to contenteditable even after blur.
- UI Confusion: Users may type in wrong locations.
- Accessibility Issues: Screen readers and keyboard navigation may malfunction.
Browser Comparison
- Safari (WebKit): This issue occurs.
- Chrome (Blink/WebKit-based): May have similar issues.
- Firefox (Gecko): Not affected.
- Edge (Chromium): May have similar issues.
Notes and Possible Workarounds
Hidden Input Field Workaround
// Create a hidden input field for focus management
const hiddenInput = document.createElement('input');
hiddenInput.type = 'text';
hiddenInput.style.position = 'absolute';
hiddenInput.style.left = '-9999px';
hiddenInput.style.opacity = '0';
hiddenInput.style.pointerEvents = 'none';
document.body.appendChild(hiddenInput);
function transferFocusFromContentEditable(targetElement) {
const editor = document.querySelector('[contenteditable]');
// First, blur the contenteditable
editor.blur();
// Use hidden input as intermediate step
hiddenInput.focus();
// Then transfer to target
setTimeout(() => {
targetElement.focus();
}, 0);
}
Force Blur with Selection Clear
function forceBlurContentEditable(editor, targetElement) {
// Clear selection
const selection = window.getSelection();
selection.removeAllRanges();
// Blur the element
editor.blur();
// Force focus on body to ensure contenteditable loses focus
document.body.focus();
// Then focus target element
setTimeout(() => {
targetElement.focus();
}, 10);
}
References
- GitHub Gist: WebKit contenteditable focus bug workaround
- Known WebKit bug affecting focus transfer from contenteditable elements