12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Editor.jsを使ってテキストエディタを作る

Posted at

はじめに

Editor.jsを使ってテキストエディタを実装する方法を解説します。
本記事ではNuxt.jsを使ってますが、Vue.jsやJavaScript単体でも使用できます。
主に公式のガイドを参考にして、できるだけ丁寧に解説するので、ぜひお付き合いください。

Editor.jsの概要

Editor.jsとはテキストエディタの開発するためのJavaScriptライブラリです。

Editor.jsの特徴として

  • 流行りのブロックスタイル
  • 出力がJSON形式で汎用的
  • カスタマイズ性が高い

があげられ、シンプルかつ自分好みのエディタを簡単に実装できます。

導入

まずライブラリをインストールします。 ※CDNを使う方法もありますが割愛します

$ yarn add @editorjs/editorjs

pluginsフォルダ(なければ作成)にeditor.jsというファイルを作成し、以下のように記述します。

./plugins/editor.js
import EditorJS from '@editorjs/editorjs'

export default ({ $axios }, inject) => {
  inject('editor', {
    EditorJS: ({ holder, placeholder, data }) => {
      return new EditorJS({
        holder,
        placeholder,
        data,
      })
    },
  })
}

nuxt.config.jsを編集して、作成したファイルプラグインとして呼び出せるようにします。

nuxt.config.js
//省略

// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
 plugins: [{ src: '~/plugins/editor.js', ssr: false }],

ssr:falseと記述することでクライアントサイドでのみファイルがインクルードされるようにしています。(デフォルトではtrue)
Nuxt - plugins プロパティ

次にエディット画面を用意します。
pagesディレクトリ配下にedit.vueファイルを作成します。

./pages/edit.vue
<template>
  <div id="editorjs"></div>
</template>

<script>
export default {
  data() {
    return {
      editor: null,
    }
  },
  mounted() {
    this.editor = this.$editor.EditorJS({
      holder: 'editorjs',
      placeholder: 'No content',
      data: {},
    })
  },
}
</script>

エディターを描画したい要素のIDを任意に設定し、holderに同じように記述します。
今回はeditorjsにしています。

placeholderには何も入力がない状態で表示させる文字を設定します。
今回はNo contentにしています。

dataには初期表示させるデータを設定します。

これでEditor.jsを使う準備ができました。

editページにアクセスすると
http://localhost:3000/edit

スクリーンショット 2021-11-17 20.57.23.png

マッサラなキャンパスが用意されています。

Notionのようなブロックエディタになっていて、文字の入力と3つのインラインツールが使用できます。
スクリーンショット 2021-11-17 22.14.38.png

しかしこのままではシンプルな文字が入力できるだけです。

拡張

Editor.jsは機能を追加して拡張することで真価を発揮します。

拡張の方法は2つ。

  • 既存のプラグインを追加する
  • 自作する

まずは前者の方法から。

既存のプラグインを追加する

Editor.jsには基本的な機能を追加するためのプラグインがいくつか用意されています。
今回はEditor.jsのGithubで紹介されている中からいくつか選んで使用します。

まずはモジュールをインストールします。

$ yarn add @editorjs/header @editorjs/link @editorjs/quote

editor.jsファイルを更新します。

./plugins.editor.js
import EditorJS from '@editorjs/editorjs'
import Header from '@editorjs/header'
import List from '@editorjs/list'
import Quote from '@editorjs/quote'

export default ({ $axios }, inject) => {
  inject('editor', {
    EditorJS: ({ holder, placeholder, data }) => {
      return new EditorJS({
        holder,
        placeholder,
        data,
        tools: {
          header: {
            class: Header,
            inlineToolbar: true,
            config: {
              levels: [2, 3, 4],
              defaultLevel: 3,
            },
          },
          quote: {
            class: Quote,
            inlineToolbar: true,
          },
          list: {
            class: List,
            inlineToolbar: true,
          },
        },
      })
    },
  })
}

toolsに拡張したい機能を追加していきます。

headerを例に解説すると

