1. ホーム
  2. Web制作
  3. HTML/Xhtml

QuillエディタにカスタムHTMLレコードを挿入する例

2022-01-07 13:07:54

2020年になり、のどが渇いた人間は、もはや単純なテキストでは満足しないので、テキストの様々なスタイルを運ぶ空想がある、しかし、テキストだけでは十分ではありません、我々はより見栄えのソフトコピーを送信するように、編集時にユーザーがカスタムメッセージタイプの様々を挿入できるようにする必要があり、この記事があるように。

前置き

Quillエディタに付属するリッチテキストのフィルタリング(主要なエディタのほとんどがリッチテキストに対して行っている)は、カスタムHTMLテンプレートを構成しようとする開発者に多くの問題を引き起こします。

I. Quillのレンダリングロジックの解析

QuillでHTMLブロックの内容をカスタマイズするためには、まずQuill内部のレンダリング処理を理解する必要があり、ここではいくつかの重要な概念を説明します。

1. デルタ

Deltaは、Quillが内部で定義したデータフォーマットで、文書の内容や文書の変更操作を見やすくシンプルに表現し、文書の内容をDeltaの形で保持し、HTMLの内容とDeltaを相互に変換できるようにしたものである。

一例として

すると、リッチテキストは次のように表現されます。

{  
"ops":[ 
{"insert":"this is a simple text.\\\nbut when "}, 
{"attributes":{"bold":true},"insert":"it is "}, 
{"insert":"not bold.\\nlet me try "}, {"insert":"not bold.\nlet me try "}, 
{"attributes":{"italic":true},"insert":"italic "}, 
{"insert":"haha\\nwhat about "}, {"attributes": 
{"italic":true,"bold":true},"insert":"both"}, 
{"insert":" ? \\n"}]  
}"

プレーンテキストは、個々の挿入アクションとして定義され、それぞれがデルタを表し、すべてがテキストの内容を記述しています。

同様に、変更と削除を行った場合、対応するデルタが生成され、新たに生成された変更デルタは元のデルタとマージされ、新しいデルタが作成されます。(デルタには、挿入、削除、保持の3つのアクションがあります)。

最初の10文字を残し、次の20文字を太字にするデルタは次のようになります。

{
  "ops": [
    { "require": },
    { "require": , "attributes": { "bold": } }
  ]
}

最初の10文字を残し、次の20文字に対して以下のように削除操作を行う。

{
  "ops": [
    { "retain": },
    { "delete": }
  ]
}

2. パーチメント

Parchmentは、Blotを管理する抽象的な文書モデルです。

Parchmentを完全なDOMツリー構造と考えると、Blotはその中の1つのノードと言えます。そしてBlotはQuillのデフォルトを超えて、カスタマイズすることができ、より拡張の余地を与えてくれるのです。

3. ブロート

Blot は Parchment ドキュメントに不可欠なもので、DOM ノードタイプの抽象化に相当し、具体的な Blot インスタンスはまだその中に他のノード情報を持っています。

グローバルルートBlotは、Quillが内部でカスタマイズしたScrollタイプのBlotで、その下のBlotをすべて管理します。

Blotの実装の定義については、こちらをご覧ください: https://github.com/quilljs/parchment#blots

QuillにおけるBlotのデフォルトの定義は以下の通りです。

一般的には、TextBlot(行内プレーンテキスト)、Inline(行内スタイルを持つプレーンテキスト)、Block(ブロックレベルの行、通常はパラグラフp内)、Break(改行)、Image(画像IMG挿入)、Bold(太字テキスト)などが挙げられます。

そして、どのようにブロットを構築するためのHTMLの一部?クイルノードタイプでは、テキストノードのノード優先排除に基づいて、それが要素ノードであれば、再び決定するためにノードのクラス名に基づいて、まだ一致BlotNameを見つけることができない場合は、デフォルトは対応BlotClassを見つけるために次のマッピング関係を一致させることができます。

4. デルタの実用的な意味

コンテンツの構造を表現するBlotがすでにあるのに、なぜDeltaが必要なのでしょうか?HTMLのデータソースとして使用しないのであれば、Deltaデータのコピーを保持する意味はあるのでしょうか?

HTML = >デルタ、そしてデルタ = >HTMLが存在しないとしたら、デルタのコピーを常に保持する意味はあるのでしょうか。

1. deltaからのHTML生成は実際に存在しますが、適用シーンはドキュメントの初期化に限定されます。Quillは、対応するデルタを生成するために、入ってくる初期化されたHTML文字列を解析し、そして、applyDeltaによってページに戻るDOMノードを生成します。

