中学生が自分だけのMarkdownエディターを作ってみた話

続編・https://qiita.com/I-AM-RAILWAY-FAN/items/7b8752dbfe9d08be2800

## はじめに制作物

https://yo-train-ch.github.io/Markdown-editor-MAKUMAKU/

## 自己紹介
こんにちは
現役中学生の「よー」です。
今回は、自分だけのオリジナルマークダウンエディターをJavaScriptで作ってみた話をしたいと思います。

## <基礎知識>Markdownとは
>Markdownは、文書を記述するための軽量マークアップ言語のひとつである
要は、メモ帳だと装飾ができなくて寂しいので作られた言語、っていうイメージです（爆）

## 要件
- 誰でも使えるよう、機能は必要最低限に。
- 非常に軽いレスポンス。
- CDNを使用せず、継続的な運用を
- HTMLへの変換も楽々
- それなりにちゃんとしたリポジトリにする（github）

## 制作リポジトリ

https://github.com/yo-train-ch/Markdown-editor-MAKUMAKU

html、css、javascriptにて制作しました。
```lib```フォルダの中身は、ライブラリ集なので私はほぼ関知しておりません。

## index.html
```html
<html lang="ja">
    <head>
        <title>Markdownエディターのまくまく</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script src="https://yo-train-ch.github.io/Markdown-editor-MAKUMAKU//lib/bootstrap-5.0.2/js/bootstrap.bundle.min.js"></script>
        <link href="https://yo-train-ch.github.io/Markdown-editor-MAKUMAKU//lib/bootstrap-5.0.2/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://yo-train-ch.github.io/Markdown-editor-MAKUMAKU//lib/jquery/jquery-3.6.3.min.js"></script>
        <script src="https://yo-train-ch.github.io/Markdown-editor-MAKUMAKU//lib/marked/marked.min.js"></script>
        <!-- highlight.js -->
        <link rel="stylesheet" href="https://yo-train-ch.github.io/Markdown-editor-MAKUMAKU//lib/highlightjs/style.css">
        <script src="https://yo-train-ch.github.io/Markdown-editor-MAKUMAKU//lib/highlightjs/highlight.min.js"></script>
        <style>
            pre, code, var, samp, kbd, .mono {
                /*等幅フォント化*/
                font-family: Consolas, 'Courier New', Courier, Monaco, monospace;
                font-size: 14px;
                line-height: 1.2;
            }
            #editorWrapper #editor {
                height: 500px;
            }
            #preview {
                height: 500px;
                overflow: auto;
                overflow-y: auto;
            }
        </style>
    </head>
    <body class="p-3">
        <div class="container">
            <input class="form-control" type="text" placeholder="title" aria-label="default input example">
            <div class="row pb-2 pt-2">
                <div class="col">
                    <textarea id="editor" name="editor" class="form-control" rows="20" placeholder="Write something here..."></textarea>
                </div>
                <div class="col" id="previewWrapper">
                    <div id="preview"></div>
                </div>
            </div>
            <!-- Button trigger modal -->
            <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">HTMLを表示</button>
            <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
                <div class="modal-dialog">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title" id="exampleModalLabel">HTML</h5>
                            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                        </div>
                        <div class="modal-body">
                            <textarea id="converted" class="form-control" rows="20"></textarea>
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-primary" data-bs-dismiss="modal">閉じる</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
                <script>
            window.onload = function(){
                var editorSource = document.querySelector('#editor');
                // MarkdownからHTMLに変換する
                var convertedSource = document.querySelector('#converted ');
                var sync = function(){
                    var value = editorSource.value;
                    var md = marked(value);		
                    var convertedSource = document.getElementById('converted').value = md;
                    //プレビュー機能
                    var previewSource = $('#preview').html(md);
                    
                    $('pre code').each(function(i, block) {
                        hljs.highlightBlock(block);
                    });
                    
                    
                    //自動見出し位置合わせ
                    var editor_code = document.getElementById("editor").value;
                    var editors = document.getElementById("editor");
                    var previewer = document.getElementById("preview");
                    var pos      = editors.selectionStart;
                    var before   = editor_code.substr(0, pos);
                    var count = (before.match(/#+ (w*[・一-龠_ぁ-ん_ァ-ヴーａ-ｚＡ-Ｚa-zA-Z0-9]+|[a-zA-Z0-9_]+|[a-zA-Z0-9_]w*)/g) || []).length;
                    var elm = document.getElementById('preview');
                    var counts = elm.querySelectorAll('h1,h2,h3,h4,h5,h6');
                    
                    var hElems = elm.querySelectorAll("h1, h2, h3, h4, h5, h6");
                    var elem = hElems[count - 2];
                    var main = document.getElementById('preview');
                    main.scrollTop = elem.offsetTop;
                };
                editorSource.oninput = sync;
                sync();
            };
            $('#editor').on('keydown', function(e){
                if (e.keyCode === 9) {
                    e.preventDefault();
                    var elem = e.target;
                    var val = elem.value;
                    var pos = elem.selectionStart;
                    elem.value = val.substr(0, pos) + '\t' + val.substr(pos, val.length);
                    elem.setSelectionRange(pos + 1, pos + 1);
                }
            });
        </script>
    </body>
</html>
```
最低限、動くものが作れました。
変数宣言とか、だんだん面倒くさくなって（セキュリティーもいらないため）```var```が続出していますが、気が向いたら直そうかと思っています。

デザインはbootstrap、ライブラリ関係ではjqueryの他markedjsやhighlightjsを使用しています。

## 難しかったこと
自動位置合わせ（Qiita編集画面でいう、「同時スクロール」）が難しかったです。
メカニズムとしては、編集している文章が何個目の見出し配下にあるか正規表現で調べ、プレビュータブ内指定見出し部分の```scrollTop```を取得して代入しています。
理想としては、同時スクロールのように全て同時に動かしたかったのですが、技術的にも難しいことや重くしたくないことから、諦めました。

## 終わりに
ライブラリを使えば簡単にできてしまうし、同時スクロール等、javascriptの技術習得にもなるし、何よりも実用的です。
githubの練習にもなったので、一石四鳥でした。

どうぞ、使ってみてください！

https://yo-train-ch.github.io/Markdown-editor-MAKUMAKU/