【JavaScript】CodeMirror などで実装した Markdown エディタのリアルタイムプレビューにスクロール位置同期を実装する
StackEdit のようなマークダウンエディタには、リアルタイムタイムプレビューが実装されていて、実際に文章を書いているとリアルタイムにプレビュー画面に反映されます。機能が豊富なエディタには大体備わってますね。
さて、これをCodeMirrorやACEなどブラウザベースIDEで実装しようとすると、リアルタイムプレビューまではググってサンプルなどを動かしてるとすぐできます。が、現在編集している箇所へスクロールして追随する、「スクロール位置同期」 機能を実装しようとすると、日本語ではほとんど情報が見つかりません。一応こーいう記事はありますが。
これをどうにかする方法です。
まずはコード(CodeMirrorの例)
今回は、marked と CodeMirror を用いた例です。基本的な使い方は別途調べてね。
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.18.2/codemirror.min.css">
<style>
#editorWrapper {
float:left;
width:50%;
}
#editorWrapper .CodeMirror {
height: 500px;
}
#previewWrapper {
float:right;
width:50%;
height: 500px;
overflow: auto;
background-color: #ccc;
}
</style>
<div id="editorWrapper" style="">
<textarea id="editor">
</textarea>
</div>
<div id="previewWrapper">
<div id="preview"></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.18.2/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.18.2/addon/mode/overlay.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.18.2/mode/markdown/markdown.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.18.2/mode/gfm/gfm.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.18.2/addon/edit/continuelist.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/marked/0.3.5/marked.min.js"></script>
<script>
var preview = document.getElementById("preview");
var previewWrapper = document.getElementById("previewWrapper");
var editor = CodeMirror.fromTextArea(document.getElementById('editor'), {
mode: "gfm",
lineNumbers: true,
matchBrackets: true,
lineWrapping: true,
extraKeys: {
"Enter": "newlineAndIndentContinueMarkdownList"
}
});
editor.on("change" , function(e){
preview.innerHTML = marked(e.getValue());
});
editor.on("scroll" , function(e){
// http://liuhao.im/english/2015/11/10/the-sync-scroll-of-markdown-editor-in-javascript.html
var scrollInfo = e.getScrollInfo();
// get line number of the top line in the page
var lineNumber = e.lineAtHeight(scrollInfo.top, 'local');
// get the text content from the start to the target line
var range = e.getRange({line: 0, ch: null}, {line: lineNumber, ch: null});
var parser = new DOMParser();
var doc = parser.parseFromString(marked(range), 'text/html');
var totalLines = doc.body.querySelectorAll('p, h1, h2, h3, h4, h5, h6, li, pre, blockquote, hr, table');
// shouldPreviewScroll(length)
var body = document.getElementById("preview");
var elems = body.querySelectorAll('p, h1, h2, h3, h4, h5, h6, li, pre, blockquote, hr, table');
if (elems.length > 0) {
previewWrapper.scrollTop = elems[totalLines.length].offsetTop;
}
});
</script>
これを適当なHTMLファイルとして保存して、適当にマークダウンで文章を書いてみてください。画像や連続改行が混ざっていたとしても、プレビューとエディタ部分のスクロールがいい感じに同期されるかと思います。ソースコードにもURL書いてますが、元ネタは「The Sync Scroll of Markdown Editor in Javascript」です。個人的は良い感じ。
他に参考になりそうな記事
不具合があるとか、CodeMirrorを使っていないとか色々あると思いますので参考程度に。
- Make side-by-side markdown preview scroll with its editor
- Ace Scroll Sync with Preview : Get line height of wrapped lines
あと、できれば CommonMark に準拠したライブラリの方が色々良いですよ!