Summernoteをカスタマイズして使う

Summernoteは設定を変更することで色々とカスタマイズできるようだ。
自分好みの設定にカスタマイズしてみた。

動作確認環境

  • jQuery(3.7.1)
  • Bootstrap(5.3.5)
  • Summernote(0.9.1)

カスタマイズ前の状態

前回のBootstrap5.3に対応させた以下のソースをベースにカスタマイズしていく。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>Summernoteカスタマイズ</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" type="text/css" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.css" type="text/css" />
    <link rel="stylesheet" href="./css/app.css" type="text/css" />
    <link rel="stylesheet" href="./css/summernote-bs5-editor-custom.css" type="text/css" />

    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.bundle.min.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.js" type="text/javascript"></script>
  </head>
  <body>
    <div class="d-flex justify-content-center">
      <textarea id="note" class="summernote" style="display: none" value=""></textarea>
    </div>

    <script type="text/javascript">
      $(function () {
        $('#note').summernote({
          height: 400,
        });
        // ファイル選択をBootstrapのレイアウトにする
        $('.note-modal .form-control-file').each(function () {
          $(this).addClass('form-control').removeClass('form-control-file');
        });
      });
    </script>
  </body>
</html>
css/app.css
p {
  margin-bottom: 0;
}
css/summernote-bs5-editor-custom.css
/* popoverの表示位置修正 */
.note-popover {
  position: absolute !important;
}
/* popoverの矢印ズレ調整 */
.note-popover > .popover-arrow::before {
  top: -7px !important;
}
.note-popover > .popover-arrow::after {
  top: -6px !important;
}

日本語化する

日本語の定義ファイルを追加で読み込み、langプロパティに’ja-JP’を指定するだけで日本語化できるため簡単だった。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>Summernoteカスタマイズ</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" type="text/css" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.css" type="text/css" />
    <link rel="stylesheet" href="./css/app.css" type="text/css" />
    <link rel="stylesheet" href="./css/summernote-bs5-editor-custom.css" type="text/css" />

    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.bundle.min.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/lang/summernote-ja-JP.min.js" type="text/javascript"></script>
  </head>
  <body>
    <div class="d-flex justify-content-center">
      <textarea id="note" class="summernote" style="display: none" value=""></textarea>
    </div>

    <script type="text/javascript">
      $(function () {
        $('#note').summernote({
          lang: 'ja-JP',
          height: 400,
        });
        // ファイル選択をBootstrapのレイアウトにする
        $('.note-modal .form-control-file').each(function () {
          $(this).addClass('form-control').removeClass('form-control-file');
        });
      });
    </script>
  </body>
</html>

日本語化後の表示(ダイアログやツールチップなど全体的に日本語化される)

ツールバーの表示項目を変更する

toolbarプロパティに表示したいツールバーを設定することで表示する項目や項目位置を変更可能であった。指定できるツールバーの項目は公式サイトを参照。
また、ツールバーを表示せずポップアップで操作させる「AirMode」なるものもあるようだった。
今回はフォントの種類以外の全項目を表示するようにカスタマイズし、あわせて選択できるフォントサイズをfontSizesプロパティの設定を変更して増やしてみた。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>Summernoteカスタマイズ</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" type="text/css" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.css" type="text/css" />
    <link rel="stylesheet" href="./css/app.css" type="text/css" />
    <link rel="stylesheet" href="./css/summernote-bs5-editor-custom.css" type="text/css" />

    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.bundle.min.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/lang/summernote-ja-JP.min.js" type="text/javascript"></script>
  </head>
  <body>
    <div class="d-flex justify-content-center">
      <textarea id="note" class="summernote" style="display: none" value=""></textarea>
    </div>

    <script type="text/javascript">
      $(function () {
        $('#note').summernote({
          lang: 'ja-JP',
          height: 400,
          fontSizes: ['8', '9', '10', '11', '12', '14', '16', '18', '20', '22', '24', '26', '28', '36', '48', '72'],
          toolbar: [
            ['style', ['style']],
            ['font', ['fontsize', 'fontsizeunit', 'forecolor', 'backcolor']],
            ['fontstyle', ['bold', 'italic', 'strikethrough', 'underline', 'subscript', 'superscript', 'paragraph', 'clear']],
            ['para', ['ul', 'ol']],
            ['table', ['table']],
            ['insert', ['link', 'picture', 'video']],
            ['operation', ['undo', 'redo']],
            ['view', ['codeview', 'fullscreen', 'help']],
          ],
        });
        // ファイル選択をBootstrapのレイアウトにする
        $('.note-modal .form-control-file').each(function () {
          $(this).addClass('form-control').removeClass('form-control-file');
        });
      });
    </script>
  </body>
