Do you want to request a feature or report a bug?
BUG
What is the current behavior?
Press backspace will partially delete some emoji character, for example π
ββοΈ (no_good_men) will become π
(no_good_woman)
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. You can use this jsfiddle to get started:
Paste this emoji in any draftjs editor, press backspace several times will see the emoji changes to another one
What is the expected behavior?
The emoji should be deleted as a whole.
Which versions of Draft.js, and which browser / OS are affected by this issue? Did this work in previous versions of Draft.js?
All version of draftjs, all browser, only tried on MacOS.
This is because this emoji is a sequence of three characters: π + a zero-width joiner + β. Draft.js (or contenteditable?) has varying support for Unicode sequences.
GIF:

On the first line, I pressed backspace three times to delete the emoji from the right-hand side. On the second, pressed fn + backspace (suppr) three times from the left-hand sign. You can see the cursor jumping position at some point β I think this is because of contenteditable's handling of the zero-width character.
If anyone wants to look into this, https://mathiasbynens.be/notes/javascript-unicode is a good read on unicode gotchas in JavaScript.
I was able to workaround the issue by utilizing this library: https://github.com/orling/grapheme-splitter to determine the length of the emoji grapheme about to be deleted, and then writing a custom handleBackspace() function to handle the deleting of the full emoji grapheme.
You can mark the inserted emoji as an immutable entity, so it gets removed as a whole, not using individual utf8 code-points:
function addEmoji(editorState, utf8Code) {
const contentState = editorState.getCurrentContent();
const contentStateWithEntity = contentState.createEntity('emoji', 'IMMUTABLE', { utf8Code });
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const contentWithEmoji = Modifier.insertText(contentState, editorState.getSelection(), utf8Code, undefined, entityKey);
return EditorState.push(editorState, contentWithEmoji, 'insert-characters');
};
That should prevent the problem.

There's a sample in the documentation related to entities:
@luisbelloch the problem with using entities for this is that there can only be one entity on a given range of text β so if you want to use links in your editor, adding the links will replace the previous entity, and we're back to having broken emoji deletion. It also doesn't work if emojis are pasted in the editor rather than added via some emoji picker.
Thanks for pointing those out! :-) I may be wrong, but I'd say links having emojis can be easily solved using composite decorators, but it depends on each use case, Draftjs has many possibilities!
For the copy/paste issue we managed to use the handlePastedText event and correct the entity structure there.
@thibaudcolas:
This is because this emoji is a sequence of three characters: π + a zero-width joiner + β. Draft.js (or contenteditable?) has varying support for Unicode sequences.
This also affects Emoji modifier sequences. I understand that this is an explanation of what's happening, but argue that you're just restating the bug. I came to report the issue when working with Emoji modifiers and found this bug.
According to the Unicode UTS #51 spec:
A supported emoji modifier sequence should be treated as a single grapheme cluster for editing purposes (cursor moment, deletion, and so on); word break, line break, and so on.
These shouldn't be treated as graphics or need entities to mark them - they are basic text and should be ideally treated according to specification, otherwise we might downplay the problem as being related to visuals when indeed it also impacts non-Roman scripts in similar ways, for example, many Indic scripts which rely on the ZWJ and ZWNJ for normal text.
For more information see UAX #29 - Unicode Text Segmentation. My interpretation is that even with ZWJ sequences once the text is entered into the document it should remain an atomic unit for editing purposes.
a grapheme cluster should be an atomic unit with respect to the process of determining these other boundaries.
As far as a user is concerned, the underlying representation of text is not important, but it is important that an editing interface present a uniform implementation of what the user thinks of as characters. Grapheme clusters can be treated as units, by default, for processes such as the formatting of drop caps, as well as the implementation of text selection, arrow key movement or backspacing through text, and so forth. For example, when a grapheme cluster is represented internally by a character sequence consisting of base character + accents, then using the right arrow key would skip from the start of the base character to the end of the last accent.
The spec does allow for exceptions to this ruleβ¦
editing a grapheme cluster element by element may be preferable in some circumstances. For example, on a given system the backspace key might delete by code point, while the delete key may delete an entire cluster.
β¦but in this case it feels like this is a bug more than an intentional decision.
To wit, draftjs selection and cursor movement appears to get this right - it's specifically the behavior on backspace that breaks conformance.
Was there any resolution to this?
Does anyone have a solution to this issue ?
Having the same problem...
Most helpful comment
Does anyone have a solution to this issue ?