はじめに
2019/11時点でGitHubにあるWYSIWYGタグでスターが多いライブラリを調べてみます。
Quill
概要
GitHub
https://github.com/quilljs/quill
Demo
https://quilljs.com/
・テーブルの作成はできないようです(ver2.x用にテーブル追加のプラグインはある)
・クリップボードを経由して画像のアップロードが可能です。
・highlight.jsを使用してコードブロックのハイライトが可能のようです
ライセンス
BSD 3-clause
サンプル
1.3.7のサンプル
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test Quill</title>
<!-- highlight.js を使う場合はquillの前に参照する-->
<link rel="stylesheet"
href="http://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.16.2/build/styles/default.min.css">
<script src="http://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.16.2/build/highlight.min.js"></script>
<link href="https://cdn.quilljs.com/1.3.7/quill.snow.css" rel="stylesheet">
<script src="https://cdn.quilljs.com/1.3.7/quill.js"></script>
</head>
<body>
<!-- Create the editor container -->
<div id="editor">
<p>Hello World!</p>
</div>
<button id="btnContent">コンテンツ取得</button>
<button id="btnImage">イメージの挿入</button>
<button id="btnDisable">編集可能/不可能</button>
<script>
var Delta = Quill.import('delta');
const quill = new Quill('#editor', {
theme: 'snow',
modules: {
syntax : true, // Include syntax module
// https://quilljs.com/docs/modules/toolbar/
toolbar : [
['bold', 'italic', 'underline', 'strike'],
[{ 'color': [] }, { 'background': [] }],
['link', 'image'] ,
['code-block']
]
}
});
document.getElementById('btnContent').addEventListener('click', function() {
console.log(quill.getContents());
});
document.getElementById('btnImage').addEventListener('click', function() {
// このあたりを工夫すればクリップボードからの画像貼り付け等ができそう・・
console.log(quill.getSelection(true).index);
quill.insertEmbed(quill.getSelection(true).index, 'image', 'https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png');
});
let enableEditor = false;
document.getElementById('btnDisable').addEventListener('click', function() {
quill.enable(enableEditor);
enableEditor = !enableEditor;
console.log(enableEditor);
});
/**
* ペーストのイベント追加例
*/
quill.root.addEventListener("paste", function (t) {
console.log('paste');
console.log(t);
return true;
} , false);
</script>
</body>
</html>
拡張モジュール
画像の貼り付けについて
quill-image-drop-and-paste
https://github.com/chenjuneking/quill-image-drop-and-paste
quill-image-drop-and-pasteはどうも以下のように修正しないと動作しないようです。
export.ImageDropAndPasteを使用しているが、設定していないので替わりにImageDropAndPasteを設定する。
(function(){var exports={};
"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function e(e,t){for(var a=0;a<t.length;a++){var n=t[a];n.enumerable=n.enumerable||false;n.configurable=true;if("value"in n)n.writable=true;Object.defineProperty(e,n.key,n)}}return function(t,a,n){if(a)e(t.prototype,a);if(n)e(t,n);return t}}();function _classCallCheck(e,t){if(!(e instanceof t)){throw new TypeError("Cannot call a class as a function")}}var ImageDropAndPaste=function(){function e(t){var a=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};_classCallCheck(this,e);this.quill=t;this.options=a;this.handleDrop=this.handleDrop.bind(this);this.handlePaste=this.handlePaste.bind(this);this.quill.root.addEventListener("drop",this.handleDrop,false);this.quill.root.addEventListener("paste",this.handlePaste,false)}_createClass(e,[{key:"handleDrop",value:function e(t){var a=this;t.preventDefault();if(t.dataTransfer&&t.dataTransfer.files&&t.dataTransfer.files.length){if(document.caretRangeFromPoint){var n=document.getSelection();var i=document.caretRangeFromPoint(t.clientX,t.clientY);if(n&&i){n.setBaseAndExtent(i.startContainer,i.startOffset,i.startContainer,i.startOffset)}}this.readFiles(t.dataTransfer.files,function(e,t){if(typeof a.options.handler==="function"){a.options.handler(e,t)}else{a.insert.call(a,e,t)}},t)}}},{key:"handlePaste",value:function e(t){var a=this;if(t.clipboardData&&t.clipboardData.items&&t.clipboardData.items.length){this.readFiles(t.clipboardData.items,function(e,t){if(typeof a.options.handler==="function"){a.options.handler(e,t)}else{a.insert(e,t)}},t)}}},{key:"readFiles",value:function e(t,a,n){[].forEach.call(t,function(e){var t=e.type;if(!t.match(/^image\/(gif|jpe?g|a?png|svg|webp|bmp)/i))return;n.preventDefault();var i=new FileReader;i.onload=function(e){a(e.target.result,t)};var r=e.getAsFile?e.getAsFile():e;if(r instanceof Blob)i.readAsDataURL(r)})}},{key:"insert",value:function e(t,a){var n=(this.quill.getSelection()||{}).index||this.quill.getLength();this.quill.insertEmbed(n,"image",t,"user")}}]);return e}();exports.default=ImageDropAndPaste;
window.Quill.register('modules/imageDropAndPaste',ImageDropAndPaste)})(); // export.ImageDropAndPaste->ImageDropAndPaste
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test Quill</title>
<link href="https://cdn.quilljs.com/1.3.7/quill.snow.css" rel="stylesheet">
<script src="https://cdn.quilljs.com/1.3.7/quill.js"></script>
<script src="quill-image-drop-and-paste-master/quill-image-drop-and-paste.min.js" type="text/javascript"></script>
</head>
<body>
<button id="btnContent">コンテンツ取得</button>
<br>
クリップボードからイメージをbase64で張り付けている。
<!-- Create the editor container -->
<div id="editor">
<p>Hello World!</p>
</div>
<script>
const quill = new Quill('#editor', {
theme: 'snow',
modules: {
imageDropAndPaste : true
}
});
document.getElementById('btnContent').addEventListener('click', function() {
console.log(quill.getContents());
});
</script>
</body>
</html>
テーブル操作
quilljs-table
quilljs-table
https://github.com/dost/quilljs-table
サンプルを見る限り、テーブルの削除や列、行の削除がGUIからできそうにないです。
最終コミット日が2017年。
quilljs-table
quilljs-table
https://github.com/volser/quill-table-ui
quilljs v2.0.0-dev.3が必要になります。
最終更新日は2019年10月25日です。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test Quill</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/quill/2.0.0-dev.3/quill.min.js" type="text/javascript"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/quill/2.0.0-dev.3/quill.snow.min.css" rel="stylesheet">
<script src="https://unpkg.com/quill-table-ui@1.0.5/dist/umd/index.js" type="text/javascript"></script>
<link href="https://unpkg.com/quill-table-ui@1.0.5/dist/index.css" rel="stylesheet">
</head>
<body>
<button id="btnContent">コンテンツ取得</button>
<button id="btnTable">テーブル追加</button>
<br>
クリップボードからイメージをbase64で張り付けている。
<!-- Create the editor container -->
<div id="editor">
<p>Hello World!</p>
</div>
<script>
Quill.register({
'modules/tableUI': quillTableUI.default
}, true);
const quill = new Quill('#editor', {
theme: 'snow',
modules: {
table: true,
tableUI: true,
}
});
document.getElementById('btnContent').addEventListener('click', function() {
console.log(quill.getContents());
});
document.getElementById('btnTable').addEventListener('click', function() {
let table = quill.getModule('table');
console.log(table);
table.insertTable(3, 3);
});
</script>
</body>
</html>
メモ
2019/11/28時点の最終リリースはバージョン1.3.7です。
2.0の開発が進められていますが、そのマイルストーンは不透明なものとなっています。
https://github.com/quilljs/quill/issues/2435
moduleを実装することで拡張機能が作れる模様。
trix
GitHub
https://github.com/basecamp/trix
対象ブラウザ
IE 11以降をサポートしているようです。
https://github.com/basecamp/trix/issues/173
ライセンス
MIT License
サンプル
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test Trix</title>
<link rel="stylesheet" type="text/css" href="trix.css">
<script type="text/javascript" src="trix.js"></script>
</head>
<body>
<!-- Create the editor container -->
<trix-editor class="trix-content">サンプル</trix-editor>
<button id="btnContent">コンテンツ取得</button>
<button id="btnSave">セーブ</button>
<button id="btnLoad">ロード</button>
<script>
document.getElementById('btnContent').addEventListener('click', function() {
var element = document.querySelector("trix-editor");
console.log(element.editor.getDocument());
console.log(element.editor.getDocument().toString());
});
// エディタの内容はJSON化して保存と読み込みが可能
document.getElementById('btnSave').addEventListener('click', function() {
var element = document.querySelector("trix-editor");
localStorage["editorState"] = JSON.stringify(element.editor);
});
document.getElementById('btnLoad').addEventListener('click', function() {
var element = document.querySelector("trix-editor");
element.editor.loadJSON(JSON.parse(localStorage["editorState"]));
});
// イベントの確認
addEventListener("trix-attachment-add", function(event) {
// 添付ファイルや画像を追加するとこのイベントが実行される
// 以下のコードを参考にfileuploadとかができそう
// https://trix-editor.org/js/attachments.js
console.log('trix-attachment-add');
console.log(event.attachment);
});
addEventListener("trix-attachment-remove", function(event) {
// 添付ファイルや画像を削除するとこのイベントが実行される
console.log('trix-attachment-remove');
console.log(event.attachment);
});
addEventListener("trix-change", function(event) {
// 内容が変化した場合実行
console.log('trix-change');
console.log(event);
});
</script>
</body>
</html>
メモ
学習コストは低いと思われる。
※最低限の動作確認はtrix-editorタグを作ってtrix.jsを読み込むだけでいい。
テーブルをサポートする予定はない。
https://github.com/basecamp/trix/issues/539
拡張とかはできなさそう。
MediumEditor
medium.comインラインエディターツールバーのクローン
GitHub
https://github.com/yabwe/medium-editor
Demo
http://yabwe.github.io/medium-editor/
画像の貼り付けやコードブロックはなさそう。
ドキュメント
https://github.com/yabwe/medium-editor/wiki
ライセンス
MIT
サンプル
単純な例
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test Trix</title>
<script src="http://cdn.jsdelivr.net/npm/medium-editor@latest/dist/js/medium-editor.min.js"></script>
<link rel="stylesheet" href="http://cdn.jsdelivr.net/npm/medium-editor@latest/dist/css/medium-editor.min.css" type="text/css" media="screen" charset="utf-8">
</head>
<body>
<div class="editable"></div>
<button id="btnSave">セーブ</button>
<button id="btnLoad">ロード</button>
<script>
var editor = new MediumEditor('.editable', {
placeholder: {
text: 'テキストを入力してください',
hideOnClick: true
},
toolbar: {
/* These are the default options for the toolbar,
if nothing is passed this is what is used */
allowMultiParagraphSelection: true,
buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote'],
diffLeft: 0,
diffTop: -10,
firstButtonClass: 'medium-editor-button-first',
lastButtonClass: 'medium-editor-button-last',
relativeContainer: null,
standardizeSelectionStart: false,
static: false,
/* options which only apply when static is true */
align: 'center',
sticky: false,
updateOnEmptySelection: false
}
});
document.getElementById('btnSave').addEventListener('click', function() {
console.log(editor.getContent());
localStorage["medium"] = editor.getContent();
});
document.getElementById('btnLoad').addEventListener('click', function() {
console.log(editor.getContent());
editor.setContent(localStorage["medium"]);
});
</script>
</body>
</html>
MediumEditor Tables
テーブルの作成を行うプラグインです。
Jqueryに依存しています。
GitHub
https://github.com/yabwe/medium-editor-tables
demo
https://yabwe.github.io/medium-editor-tables/
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test Medium</title>
<link rel="stylesheet" href="http://cdn.jsdelivr.net/npm/medium-editor@latest/dist/css/medium-editor.min.css" type="text/css" media="screen" charset="utf-8">
<!-- medium-editor-tables.js が使用している -->
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="http://cdn.jsdelivr.net/npm/medium-editor@latest/dist/js/medium-editor.min.js"></script>
<script type="text/javascript" src="lib/js/medium-editor-tables.js"></script>
<link rel="stylesheet" href="lib/css/medium-editor-tables.css" />
</head>
<body>
<div class="editable"></div>
<script>
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: [
'bold',
'italic',
'table'
]
},
extensions: {
table: new MediumEditorTable()
}
});
</script>
</body>
</html>
jQuery insert plugin for MediumEditor
画像やYoutubeやTwitterなどの埋め込みが可能なプラグインです。
Jqueryに依存します。
GitHub
https://github.com/orthes/medium-editor-insert-plugin
demo
https://linkesch.com/medium-editor-insert-plugin/
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test Medium</title>
<link href="http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/medium-editor-insert-plugin/2.5.0/css/medium-editor-insert-plugin-frontend.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/medium-editor-insert-plugin/2.5.0/css/medium-editor-insert-plugin.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/medium-editor/5.23.3/css/medium-editor.min.css" />
<!-- medium-editor-tables.js が使用している -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.12/handlebars.runtime.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-sortable/0.9.13/jquery-sortable-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery.ui.widget@1.10.3/jquery.ui.widget.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.iframe-transport/1.0.1/jquery.iframe-transport.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-file-upload/9.28.0/js/jquery.fileupload.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/medium-editor/5.23.3/js/medium-editor.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/medium-editor-insert-plugin/2.5.0/js/medium-editor-insert-plugin.min.js"></script>
</head>
<body>
<div class="editable"></div>
<button id="btnSave">セーブ</button>
<button id="btnLoad">ロード</button>
<script>
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: [
'bold',
'italic',
'table'
]
}
});
$('.editable').mediumInsert({
editor: editor
});
document.getElementById('btnSave').addEventListener('click', function() {
console.log(editor.getContent());
localStorage["medium"] = editor.getContent();
});
document.getElementById('btnLoad').addEventListener('click', function() {
console.log(editor.getContent());
editor.setContent(localStorage["medium"]);
});
</script>
</body>
</html>
##メモ
Medium Editorのライブラリ自体はJavaScriptのみで外部のライブラリに依存していない。
しかし、その拡張機能がJQueryに依存している。
任意の拡張機能が作成可能。
https://github.com/yabwe/medium-editor/blob/master/src/js/extensions/README.md
Pell
もっともサイズの小さいWYSIWYGライブラリで他のライブラリに依存しません。
GitHub
https://github.com/jaredreich/pell
Demo
https://jaredreich.com/pell/
画像はURL指定して表示。
Link等でダイアログを表示する際はブラウザのメッセージボックスを使用している
ライセンス
MIT
メモ
軽量であるのが売り。
テーブル機能はなさそう。
また拡張機能等はなさそう。
Editor.js
GitHub
https://github.com/codex-team/editor.js
Demo
https://editorjs.io/
・表、画像のアップロードをサポートしている。
・ツールバーは表示されずに、必要な時にポップアップが出る
IEは対象外の模様
※すくなくともデモサイトはIE11で動作しない
ドキュメント
https://github.com/codex-team/editor.js/tree/bcdfcdadbc444921aee62b38516329cda3c96a70/docs
ライセンス
Apache License 2.0
寄付を受け付けている
https://opencollective.com/editorjs
サンプル
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Editor.js example</title>
<link href="https://fonts.googleapis.com/css?family=PT+Mono" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
</head>
<body>
<div class="ce-example">
<div class="ce-example__content _ce-example__content--small">
<div id="editorjs"></div>
<button id="saveButton">
editor.save()
</button>
<button id="loadButton">
editor.load()
</button>
</div>
</div>
<!-- Load Tools -->
<!--
You can upload Tools to your project's directory and use as in example below.
Also you can load each Tool from CDN or use NPM/Yarn packages.
Read more in Tool's README file. For example:
https://github.com/editor-js/header#installation
-->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/header@latest"></script><!-- Header -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/simple-image@latest"></script><!-- Image -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/delimiter@latest"></script><!-- Delimiter -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/list@latest"></script><!-- List -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/checklist@latest"></script><!-- Checklist -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/quote@latest"></script><!-- Quote -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/code@latest"></script><!-- Code -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/embed@latest"></script><!-- Embed -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/table@latest"></script><!-- Table -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/link@latest"></script><!-- Link -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/warning@latest"></script><!-- Warning -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/marker@latest"></script><!-- Marker -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/inline-code@latest"></script><!-- Inline Code -->
<!-- Load Editor.js's Core -->
<script src="./dist/editor.js"></script>
<!-- Initialization -->
<script>
/**
* To initialize the Editor, create a new instance with configuration object
* @see docs/installation.md for mode details
*/
var initObj = {
/**
* Wrapper of Editor
*/
holder: 'editorjs',
/**
* Tools list
*/
tools: {
/**
* Each Tool is a Plugin. Pass them via 'class' option with necessary settings {@link docs/tools.md}
*/
header: {
class: Header,
inlineToolbar: ['link'],
config: {
placeholder: 'Header'
},
shortcut: 'CMD+SHIFT+H'
},
/**
* Or pass class directly without any configuration
*/
image: {
class: SimpleImage,
inlineToolbar: ['link'],
},
list: {
class: List,
inlineToolbar: true,
shortcut: 'CMD+SHIFT+L'
},
checklist: {
class: Checklist,
inlineToolbar: true,
},
quote: {
class: Quote,
inlineToolbar: true,
config: {
quotePlaceholder: 'Enter a quote',
captionPlaceholder: 'Quote\'s author',
},
shortcut: 'CMD+SHIFT+O'
},
warning: Warning,
marker: {
class: Marker,
shortcut: 'CMD+SHIFT+M'
},
code: {
class: CodeTool,
shortcut: 'CMD+SHIFT+C'
},
delimiter: Delimiter,
inlineCode: {
class: InlineCode,
shortcut: 'CMD+SHIFT+C'
},
linkTool: LinkTool,
embed: Embed,
table: {
class: Table,
inlineToolbar: true,
shortcut: 'CMD+ALT+T'
},
},
/**
* This Tool will be used as default
*/
// initialBlock: 'paragraph',
/**
* Initial Editor data
*/
data: {
},
onReady: function(){
},
onChange: function() {
console.log('something changed');
}
};
var editor = new EditorJS(initObj);
/**
* Saving example
*/
const saveButton = document.getElementById('saveButton');
const loadButton = document.getElementById('loadButton');
saveButton.addEventListener('click', function () {
editor.save().then((savedData) => {
console.log(savedData);
localStorage["editJs"] = JSON.stringify(savedData);
});
});
loadButton.addEventListener('click', function () {
let data = JSON.parse(localStorage["editJs"]);
console.log(data);
editor.render(data);
});
</script>
</body>
</html>
メモ
IEは動作しない
undo機能は2019/11/28時点では自前でやる必要があるっぽい。
https://github.com/codex-team/editor.js/issues/518
CKEditor5
Gitのスター順で並べると上位5位にでてきませんが、バージョンごとにプロジェクトが分かれているっぽいので累計すると、結構使われているように見えます。
公式ページを見ると累計で27.500.000+のダウンロードが行われているそうです。
GitHub
https://github.com/ckeditor/ckeditor5
Demo
https://ckeditor.com/ckeditor-5/demo/
ライセンス
GNU General Public License Version 2 or later.
商用ライセンスがある。
https://ckeditor.com/pricing/#null
メモ
この中ではデモが一番、使い易かったので、金が豊富にあるならコレがよさそう。
まとめ
色々調べましたが、結局、一長一短ある感じがします。
その上で個人的にはコードのハイライトが簡単に使えそうなQuillか、学習コストの低そうなtrixがよさそうに見えます。