</html>

ツールチップ変更後の表示

見出しのスタイルを変更する

styleTagsプロパティに見出しの情報を設定することでSummernoteで利用できるようになる。設定可能なプロパティは公式サイトを参照すれば分かるが、オブジェクトで指定する場合の設定項目を備忘録として書いておく。

JavaScript
{
  tag : 'h1',				// ドロップダウンの項目に適用するタグ名
  title : '主見出し',		// ドロップダウンの項目として表示するテキスト
  style : 'margin: 4px 0;',	// ドロップダウンの項目に適用するスタイル
  className : 'headline',	// ドロップダウンの項目と挿入するタグに適用するクラス名
  value : 'h1'				// 挿入するタグのタグ名
}

tag、title、styleはドロップダウン項目のみの設定であり、実際に挿入するタグには影響しないため注意が必要(tagのみ設定し、valueの設定を省くとpタグが挿入される)。
上記を念頭に以下のように設定してカスタマイズする。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>Summernoteカスタマイズ</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" type="text/css" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.css" type="text/css" />
    <link rel="stylesheet" href="./css/app.css" type="text/css" />
    <link rel="stylesheet" href="./css/summernote-bs5-editor-custom.css" type="text/css" />

    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.bundle.min.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/lang/summernote-ja-JP.min.js" type="text/javascript"></script>
  </head>
  <body>
    <div class="d-flex justify-content-center">
      <textarea id="note" class="summernote" style="display: none" value=""></textarea>
    </div>

    <script type="text/javascript">
      $(function () {
        $('#note').summernote({
          lang: 'ja-JP',
          height: 400,
          fontSizes: ['8', '9', '10', '11', '12', '14', '16', '18', '20', '22', '24', '26', '28', '36', '48', '72'],
          toolbar: [
            ['style', ['style']],
            ['font', ['fontsize', 'fontsizeunit', 'forecolor', 'backcolor']],
            ['fontstyle', ['bold', 'italic', 'strikethrough', 'underline', 'subscript', 'superscript', 'paragraph', 'clear']],
            ['para', ['ul', 'ol']],
            ['table', ['table']],
            ['insert', ['link', 'picture', 'video']],
            ['operation', ['undo', 'redo']],
            ['view', ['codeview', 'fullscreen', 'help']],
          ],
          styleTags: [
            {tag: 'h1', title: '主見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h1'},
            {tag: 'h2', title: '見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h2'},
            {tag: 'h3', title: '小見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h3'},
          ],
        });
        // ファイル選択をBootstrapのレイアウトにする
        $('.note-modal .form-control-file').each(function () {
          $(this).addClass('form-control').removeClass('form-control-file');
        });
      });
    </script>
  </body>
</html>
css/app.css
p {
  margin-bottom: 0;
}

/* 見出し共通のスタイル */
.headline {
  font-weight: 700;
  margin: 0 0 20px;
}

/* 主見出し */
h1.headline {
  font-size: 32px;
  padding: 0.3em;
  background-color: #1E73BE;
  color: #fff;
}

/* 見出し */
h2.headline {
  font-size: 26px;
  border-bottom: 4px solid #e3e3e3;
  padding: 0.3em;
  position: relative;
}
h2.headline::after {
  content: '';
  background-color: #1E73BE;
  width: 3em;
  height: 4px;
  position: absolute;
  bottom: -3px;
  left: 0;
  z-index: 1;
}

/* 小見出し */
h3.headline {
  font-size: 20px;
  border-left: 6px solid #1E73BE;
  padding: 0.25em 0 0.3em 0.3em;
}

見出し設定後のスタイル表示

見出し挿入後の表示

Summernoteの初期化処理を共通化して呼び出せるようにする

Summernoteを複数ページで使う場合、毎回パラメータを設定するのは面倒臭いため共通部品化する。
ついでに以下の修正を共通部品に加える。
①日本語未対応の文言を日本語化する。
②モーダルのファイル選択をBootstrap化するタイミングをonInitのCallback内に移動し、今回初期化されたSummernoteのみにスタイルを適用されるようにする。
③②のカスタマイズによりonInitが各画面で設定できなくなるため共通部品側にonInitAfterという独自プロパティの呼び出し処理を追加する。

