Skip to content

Instantly share code, notes, and snippets.

@neatcoding
Last active March 2, 2023 10:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save neatcoding/58b865ca3a2f5e8097cd7a5fd2c96c5e to your computer and use it in GitHub Desktop.
Save neatcoding/58b865ca3a2f5e8097cd7a5fd2c96c5e to your computer and use it in GitHub Desktop.
Hack-fix for Samsung Keyboard within Android WebView contenteditable causing a spam of new lines & clearing HTML
/** Prevents the `beforeinput` event, so that the `input` doesn't happen. */
const preventTheEvent = (event: InputEvent): void => {
console.log('Prevented a buggy beforeinput event.');
event.preventDefault();
};
/**
* Prevents Samsung Keyboard bug which adds extra line breaks randomly, moves the caret, and removes custom
* HTML formatting in the contenteditable. It affects e.g. mobile apps, Bing, Opera, Brave, Edge
*
* Video with minimal reproduction and details: https://loom.com/share/a380ebaa36104b57b5f7161332de03dc
* Remove the fix when the issue no longer happens.
*/
export const hackFixSamsungKeyboardBug = (event: InputEvent): void => {
const target = event.target as HTMLElement;
const newText = event.data;
const isPotentiallySamsungKeyboardBugEvent =
target.isContentEditable && event.inputType === 'insertText' && !event.isComposing && newText;
if (!isPotentiallySamsungKeyboardBugEvent) {
return;
}
// Now checks that might be heavier due to DOM reading
const selection = document.getSelection();
// While the buggy event happens, there is a selection starting at the beginning of the text.
// In case of other contenteditables this was not the case, but in case of ours with ProseMirror it is.
// We want to narrow it down as much as possible to avoid preventing events that user actually meant.
if (selection && selection.rangeCount === 1) {
const range = selection.getRangeAt(0);
const isNoTextSelected = range.collapsed;
if (isNoTextSelected || range.startOffset !== 0) {
return;
}
}
// Samsung Keyboard bug changes the number of line breaks in the text, but
// rest is the same as existing .innerText (not the same as .textContent!)
const existingText = target.innerText;
const isTextSameExceptForLineBreaks = existingText.replace(/\n/g, '') === newText.replace(/\n/g, '');
if (existingText.length === 0 || !isTextSameExceptForLineBreaks) {
return;
}
// In most of the cases when the issue happens, there's a line break at the end of the text.
if (newText.endsWith('\n')) {
preventTheEvent(event);
return;
}
// Sometimes there is no line break at the end of new text,
// but another thing specific for this issue is that the number of line breaks decreases.
const existingLineBreaks = existingText.match(/\n/g);
const newLineBreaks = newText.match(/\n/g);
const numberOfLineBreaksDecreased =
existingLineBreaks && newLineBreaks && existingLineBreaks.length > newLineBreaks.length;
if (numberOfLineBreaksDecreased) {
preventTheEvent(event);
}
};
// Use code below to use the fix.
// Also, check whether the selection check in line 34 doesn't break the fix for you - you might want to remove it.
if (navigator.userAgent.includes('Android')) {
// You can also attach it directly to the `contenteditable`
document.addEventListener('beforeinput', hackFixSamsungKeyboardBug);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment