Phenomenon
In Firefox, when text is pasted into a contenteditable element, trailing whitespaces may be removed automatically. This behavior differs from Chrome, where trailing whitespaces are preserved.
Reproduction example
- Open Firefox browser on Windows.
- Copy text with trailing spaces (e.g., “Hello World ” with two trailing spaces).
- Focus a
contenteditableelement. - Paste the text (Ctrl+V).
- Observe that trailing spaces are removed.
Observed behavior
- Trailing whitespaces are automatically removed when pasting
- Multiple spaces are collapsed to single spaces in some cases
- This only occurs in Firefox
- Chrome preserves trailing whitespaces
Expected behavior
- Trailing whitespaces should be preserved when pasting
- Multiple spaces should be preserved
- Behavior should be consistent with other browsers
Impact
- Data loss: Important whitespace formatting is lost
- Code editing: Trailing spaces in code blocks are removed
- Formatting issues: Text alignment and spacing are affected
Browser Comparison
- Firefox: Trailing whitespaces are removed (this issue)
- Chrome: Trailing whitespaces are preserved
- Safari: Trailing whitespaces are preserved
- Edge: Trailing whitespaces are preserved
Notes and possible direction for workarounds
- CSS white-space property: Set
white-space: -moz-pre-spacein Firefox to preserve trailing whitespaces - Paste event handling: Intercept paste events and manually preserve whitespaces
- Normalize whitespace: Use
white-space: pre-wraporpreto preserve all whitespace
Code example
const editor = document.querySelector('div[contenteditable]');
// CSS workaround
editor.style.whiteSpace = '-moz-pre-space';
// JavaScript workaround: Intercept paste
editor.addEventListener('paste', (e) => {
e.preventDefault();
const clipboardData = e.clipboardData || window.clipboardData;
const pastedText = clipboardData.getData('text/plain');
// Preserve trailing whitespaces
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
range.deleteContents();
// Create text node with preserved whitespace
const textNode = document.createTextNode(pastedText);
range.insertNode(textNode);
// Move cursor to end
range.setStartAfter(textNode);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
}
});