Draft.js: Simple Content Manipulation
Tags: tutorial, javascript, react, draftjs
Published: 2019-11-02 00:52:36


Hey it’s part 5 of the series! I hope you are enjoying playing with Draft.js so far.

Today, I want to talk a bit about inserting content into the editor.

Some examples of when you may want to do this:

* (simple) emoji picker: Maybe you want to let people click an emoji icon and have it automatically insert that emoji character
* (simple) mentioning: Maybe you want to have an icon you click that inserts the `@` mention trigger character
* (more complicated) markdown: Maybe you want to be able to insert markdown characters  under certain circumstances, or wrap text in markdown characters (eg maybe highlighting a word and hitting cmd/ctrl b wraps it in `**` instead of actually bolding)
* (more complicated) find-and-replace functionality

Let’s look at a simple example first 🙂 

[Here is what the finished product will look like](https://www.rosey.dev/demos/draft-article-5/)

- - - -

## Simple example: Emoji Insertion

⁉️ For this example we’ll just be inserting the emoji characters into our editor when you click on them. If you are doing some real-life emoji work with your editor,  you may want to look at something like [This Draft.Js emoji plugin](https://www.draft-js-plugins.com/plugin/emoji) which converts emoji into their own custom [Entity](https://draftjs.org/docs/advanced-topics-entities) and provides a lot more flexibility for styling in-editor, as well as swapping out emoji assets for non-native assets if you want to.

### The Modifier Module
We’re going to be using [Draft.js’s Modifier](https://draftjs.org/docs/api-reference-modifier) to insert content into our editor.

What we want to do is insert the emoji at whatever location the user’s caret is currently at (their current “selection”) . We also want to replace content, if they have a range of text selected.

For this we’ll use `Modifier`’s method `replaceText` which takes:

* The editor’s current `ContentState`
* The text to be replaced, as indicated by `SelectionState` (this provides a range so the editor can say “ok I need to replace the content that currently exists between location X and location Y”. You could provide any range you like, provided content exists in that range, but for this simple case we just want to insert text where the selection already is, so no need to specify anything custom)
* The text to be inserted.

What it _returns_ is a new instance of `ContentState	` that contains this change.

However, we still need to get this change into our `EditorState` and we can do this by using `EditorState.push`

`EditorState.push` takes
* The editor state to apply changes to
* The content state to apply (so we want the result of `Modifier.replaceText` here)
* What kind of change you’re making, also known as an [EditorChangeType](https://draftjs.org/docs/api-reference-editor-change-type). We’re doing `insert-characters`

`EditorState.push` returns a new instance of `EditorState`.

So with this in mind I wrote this small `insertCharacter` helper function:

```javascript
function insertCharacter(characterToInsert, editorState) {
  const currentContent = editorState.getCurrentContent(),
        currentSelection = editorState.getSelection();

  const newContent = Modifier.replaceText(
    currentContent,
    currentSelection,
    characterToInsert
  );

  const newEditorState = EditorState.push(editorState, newContent, 'insert-characters');

  return  newEditorState;
}
```

It takes the character you want to insert, as well as the current `editorState`. It applies the modifications needed, then returns the new `editorState`.

We’d then need to apply `this.setState` with the new editorState to ensure we are actually _using_ it, so the code that calls it would be something like:

```javascript
const newEditorState = insertCharacter('💖', this.state.editorState);
this.setState({
  editorState: newEditorState
});
```

For my example I wanted to make some little emoji buttons, so I added this to my render method:

```jsx
<div className="emoji-picker">
  <h2 className="toolbar-title">Insert Emoji:</h2>
  <button
      className="emoji"
      onMouseDown={(e) => e.preventDefault()}
      onClick={this.onEmojiClick}
      data-emoji="🎊">
        <span role="img" aria-label="confetti">🎊</span>
  </button>
  <button
      className="emoji"
      onMouseDown={(e) => e.preventDefault()}
      onClick={this.onEmojiClick}
      data-emoji="💖">
        <span role="img" aria-label="sparkle heart">💖</span>
  </button>
  <button
      className="emoji"
      onMouseDown={(e) => e.preventDefault()}
      onClick={this.onEmojiClick}
      data-emoji="🌼">
        <span role="img" aria-label="yellow flower">🌼</span>
  </button>
</div>
```

Then I defined `onEmojiClick` like so -

```javascript
  onEmojiClick (e) {
    let emoji = e.currentTarget.getAttribute('data-emoji');
    this.setState({editorState: insertCharacter(emoji, this.state.editorState)});
  }
```

In other words, exactly what we talked about 🙂 

There’s one other little gotcha here. You may notice that if you click the button when the editor doesn’t have focus, an emoji is inserted but the editor remains unfocused, which is likely not your desired behaviour.

If you like,  you can ensure focus by using `EditorState.forceSelection` and update your `insertCharacter` method slightly:

```javascript
function insertCharacter(characterToInsert, editorState) {
  const currentContent = editorState.getCurrentContent(),
        currentSelection = editorState.getSelection();

  const newContent = Modifier.replaceText(
    currentContent,
    currentSelection,
    characterToInsert
  );

  const newEditorState = EditorState.push(editorState, newContent, 'insert-characters');

  return  EditorState.forceSelection(newEditorState, newContent.getSelectionAfter());
}
```

This will make sure that after you insert the character, the selected state will be immediately after that character. ([Official forceSelection documentation here](https://draftjs.org/docs/api-reference-editor-state#forceselection))

So there’s our little intro into making modifications to your editor’s content. I will follow up in my next post with some trickier content replacement 🙂 (I would have done it all in one but I think shorter articles are a bit easier for people to consume, plus it makes it easier for me to publish updates more frequently 🙃)

Thanks (as always) for reading 💕