Draft.js introduction: Custom styles (highlighted text!) and have formatting buttons show whether they are “on” or “off”
Tags: tutorial, react, javascript, draftjs
Published: 2019-10-26 18:23:09

Hello!

I can’t believe we’re already on #4 of this series 😱

Today I wanted to cover 2 fun topics that I think are useful to know about:

1. Defining custom styles (in my example we’ll be adding a “highlighter” option to the editor)
2. Detecting what styles/blocks are currently active based on the selected text or cursor position. This can be useful for things like showing an “on” and “off” in state in the formatting buttons.

🚀 [This is how the finished product will look at the end of the post](https://www.rosey.dev/demos/draft-article-4/)

- - - -

## Defining custom styles
Draft.js makes this very simple 🙂 We need to define a "style object" and pass it in to the editor.

I liked the idea of letting users highlight certain blocks of text, so that’s the style I thought we could add for this demo.

We create an object that we’ll call `styleMap` that has the `key` for the style’s name, and the `value` is another object that defines the CSS we want to use to style that item. So if bold didn’t already exist the `key` would be `BOLD` and the value would be `{'fontWeight': 'bold'}`

But bold does exist of course, so here’s our little custom style for highlight:

```javascript
const styleMap = {
  'HIGHLIGHT': {
    'backgroundColor': '#faed27',
  }
};

```

(🤓 Newbie hint: If you want to define multiple custom styles, they can be comma-separated here, so after the closing `}` for `HIGHLIGHT` you could add another style definition).

Then we need to pass in our `styleMap` to the editor component itself, as `customStyleMap` -

```javascript
<Editor
  customStyleMap={styleMap}
  placeholder={"Start typing!"}
  editorState={this.state.editorState}
  onChange={this.onChange}
  handleKeyCommand={this.handleKeyCommand}
  keyBindingFn={keyBindingFunction}
/>
```

🌼 So now the editor knows about the highlight style and knows what to do with it, but that’s not much use if we don’t expose anywhere for the user to toggle it.

As taught in [this previous series post](https://dev.to/rose/rich-text-editing-on-the-web-formatting-text-and-keyboard-shortcuts-in-draft-js-4g9f) I’ll add a keyboard shortcut (cmd [or ctrl] + shift + h) for highlighting text, and I’ll also add a button to my inline style buttons.

I’d go back and read that post if you need the full block of code to jog your memory, but for the keyboard shortcut, I’ll be adding to my `keyBindingFunction` the following `if` statement:

```javascript
if (KeyBindingUtil.hasCommandModifier(event) && event.shiftKey && event.key === 'h') {
  return 'highlight';
}
```

And then in the `handleKeyCommand` function I’ll add this:

```javascript
if (!editorState && command === 'highlight') {
  editorState = RichUtils.toggleInlineStyle(this.state.editorState, 'HIGHLIGHT');
}
```

And now the keyboard shortcut to highlight text should be functioning! 

Adding the button should also be fairly straightforward. Since I have an array defining all my inline styles which then uses `.map` to render the buttons, I just add a new item to that array:

```javascript
const inlineStyleButtons = [
  {
    value: 'Bold',
    style: 'BOLD'
  },

  {
    value: 'Italic',
    style: 'ITALIC'
  },

  {
    value: 'Underline',
    style: 'UNDERLINE'
  },

  {
    value: 'Strikethrough',
    style: 'STRIKETHROUGH'
  },

   {
    value: 'Code',
    style: 'CODE'
  },

  {
    value: 'Highlight',
    style: 'HIGHLIGHT'
  }
];
```

And we’re done. Highlighting functionality complete ✅

## Detecting active styles and blocks
The `editorState` instance contains everything there is to know about your Draft editor at any given moment, and this includes knowing where your cursor (selection state) is.

Draft.js also provides a couple of handy helper functions to use this knowledge to tell you exactly what styles are active, and what block element is selected.

To get the _current inline style_ - in other words, bold, italic, code, highlight, etc, you can call `this.state.editorState.getCurrentInlineStyle()`

This returns an [Ordered Set](https://immutable-js.github.io/immutable-js/docs/#/OrderedSet) of the currently active styles. If you aren’t familiar with immutable.js’s Ordered Set you can check out that link if you like, but the thing we care about right now is that it has a method called `has` to check for the existence of a key.

We can use that to search for things like `.has('BOLD')` to get a true-or-false response.

Here’s my updated `renderInlineStyleButton` method that uses this check and then conditionally sets an `active` class on the button if `.has` returns `true` for that style type:

```javascript
renderInlineStyleButton(value, style) {
  const currentInlineStyle = this.state.editorState.getCurrentInlineStyle();
  let className = '';
  if (currentInlineStyle.has(style)) {
    className = 'active';
  }

  return (
    <input
      type="button"
      key={style}
      value={value}
      className={className}
      data-style={style}
      onClick={this.toggleInlineStyle}
      onMouseDown={preventDefault}
    />
  );
}
```

We’re going to do something similar with our `renderBlockButton` but instead of using `editorState.getCurrentInlineStyle` we’re going to use a helper method on `RichUtils`

⁉️ I have no idea why two fairly similar functionalities are found in two different parts of Draft.js 😬

Draft only allows you to have one block type at a time, so instead of getting a set of values, we’ll just be getting a single block type.

The method we want to use is `RichUtils.getCurrentBlockType` which takes `editorState` as an argument and returns a `string` of a block type as a response. So if  we were checking for a `blockquote` we could do something like `RichUtils.getCurrentBlockType(this.state.editorState) === 'blockquote'`

Here’s my block button render method with the additional conditional `active` class:

```javascript
renderBlockButton(value, block) {
  const currentBlockType = RichUtils.getCurrentBlockType(this.state.editorState);
  let className = '';
  if (currentBlockType === block) {
    className = 'active';
  }

  return (
    <input
      type="button"
      key={block}
      value={value}
      className={className}
      data-block={block}
      onClick={this.toggleBlockType}
      onMouseDown={preventDefault}
    />
  );
}
```

And then you’re basically done. The only thing left is to add some CSS so that toggling the `active` class on and off actually has some visual effect.

Here’s the simple CSS I used if you want something to get started:

```css
.toolbar input[type="button"] {
  border: 1px solid #eee;
  background-color: #fff;
  border-radius: 5px;
  cursor: pointer;
  margin-right: 2px;
  margin-bottom: 2px;
}

.toolbar input[type="button"]:active {
  transform: scale(.95);
}

.toolbar input[type="button"]:not(.active):hover {
  background-color: #eee;
}

.toolbar input[type="button"].active {
  background-color: turquoise;
  border-color: transparent;
}
```

🎊 Another tutorial complete! Again,  [you can check out the finished product of this tutorial here.](https://www.rosey.dev/demos/draft-article-4/)

I’m hoping next time to step away from some of the basics and jump into a post about an interesting problem I had to solve recently that required some more complex manipulation of content. So stay tuned for that if you’re finding these posts a little too easy for your skill level 🙂 But also, don’t worry! If you like the simplicity of these posts, I have some more beginner-friendly ideas up my sleeve too. 💕