Help us understand the problem. What is going on with this article?

CKEditorをカスタマイズする

More than 1 year has passed since last update.

リッチな WYSIWYG エディタで CKEditor をカスタマイズする。

前提条件

  • Rails 5.x
  • CKEditor の v4.2.4 を使用

以下、JavaScriptの処理は Rails に限らず使用できる。

不要なボタンを消す

デフォルトの editorConfig を上書きすることで表示項目をカスタマイズできる。

次の例は最低限 WYSIWYG に必要な機能だけ残し、エディタの高さを 380px 固定にした例。

app/assets/javascripts/ckeditor/config.js
CKEDITOR.editorConfig = function (config) {
    config.toolbar_mini = [
        {
            name: 'paragraph',
            groups: ['list', 'indent', 'blocks', 'align', 'bidi'],
            items: [
                'NumberedList', 'BulletedList',
                '-',
                'Outdent', 'Indent',
                '-',
                'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'
            ]
        },
        {
            name: 'links',
            items: [
                'Link', 'Unlink'
            ]
        },
        {
            name: 'basicstyles',
            groups: ['basicstyles', 'cleanup'],
            items: [
                'Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript',
                '-',
                'RemoveFormat'
            ]
        },
        {
            name: 'insert',
            items: [
                'Image', 'Table', 'HorizontalRule'
            ]
        }
    ];

    // toolbar_mini が読み込まれる
    config.toolbar = "mini";

    // 高さを指定
    config.height = '380px';
};

image.png

i18n

locale ファイルを追加することでボタンなどの表示を i18n 対応できる。
しかし locale ファイルが適用されない箇所もあり、それは後述する。

config/locales/ja.ckeditor.yml
ja:
  ckeditor:
    confirm_delete: 'ファイルを削除してもよろしいですか?'
    buttons:
      cancel: 'キャンセル'
      upload: 'アップロード'
      delete: '削除'
      next: '次へ'
    dialog:
      title: '画像'

リンク追加ダイアログをカスタマイズ

Link ボタンをクリックしたときに表示されるダイアログをカスタマイズする。
ダイアログはボタンがクリックされたときに JavaScript で生成される。その生成を hook する dialogDefinition イベントにコールバックを設定し動的にカスタマイズする必要がある。

image.png

ダイアログを取得し、そのタブ内の任意のフィールドを書き換えている。.getContents で要素を取得したり、 .removeContents で不要な要素を削除できる。
日本語をハードコーディングしているところは、i18n-jsとか使って i18n 対応した方がよい。

app/assets/javascripts/ckeditor/config.js
CKEDITOR.on('dialogDefinition', function (ev) {
    var dialogName = ev.data.name;
    var dialogDefinition = ev.data.definition;

    if (dialogName === 'link') {
        var infoTab = dialogDefinition.getContents('info');
        if (infoTab) {
            var urlField = infoTab.get('url');
            urlField['default'] = 'www.example.com';
        }

        if (dialogDefinition.getContents('advanced')) dialogDefinition.removeContents('advanced');
    }
}

画像選択ダイアログをカスタマイズ

リンクと同様に JavaScript でカスタマイズする。

image.png

app/assets/javascripts/ckeditor/config.js
CKEDITOR.on('dialogDefinition', function (ev) {
    var dialogName = ev.data.name;
    var dialogDefinition = ev.data.definition;

    •••

    if (dialogName === 'image') {
        var uploadTab = dialogDefinition.getContents('Upload');
        if (uploadTab) {
            var upload = uploadTab.get('upload');
            if (upload) upload.label = '画像を選択してください';

            var uploadButton = uploadTab.get('uploadButton');
            if (uploadButton) uploadButton.label = 'アップロード';
        }

        var infoTab = dialogDefinition.getContents('info');
        if (infoTab) {
            infoTab.remove('txtAlt');
            infoTab.get('txtUrl')['hidden'] = true;
            infoTab.remove('txtHSpace');
            infoTab.remove('txtVSpace');
            infoTab.remove('txtBorder');
            infoTab.remove('cmbAlign');

            var browse = infoTab.get('browse');
            if (browse) browse.label = 'アップロード済みの画像を選択';
        }

        // remove unnecessary tabs
        if (dialogDefinition.getContents('Link')) dialogDefinition.removeContents('Link');
        if (dialogDefinition.getContents('advanced')) dialogDefinition.removeContents('advanced');
    }
});

画像のサイズ指定方法を変更

デフォルトだと画像のサイズ指定が、インラインの style タグで記述される。
<img>height width 属性で指定したい場合は次の設定をする。

app/assets/javascripts/ckeditor/config.js
CKEDITOR.editorConfig = function (config) {
    •••

    // 画像のサイズ指定をstyleタグではなく,width,height属性で設定
    config.disallowedContent = 'img{width,height}';
    config.extraAllowedContent = 'img[width,height]';
};

Turbolinks と組み合わせる

turbolinks:load イベント内で、<%= f.cktext_area :content %> によってレンダリングされた全ての HTML を初期化する。

app/assets/javascripts/ckeditor/index.js
//= require ckeditor/init.js.erb
//= require ./config

$(document).on('turbolinks:load', function () {
    $('.ckeditor').each(function () {
        CKEDITOR.replace($(this).attr('id'))
    });
});

Form の Dirty Check をする

CKEditor の form と、それ以外を両方 Dirty Check するコードを示す。

まず CKEDITORhasDirtyForm メソッドを追加する。

app/assets/javascripts/ckeditor/index.js
//= require ckeditor/init.js.erb
//= require ./config

CKEDITOR.hasDirtyForm = function () {
    var editors = this.instances;
    return Object.keys(editors).some(function (key) {
        return editors[key].checkDirty();
    });
};

turbolinks:load で Dirty Check を初期化する。

Form が Dirty で、まだ Submit されていない場合にのみ confirm が表示されるようになる。

app/assets/javascripts/dirty-forms.js
$(window)
    .on('turbolinks:load', function () {
        if (!window.app) window.app = {};
        window.app.formDirty = false;
        window.app.formSubmit = false;
        window.app.dirtyMessage = '保存していない内容が失われますが、よろしいですか';

        $(':input').change(function () {
            if (!window.app.formDirty) window.app.formDirty = true;
        });
        $('form').submit(function () {
            window.app.formSubmit = true;
        });
    })
    .on("page:before-change turbolinks:before-visit", function () {
        if (window.app.formDirty || CKEDITOR.hasDirtyForm()) {
            return confirm(window.app.dirtyMessage);
        }
    })
    .bind("beforeunload", function (event) {
        if (!window.app.formSubmit &&
            (window.app.formDirty || CKEDITOR.hasDirtyForm())) {
            event.returnValue = window.app.dirtyMessage;
            return event.returnValue;
        }
    });
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away