js/summernote-init.js
(function($) {

  // 日本語未対応の文言を日本語化
  $.extend(true, $.summernote.lang, {
    'ja-JP': {
      font: {
        subscript: '下付き',
        superscript: '上付き',
      },
    },
  });

  // Summernote初期化
  $.fn.initSummernote = function (options) {

    // デフォルトオプション
    const defaultOptions = {
      lang: 'ja-JP',
      fontSizes: ['8', '9', '10', '11', '12', '14', '16', '18', '20', '22', '24', '26', '28', '36', '48', '72'],
      toolbar: [
        ['style', ['style']],
        ['font', ['fontsize', 'fontsizeunit', 'forecolor', 'backcolor']],
        ['fontstyle', ['bold', 'italic', 'strikethrough', 'underline', 'subscript', 'superscript', 'paragraph', 'clear']],
        ['para', ['ul', 'ol']],
        ['table', ['table']],
        ['insert', ['link', 'picture', 'video']],
        ['operation', ['undo', 'redo']],
        ['view', ['codeview', 'fullscreen', 'help']],
      ],
      styleTags: [
        {tag: 'h1', title: '主見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h1'},
        {tag: 'h2', title: '見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h2'},
        {tag: 'h3', title: '小見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h3'},
      ],
      callbacks: {
        onInit: function(note) {
          // ファイル選択をBootstrapのレイアウトにする
          note.editor.find('.note-modal .form-control-file').each(function () {
            $(this).addClass('form-control').removeClass('form-control-file');
          });
          // optionsに拡張プロパティのonInitAfterが設定されてたらコールする
          $(this).data('summernote').options?.callbacks?.onInitAfter?.(note);
        },
      }
    }

    // オプションをマージ
    settings = $.extend(true, {}, defaultOptions, options);

    // Summernoteを初期化
    $(this).summernote(settings);

    return $(this);
  };
})(jQuery);
index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>Summernoteカスタマイズ</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" type="text/css" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.css" type="text/css" />
    <link rel="stylesheet" href="./css/app.css" type="text/css" />
    <link rel="stylesheet" href="./css/summernote-bs5-editor-custom.css" type="text/css" />

    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.bundle.min.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/lang/summernote-ja-JP.min.js" type="text/javascript"></script>
    <script src="./js/summernote-init.js" type="text/javascript"></script>
  </head>
  <body>
    <div class="d-flex justify-content-center">
      <textarea id="note" class="summernote" style="display: none" value=""></textarea>
    </div>

    <script type="text/javascript">
      $(function () {
//       $('#note').summernote({
//         lang: 'ja-JP',
//         height: 400,
//         fontSizes: ['8', '9', '10', '11', '12', '14', '16', '18', '20', '22', '24', '26', '28', '36', '48', '72'],
//         toolbar: [
//           ['style', ['style']],
//           ['font', ['fontsize', 'fontsizeunit', 'forecolor', 'backcolor']],
//           ['fontstyle', ['bold', 'italic', 'strikethrough', 'underline', 'subscript', 'superscript', 'paragraph', 'clear']],
//           ['para', ['ul', 'ol']],
//           ['table', ['table']],
//           ['insert', ['link', 'picture', 'video']],
//           ['operation', ['undo', 'redo']],
//           ['view', ['codeview', 'fullscreen', 'help']],
//         ],
//          styleTags: [
//           {tag: 'h1', title: '主見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h1'},
//           {tag: 'h2', title: '見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h2'},
//           {tag: 'h3', title: '小見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h3'},
//         ],
//       });
//       // ファイル選択をBootstrapのレイアウトにする
//       $('.note-modal .form-control-file').each(function () {
//         $(this).addClass('form-control').removeClass('form-control-file');
//       });
        $('#note').initSummernote({
          height: 400
        });
      });
    </script>
  </body>
</html>

追加したオプションのonInitAfterは以下のようにcallbacksに設定してあげれば利用できる。

JavaScript
$('#note').initSummernote({
  height: 400,
  callbacks: {
    onInitAfter: function(note) {
      console.log(note);
    }
  }
});

カスタマイズ結果

コメント