2、ここで参照してくださいあなたは満足していないかもしれません、なぜ我々はプロセスのこの手順を通過する必要があります、直接文字列の初期化 document.getElementById('container').innerHTML = valはできません、はい、あなたはできますが、デルタの存在は、ユーザーの文書は、より細かい粒子になる保守しやすく、追跡可能になるようにします。AとBが同時に文書を編集していて、Aが2行目の10文字を削除した場合、文書の内容を全面的に更新するのではなく、Aは自身の動作を同期させるアクションを送信するだけでよく、Bは競合処理後にマージするだけでよいのです。デルタの整備によりロジックはかなり複雑になりますが、その存在により文書の拡張の可能性も広がります。

5. エディタのレンダリングと更新フロー

コンテンツに変更を加えるには、3つの方法があります。

1. エディタコンテンツを初期化する。quill.pasteHTMLを呼び出して初期化し、HTMLでフィルタリングしてエディタボックスにパースして戻します。

2. 入力イベント:ユーザー入力と編集操作を聞き取り、MutationObserverで処理し、デルタを更新します。

3. APIコール:内部で提供されるAPIをmodifyメソッドで呼び出し、グローバルスクロールインスタンスのメソッドを呼び出して変更する。

II. カスタムHTMLブロックの挿入

記事の内容が多様化し、地図や音楽プレーヤー、広告パネルなどを記事に挿入する必要が出てきたため、リッチテキストエディタの機能を拡張する必要が出てきたのです。しかし、攻撃に対するxssの防御をしっかりすることも重要です。

Part 1で説明したように、Quillが認識できるカスタムHTMLブロックを挿入する必要があります。おわかりのように、Blotをカスタマイズする必要があります。これは、初期化時にQuillがHTMLブロック表示を認識し、またQuillが汚いHTMLフィルタリングを行わずにHTMLブロックを挿入できる方法で定義されています。

Blotメソッドを以下のように登録します。

export default function (Quill) {
  // Introduce BlockEmbed in the source code
  const BlockEmbed = Quill.import('blots/block/embed');
  // Define a new blot type
  class AppPanelEmbed extends BlockEmbed {
    static create(value) {
      const node = super.create(value);
      node.setAttribute('contenteditable', 'false');
      node.setAttribute('width', '100%');
      // Set custom html
      node.innerHTML = this.transformValue(value)
      return node;
    }

    static transformValue(value) {
      let handleArr = value.split('\n')
      handleArr = handleArr.map(e => e.replace(/^[\s]+/, '')
        .replace(/[\s]+$/, ''))
      return handleArr.join('')
    }

    // return the value of the node itself for undo operations
    static value(node) {
      return node.innerHTML
    }
  }
  // blotName
  AppPanelEmbed.blotName = 'AppPanelEmbed';
  // class name will be used to match the blot name
  AppPanelEmbed.className = 'embed-innerApp';
  // Label type customization
  AppPanelEmbed.tagName = 'div';
  Quill.register(AppPanelEmbed, true);
}

次に、このように呼び出すだけで、エディタにカスタムHTMLブロックを挿入することができます。

quill.insertEmbed(quill.getSelection().index || 0, 'AppPanelEmbed', `
          <div class="app_card_header">     
              Custom Panel Header
          </div>
          <div class="app_card_content">     
              Custom Panel Content
          </div>
          <div class="app_card_footer">     
              footer
          </div>
      `);

リファレンスを渡す際の書式条件は以下の通りです。

insertEmbed(index: Number, type: String, value: any, source: String \= 'api'): Delta

これは単なる例であり、カスタムBlotの機能を充実させたい場合は、https://github.com/quilljs/parchment#blots を参照することができます。

contenteditable属性は自由化されているため、xss攻撃を引き起こさないように、この属性に対して特別なフィルタリングを行う必要があります。ここでは、xssモジュールの処理例を示します。

handleWithXss(content) {
      const options = {
        whiteList: {
         ...
          div: ['class', 'style', 'data-id', 'contenteditable'],
         ...
        },
        css: {
          whiteList: {
            color: true,
            'background-color': true,
            'max-width': true,
          },
        },
        stripIgnoreTag: true,
        onTagAttr: (tag, name, value, isWhiteAttr) => {
          // contenteditable handling for divs
          if (isWhiteAttr && tag === 'div' && name === 'contenteditable') {
            return 'contenteditable="false"';
          }
        },
      } // Custom rules
      const myxss = new xss.FilterXSS(options)
      return myxss.process(content)
    }

Quill EditorのカスタムHTMLレコードの挿入については、この記事がすべてです。Quill EditorのカスタムHTMLに関するより多くのコンテンツは、Script Houseの過去の記事を検索するか、以下の関連記事を参照してください、今後ともScript Houseをよろしくお願いします