MerkedとMermaidを連携させたのにうまく行かなくなった!
自分のツールとして作っているコードの中で、MarkedとMermaidの連携について、mermaid.jsの見本であるmermaidjs.github.io - UsageのExample of a marked rendererをそのまま使っていた。
しかし、最近なぜかうまく表示されなくなった。原因は、この記事執筆時点でも未だにわからず。
コードの紹介
Usageにかかれていたコードは以下のとおり。
要するに、markdownの ``` に当たる部分でmermaidの構文を書いた場合に、mermaidが適用できる(<div class="mermaid">に記述した構文をmermaidがsvg画像化してくれる)ようにmarkedがDIVタグを加えるようHTML変換する仕組みを取るという、ごく単純な方法だ。
var renderer = new marked.Renderer();
renderer.code = function (code, language) {
// mermaidはcode部分の冒頭がこの2つの文言になるので、それで判断している
if(code.match(/^sequenceDiagram/)||code.match(/^graph/)){
// divタグを加えて上げると、mermaidが認識してくれるのだ!
return '<div class="mermaid">'+code+'</div>';
}
else{
return '<pre><code>'+code+'</code></pre>';
}
};
HTMLツール全体としては、以下のソースにしていた。これに、mermaidの構文を含むmdファイルを読み込ませることで、mermaidを図化する方式をとっていた。
<!DOCTYPE html>
<html lang="jp">
<head>
<meta charset="utf-8">
<title>MARKUP</title>
<link rel="stylesheet" href="./styles/default.css" />
<script src="./lib/jquery-3.3.1.js"></script>
<script src="./lib/marked.min.js"></script>
<script src="./lib/mermaid.min.js"></script>
<script src="./lib/highlight.pack.js"></script>
</head>
<body>
<div id="content"></div>
<script>
var renderer = new marked.Renderer();
renderer.code = function (code, language) {
if(code.match(/^sequenceDiagram/)||code.match(/^graph/)||code.match(/^gantt/)){
return '<div class="mermaid">'+code+'</div>';
}else{
return '<pre><code>' + hljs.highlightAuto(code).value + '</code></pre>';
}
};
$(document).ready(function(){
if (location.href.split("#").length > 1){
var urlParam = location.href.split("#")[1];
$.get( urlParam, function( data ) {
// console.log(data);
$('#content').html(marked(data, { renderer: renderer })); });
}else{
$('#content').html('please url#any.md');
}
});
</script>
</body>
</html>
これでうまく動いていた、少し前までは。
それが、最近いつの間にか動かなくなり、marmaid構文のまま表示されるようになってしまった。
パソコンを変えたタイミングだったので、内部的に何かしらの設定が変わってしまったのかもしれないが…根本的な原因はわからない。
さて、それで3,4時間頭を悩ませ続けて、ようやく対処法に気づいた。
merked がレンダリングする前に、mermaidが動いてしまっているのだろう
mermaidは <div class="mermaid"></div>の中に、コードがなければならないことは、先に書いたとおりだ。
つまり、下の理想の図になるはずだ。
実際には、mermaidがclass="mermaid"がないmarkdownソースのままにレンダリングしてしまって、それからmarkedがmarkdownをHTMLにレンダリングするということが起こったのだと推測した。
つまり、下の実際の図のとおりに処理が動いた。
昔は、パソコンのスペックが少し悪かったから、markedとmermaidの処理の順番が逆になって、理想どおりに動いていた…? 考えにくいがそうかもしれない。
対処方法を調べよう
ということで、google先生に聞いたところ、素晴らしい講師を紹介していただいた。
mermaidを非同期で読み込む - なおしむ論
記事を読めば一目瞭然だが、要するに、読み込んだタイミングでmermaidが動くなら、最初から動かさなければいいじゃないという論理。
至極、当然の話であった。
さて、この方が書いた内容は自分のコードでも適用できるので、真似させてもらった。
コード修正
ということで、<HEAD&mt;に、mermaidがHTMLをロード時に勝手に動かないようにするコードを入れる。
// デフォルトのtrueだと、HTMLのロード時にレンダリングしてしまうので、falseで停止してあげる
mermaid.initialize({startOnLoad:false})
そして、mermaidを動かしたいタイミング、具体的にはmarkedのレンダリングが終わった直後に以下のコードを入れる。
// このコードで自主的にレンダリングしてくれる。
mermaid.init()
あっさりと成功した。
元記事の方もおっしゃっているが、驚くほど簡単だった。3,4時間何に悩んでいたんだろう…。
最終的なコードは以下のようになった。
なお、MDファイルの読み込み方は、URLに「mermaid.html#hoge.md」のようにして#で変換したいmdファイルを付けるMDwiki - Markdown based wiki done 100% on the client via javascriptリスペクトの方式である。
<!DOCTYPE html>
<html lang="jp">
<head>
<meta charset="utf-8">
<title>MARKUP</title>
<link rel="stylesheet" href="./styles/default.css" />
<script src="./lib/jquery-3.3.1.js"></script>
<script src="./lib/marked.min.js"></script>
<script src="./lib/mermaid.min.js"></script>
<script src="./lib/highlight.pack.js"></script>
<!--
MEMO: レンダリングを読み込み時にしない
任意のタイミングで行うために、するのだ。
ref: https://naosim.hatenablog.jp/entry/2018/02/20/071042
-->
<script>mermaid.initialize({startOnLoad:false});</script>
</head>
<body id="content">
<script>
var renderer = new marked.Renderer();
renderer.code = function (code, language, escaped) {
if(code.match(/^sequenceDiagram/)||code.match(/^graph/)||code.match(/^gantt/)){
return '<div class="mermaid">' + code + '\n</div>';
}else{
return '<pre><code>\n' + hljs.highlightAuto(code).value + '\n</code></pre>';
}
};
$(document).ready(function(){
if (location.href.split("#").length > 1){
var urlParam = location.href.split("#")[1];
$.get( urlParam, function( data ) {
// Memo: markedのレンダリング-> marmaid.init()
// ただし、あらかじめstartOnLoad:falseにしておかないと、
// markedの前にmermaidが走ってしまって、mermaidで変換されない。
$('#content').html(marked(data, { renderer: renderer }));
mermaid.init();
});
}else{
$('#content').html('please url#any.md');
}
});
</script>
</body>
</html>