CKEditor5で先頭にinsertContentした後に選択範囲を取り直す方法

掲載日

結論

insertContentした後に、範囲を取り直すだけ。

const model = this.editor.model;

model.change((writer) => {
    let range = this.editor.model.document.selection.getFirstRange()
    
    // 前に追加したいElementを作成
    let iconImage = writer.createElement('imageInline', {
        "src": "任意の画像パス",
        "alt": "任意の画像代替文字",
        htmlImgAttributes: {
            classes: ["任意のクラス"],
        },
        // 他必要な属性等
    });
    // 先頭に追加
    model.insertContent(iconImage, range.start)

    // アイコン分範囲拡張しているので、範囲を取り直す
    range = this.editor.model.document.selection.getFirstRange()
    
    // 後ろに追加したいElementを作成
    let outerIconImage = writer.createElement('imageInline', {
        "src": "任意の画像パス",
        "alt": "任意の画像代替文字",
        htmlImgAttributes: {
            classes: ["任意のクラス"],
        },
        // 他必要な属性等
    });
    // 後ろに追加
    model.insertContent(outerIconImage, range.end)
})

経緯

CKEditor5ではデフォルトでイメージやファイルアップロードのプラグインが用意されていますが、
基本的にはファイルをアップロードしてリンクを付与するシンプルなものです。

今回、「ファイルをアップロードすると、リンクの先頭にそのファイルの拡張子に対応するアイコンを付与し、ファイルの末尾には別タブを開くアイコンを付与したい」という要件があり、それに対応しようとした際に選択範囲の取得で詰まったので解決の備忘録になります。

選択範囲をベースに前後に内容を追加。

まず、選択範囲を基に前後に内容を追加する場合、this.editor.model.document.selectionを使って現在の選択範囲を取得します。
selection.getFirstPosition();で先頭、selection.getLastPosition();で最後尾の位置を取得出来るので、model.insertContent(iconImage, firstPosition)で選択している文章の先頭にコンテンツを追加することが可能です。(firstPositionlastPositionに変えれば選択している文章の最後尾。)

先頭に内容を追加すると、初期の選択範囲がズレる。

ここで問題が発生します。

最初のアイコンを付与すると、先頭に1個分の位置ズレが発生するため、getLastPositionで末尾に文章を付与した際に内容の追加位置がおかしくなります。(一文字分ズレるなど)

解決までの試行錯誤

LastPositionの値をいじる

Positionの値は配列になっています。
シンプルなpタグの中の文章であれば、以下のようになっているため、単純に最後の値をインクリメントすればいけるかーと思ってしまいました。

[0,2], // 0行目の2文字目
[1,5] // 1行目の5文字目

この方法だと、複雑な構造(テーブルタグの中身等)では配列が複雑になって最後の値を増やすだとエラーが発生するのでNGでした。

最後尾をinsertしてから先頭にinsert

シンプルですが、これはOKだったので最初はこれで行こうとしてました。
ただし、結局範囲が取得できないのは今後別の要件で躓きそうだったのでその後も調査。
(例えば追加した画像も含めてaタグで囲いたい等あった場合に大変そう。)

改めて選択範囲を取得し直す

考えてみれば当たり前(変数とスコープの超初歩的な問題)なのですが、大本の範囲を取得し直せばちゃんと位置が更新されています。

よって、選択範囲より前の位置にinsertContentした場合は、再度範囲を取得すればちゃんと位置ずれせずにコンテンツを後ろに追加できました。

おわりに

CKEditor5ははじめて一年くらいたちますがまだまだ慣れないです。

情報が少ないのか生成AIも役に立ちません。

LiveRangeクラス使え(もちろんそんなクラスは存在しません)と言われて血管千切れかけました。

記事の作成者のA.W.のアイコン

この記事を書いた人

A.W.
茨城県在住Webエンジニアです。 PHP、PostgreSQL、Linuxなどを業務で使用しています。 趣味ではGoやNuxt、Flutterをやってます。

Comment