SummernoteのAPIを使ってセーブ/ロード機能を実装する

前回カスタマイズしたSummernoteを使ってセーブ/ロード機能を持つ画面を作成してみる。
Summernoteにはエディタを操作できるAPIがあり、codeというAPIを使えばエディタの入力内容を取得/設定することができる。

取得方法

JavaScript
const contents = $('#note').summernote('code');
console.log(contents);

設定方法

JavaScript
$('#note').summernote('code', '<p>Hello world</p>');

このAPIを使ってローカルストレージから編集内容をセーブ/ロードする機能を追加してみた。
また、ついでにプレビュー機能も作成してみた。

動作確認環境

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

実装例

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 mt-3 mb-3">
      <div class="d-flex gap-5">
        <button id="btnLoad" class="btn btn-primary" style="min-width:120px">ロード</button>
        <button id="btnSave" class="btn btn-danger" style="min-width:120px">セーブ</button>
      </div>
    </div>
    <div class="d-flex justify-content-center mb-3">
      <textarea id="note" class="summernote" style="display: none" value=""></textarea>
    </div>
    <!-- プレビュー領域 -->
    <div class="d-flex justify-content-center">
      <div class="d-flex flex-wrap" style="width:1024px">
        <div class="w-100"><h2>プレビュー</h2></div>
        <div id="preview" class="p-2 w-100" style="border:solid 1px;"></div>
      </div>
    </div>

    <script type="text/javascript">
      $(function () {
        const storageKey = 'note-data';
        let timeoutId = '';

        // ローカルストレージ対応ブラウザチェック
        if(typeof localStorage === 'undefined') {
          alert('ブラウザがローカルストレージに対応していないため、ロード/セーブ機能は利用できません。')
          $("#btnLoad").prop("disabled", true);
          $("#btnSave").prop("disabled", true);
        }

        // Summernote初期化
        $('#note').initSummernote({
          height: 400,
          width: 1024,
          focus: true,
          callbacks: {
            onChange: function(contents, $editable) {
              // プレビュー欄に変更を反映させる(0.5秒後)
              clearTimeout(timeoutId);
              timeoutId = setTimeout(() => {
                $('#preview').html(contents);
              }, 0.5 * 1000);
            },
          }
        });

        // ロードボタン押下時
        $('#btnLoad').click(function() {
          if (!localStorage.hasOwnProperty(storageKey)) {
            alert('記事が保存されていません。');
            return;
          }

          // 記事を取得しエディタにセットする
          const contents = localStorage.getItem(storageKey);
          if (contents)
            $('#note').summernote('code', contents);
        });

        // セーブボタン押下時
        $('#btnSave').click(function() {
          try {
            // summernoteで編集した記事を取得
            const contents = $('#note').summernote('code');
            // ローカルストレージに記事を保存
            localStorage.setItem(storageKey, contents);
            alert('記事を保存しました。');
          } catch(err) {
            console.error(err);
            alert('記事の保存に失敗しました。');
          }
        });
      });
    </script>
  </body>
</html>

セーブ/ロード機能、プレビュー機能共に簡単に実装可能だった。
プレビュー機能はonChange毎にDOMを差し替えるのは流石に重くなりそうだったのでonChangeが走った後、0.5秒の間に再度onChangeが走らなければDOMを差し替えるように実装してみた。

実装結果

Summernoteにはcode以外にも便利なAPIが多数用意されているので公式サイト参照のこと。

コメント