header: {
  class: Header,
  inlineToolbar: true,
  config: {
    levels: [2, 3, 4],
    defaultLevel: 3,
},
  • header:JSONで出力されるときのtypeプロパティの値になります。
  • class:importしたHeaderクラスを定義しています。
  • inlineToolbar:trueにすることで、文字をドラッグしたときにでるインラインツールバーから機能を使うことができるようになります。
  • config:追加した機能にオプションがある場合に入力します。今回はヘッダータグがデフォルトで<h3>タグになるようにしています。

これで拡張ができました。
スクリーンショット 2021-11-17 22.38.11.png

自作する

Editor.jsは任意の機能を追加するために必要なAPIを提供してくれていて、とても簡単に機能追加ができます。

今回はコードを記述するコードブロックを作成します。
pluginsディレクトリ配下にcode_tool.jsファイルを追加します。

./plugins/code_tool.js
class CodeTool {
    static get toolbox() {
      return {
        title: 'code',
        icon: `<span>Code</span>`,
      }
    }

    constructor({ data }) {
      this.data = data
      this.wrapper = undefined
    }

    render() {
      this.wrapper = document.createElement('div')
      this.wrapper.classList.add('code-block')
      const pre = document.createElement('pre')
      const code = document.createElement('code')
      const textarea = document.createElement('textarea')
      textarea.value = this.data.code ? this.data.code : ''

      this.wrapper.appendChild(pre)
      pre.appendChild(code)
      code.appendChild(textarea)

      textarea.addEventListener('input', () => {
        const scrollHeight = textarea.scrollHeight
        textarea.style.height = scrollHeight + 'px'
      })
      return this.wrapper
    }

    save(blockContent) {
      const textarea = blockContent.querySelector('textarea')
      const code = textarea.value
      return {
        code,
      }
    }
  }

  export { CodeTool }


各メソッドを解説します。

 static get toolbox() {
      return {
        title: 'code',
        icon: `<span>Code</span>`,
      }
    }

toolboxメソッドではエディタで機能を使用する際の名前とアイコンを設定します。

今回の設定だと画像のようになります。
スクリーンショット 2021-11-17 22.56.10.png

constructor({ data }) {
    this.data = data
    this.wrapper = undefined
}

constructorメソッドでは初期値として受け取ったdataの中の、ブロック単位のデータを引数として受け取れます。

今回作成するコードでは以下のようなデータなります。

{
    "id": "Q5whZ_4tYd",
    "data": {
        "code": "const hoge = 'hoge'"
    },
    "type": "code"
}
render() {
    this.wrapper = document.createElement('div')
    this.wrapper.classList.add('code-block')
    const pre = document.createElement('pre')
    const code = document.createElement('code')
    const textarea = document.createElement('textarea')
    textarea.value = this.data.code ? this.data.code : ''

    this.wrapper.appendChild(pre)
    pre.appendChild(code)
    code.appendChild(textarea)

    textarea.addEventListener('input', () => {
    const scrollHeight = textarea.scrollHeight
    textarea.style.height = scrollHeight + 'px'
    })
    return this.wrapper
}

render関数ではエディタに描画されるDOM要素を設定します。

save(blockContent) {
    const textarea = blockContent.querySelector('textarea')
    const code = textarea.value
    return {
        code,
    }
}

saveメソッドでは描画された要素のデータを引数として受け取り、出力されるJSONのdataプロパティの内容を戻り値として設定します。
codeプロパティの値がtextreaに入力された値になるように設定しています。

次に作成したCodeToolを読み込みます。

./plugins/editor.js
import EditorJS from '@editorjs/editorjs'
// 省略
import { CodeTool } from './code_tool'

export default ({ $axios }, inject) => {
  inject('editor', {
    EditorJS: ({ holder, placeholder, data }) => {
      return new EditorJS({
        holder,
        placeholder,
        data,
        logLevel: 'ERROR',
        minHeight: 50,
        tools: {
        // 省略
          code: CodeTool,
        },
      })
    },
  })
}

これで自作したCodeToolが使えるようになりました。
このままではコードを記述する機能としては不十分ですが、今回はよしとします。

editページで確認すると画像のようにコードブロックっぽいだけのtextarea要素に記述ができるようになっています。。

スクリーンショット 2021-11-17 23.22.57.png

データ管理

このままではタブを閉じたりリロードすると入力した内容は消えてしまうので、保存と更新ができるようにします。

Editor.jsはデータをJSON形式で出力するため、簡単にデータのやり取りができます。
基本的にデータの加工が必要ないので、データの保管をする収納ケースさえあれば十分です。

今回は、 記事の内容を文字列で取得/更新できるAPIがあると仮定します。

edit.vueファイルにasyncDataメソッドを追加して、記事のデータを取得します。

./pages/edit.vue
<script>
export default {
  asyncData(context) {
    return context.$axios
      .get('post')
      .then((response) => {
        return {
          post: response,
        }
      })
      .catch((error) => {
        console.log(error)
      })
  },
// 省略
}
</script>

そして、取得した記事データを初期値として設定します。

./pages/edit.vue
<script>
export default {
// 省略
  mounted() {
    this.editor = this.$editor.EditorJS({
      holder: 'editorjs',
      placeholder: 'No content',
    - data: {},
    + data: JSON.parse(this.post),
    })
  },
// 省略
}
</script>

これで取得したデータを初期値として表示できるようになりました。

次に編集した内容で更新できるようにします。

Editor.JSではsaveメソッドを使ってデータの保存をすることができます。
edit.vueファイルを更新します。

./pages/edit.vue
<template>
  <div>
    <div id="editorjs"></div>
    <button @click="save()">更新</button>
  </div>
</template>

<script>
export default {
// 省略
  methods: {
    async save() {
      await this.editor.save().then((savedData) => {
        this.post = JSON.stringify(savedData)
      })
      this.$axios
        .put('post', {
          post: this.post,
        })
        .catch((err) => {
          console.log(err)
        })
    },
  },
}
</script>

EditorJSのsaveメソッドはエディタに記述された内容をJSON形式のデータで出力してくれます。
今回はsaveメソッドで出力されたJSONオブジェクトを文字列形式に変換しています。

更新ボタンをクリックすることで記事の更新が可能になりました。

これでテキストエディタとして標準的な機能は揃いました。

さいごに

Editor.jsを使ってテキストエディタを実装する方法を解説しました。
自分自身がエディタを実装する際に出会って、いいな〜って思ったので記事にしました。
本記事では解説してませんが、インラインツールの拡張等もEditor.jsでは可能なのでぜひ使ってみてください。

12
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?