🐕

GitBucket Markdown Enhanced Plugin でプレビュー時に KaTeX が描画されない問題の対処

に公開

概要

GitBucketのプラグインGitBucket Markdown Enhanced Pluginを開発しています。

GitBucket Markdown Enhanced Plugin では、KaTeX による数式の描画が可能ですが、バージョン 0.3.4 までは、編集時のプレビュー画面で KaTeX による数式の描画が働いていませんでした。

今回は、この問題の解消方法に関する記事です。

前回の記事

https://zenn.dev/yasumichi/articles/7d0b42a9858912

時系列は逆転していますが…

原因

KaTeX による数式の描画は、以下のソースで行っていました。

src\main\resources\gme\assets\gme.js

(function () {
    console.log("gme.js loaded.")

    document.addEventListener("DOMContentLoaded", function () {
        console.log("DOMContentLoaded fire")
        var mathElems = document.getElementsByClassName("katex");
        var elems = [];
        for (const i in mathElems) {
            if (mathElems.hasOwnProperty(i)) elems.push(mathElems[i]);
        }

        elems.forEach(elem => {
            katex.render(elem.textContent, elem, { throwOnError: false, displayMode: elem.nodeName !== 'SPAN', });
        });
    });
})();

DOMContentLoaded イベントをトリガーに KaTeX による数式の描画を行っていましたが、プレビュー表示は、そのイベントよりも後に行われるため、対応できていませんでした。

対処方法の検討

  • プレビューへの切替時、サーバーに markdown が POST され、レンダリング結果が返される。これを捕捉できないか? → 無理そうでした
  • レンダリング結果が返されると #preview のノードに結果が追加される。これを捕捉できないか。 → 方法がありそうだったのでこちらを採用

対処後のソース

src\main\resources\gme\assets\gme.js

(function () {
    console.log("gme.js loaded.");

    var renderKatex = function() {
        var mathElems = document.getElementsByClassName("katex");
        var elems = [];
        for (const i in mathElems) {
            if (mathElems.hasOwnProperty(i)) elems.push(mathElems[i]);
        }

        elems.forEach(elem => {
            katex.render(elem.textContent, elem, { throwOnError: false, displayMode: elem.nodeName !== 'SPAN', });
        });
    };


    document.addEventListener("DOMContentLoaded", function(){
        var preview = document.querySelector("#preview");
        if (preview) {
            const config = { attributes: false, childList: true, subtree: false };

            const observer = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {
                    if (mutation.addedNodes.length == 1) {
                        observer.disconnect();
                        renderKatex();
                        observer.observe(preview, config);
                    }
                });
            });

            observer.observe(preview, config);
        }
        renderKatex();
    });
})();

#preview のタグが見つかったら、そのタグに子が追加・削除されるタイミングを監視し、プレビューの結果が返ってきた際のタイミングの条件に合致するときに監視を中断し、KaTex による数式の描画を行うようにしました。

参考文献にも書いてありますが、監視を中断せずに KaTeX による描画を行ってしまうとそれによる DOM の変化が起こるので無限ループに陥ってしまいます。(経験者は語る…)

スクリーンショット

下図のように無事、プレビュー時にも数式が描画されるようになりました。

参考リンク

GitHubで編集を提案

Discussion