続編・https://qiita.com/I-AM-RAILWAY-FAN/items/7b8752dbfe9d08be2800
はじめに制作物
自己紹介
こんにちは
現役中学生の「よー」です。
今回は、自分だけのオリジナルマークダウンエディターをJavaScriptで作ってみた話をしたいと思います。
<基礎知識>Markdownとは
Markdownは、文書を記述するための軽量マークアップ言語のひとつである
要は、メモ帳だと装飾ができなくて寂しいので作られた言語、っていうイメージです(爆)
要件
- 誰でも使えるよう、機能は必要最低限に。
- 非常に軽いレスポンス。
- CDNを使用せず、継続的な運用を
- HTMLへの変換も楽々
- それなりにちゃんとしたリポジトリにする(github)
制作リポジトリ
html、css、javascriptにて制作しました。
lib
フォルダの中身は、ライブラリ集なので私はほぼ関知しておりません。
index.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-Za-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の練習にもなったので、一石四鳥でした。
どうぞ、使ってみてください!