Phenomenon
In Chrome and Safari, calling focus() on a contenteditable div can select the entire content instead of placing the cursor at the beginning, as observed in Firefox and IE.
Reproduction example
- Open Chrome or Safari browser on macOS.
- Create a
contenteditableelement with some text content. - Programmatically call
focus()on the element (e.g.,element.focus()). - Observe that all text is selected instead of the cursor being placed at the beginning.
Observed behavior
- Calling
focus()selects all content - Cursor is not placed at the beginning
- This differs from Firefox and IE behavior
- User must manually deselect to start typing
Expected behavior
- Calling
focus()should place cursor at the beginning - Content should not be selected
- Behavior should be consistent with Firefox and IE
Impact
- User confusion: Users may accidentally overwrite content
- Poor UX: Users must manually deselect before typing
- Inconsistent behavior: Different from other browsers
Browser Comparison
- Chrome: All content is selected (this issue)
- Safari: All content is selected (this issue)
- Firefox: Cursor is placed at beginning
- Edge: Cursor is placed at beginning
Notes and possible direction for workarounds
- Manual selection: After focus(), manually set cursor position using Selection API
- Range manipulation: Create a range at the beginning and set selection
- Event handling: Handle focus events to reset selection
Code example
const editor = document.querySelector('div[contenteditable]');
function focusEditor() {
editor.focus();
// Workaround: Set cursor at beginning
const selection = window.getSelection();
const range = document.createRange();
// Find first text node or element
const firstNode = getFirstTextNode(editor);
if (firstNode) {
range.setStart(firstNode, 0);
range.setEnd(firstNode, 0);
} else {
range.selectNodeContents(editor);
range.collapse(true);
}
selection.removeAllRanges();
selection.addRange(range);
}
function getFirstTextNode(node) {
if (node.nodeType === Node.TEXT_NODE) {
return node;
}
for (let child of node.childNodes) {
const textNode = getFirstTextNode(child);
if (textNode) {
return textNode;
}
}
return null;
}
// Use focusEditor() instead of editor.focus()
editor.addEventListener('click', () => {
focusEditor();
});