はじめに
Editor.jsを使ってテキストエディタを実装する方法を解説します。
本記事ではNuxt.jsを使ってますが、Vue.jsやJavaScript単体でも使用できます。
主に公式のガイドを参考にして、できるだけ丁寧に解説するので、ぜひお付き合いください。
Editor.jsの概要
Editor.jsとはテキストエディタの開発するためのJavaScriptライブラリです。
Editor.jsの特徴として
- 流行りのブロックスタイル
- 出力がJSON形式で汎用的
- カスタマイズ性が高い
があげられ、シンプルかつ自分好みのエディタを簡単に実装できます。
導入
まずライブラリをインストールします。 ※CDNを使う方法もありますが割愛します
$ yarn add @editorjs/editorjs
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を編集して、作成したファイルプラグインとして呼び出せるようにします。
//省略
// 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ファイルを作成します。
<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
マッサラなキャンパスが用意されています。
Notionのようなブロックエディタになっていて、文字の入力と3つのインラインツールが使用できます。
しかしこのままではシンプルな文字が入力できるだけです。
拡張
Editor.jsは機能を追加して拡張することで真価を発揮します。
拡張の方法は2つ。
- 既存のプラグインを追加する
- 自作する
まずは前者の方法から。
既存のプラグインを追加する
Editor.jsには基本的な機能を追加するためのプラグインがいくつか用意されています。
今回はEditor.jsのGithubで紹介されている中からいくつか選んで使用します。
まずはモジュールをインストールします。
$ yarn add @editorjs/header @editorjs/link @editorjs/quote
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>
タグになるようにしています。
自作する
Editor.jsは任意の機能を追加するために必要なAPIを提供してくれていて、とても簡単に機能追加ができます。
今回はコードを記述するコードブロックを作成します。
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メソッドではエディタで機能を使用する際の名前とアイコンを設定します。
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を読み込みます。
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要素に記述ができるようになっています。。
データ管理
このままではタブを閉じたりリロードすると入力した内容は消えてしまうので、保存と更新ができるようにします。
Editor.jsはデータをJSON形式で出力するため、簡単にデータのやり取りができます。
基本的にデータの加工が必要ないので、データの保管をする収納ケースさえあれば十分です。
今回は、 記事の内容を文字列で取得/更新できるAPIがあると仮定します。
edit.vueファイルにasyncDataメソッドを追加して、記事のデータを取得します。
<script>
export default {
asyncData(context) {
return context.$axios
.get('post')
.then((response) => {
return {
post: response,
}
})
.catch((error) => {
console.log(error)
})
},
// 省略
}
</script>
そして、取得した記事データを初期値として設定します。
<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ファイルを更新します。
<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では可能なのでぜひ使ってみてください。