マークダウンのリアルタイムプレビュー機能を作ってみたいと思い、こちらの方の記事を参考にさせていただきました。
ただ、そもそも使用しているライブラリ、およびJSに対する知見がないため、メモとして残したいと思います。
記事の中では下記2つのライブラリを使用していらっしゃいます。
後述しますが、vue.jsはJavaScriptの軽量フレームワークのようなので、代わりにJQueryで実装しました。
marked.js自体のドキュメントはこちら。
https://github.com/chjj/marked
marked.jsのGemのドキュメントはこちらです。
https://github.com/rosscooperman/marked-rails
Gemfileに追記し、インストールします。
gem 'marked-rails'
$ bundle install
application.jsに下記を追記します。
(Rails 3.1以上であれば、Asset Pipelineによって読み込めるので、個別インストールの必要はないようです。)
//= require marked
Railsへのインストールはこれで完了です。
marked.jsの公式ドキュメントを参考に、実際に使用してみます。
適当なビューを作成し(今回はindexとします。)、下記を記載します。
<div id="content"></div> <script> console.log(marked('I am using __markdown__.')); document.getElementById('content').innerHTML = marked('# Marked in browser\n\nRendered by **marked**.'); </script>
すでにmarkedはGemでインストールしているので、`marked`メソッドが使用できます。
ブラウザで表示した結果はこちらです。
逆に`marked`メソッドがない場合は下のようになります。
document.getElementById('content').innerHTML = '# Marked in browser\n\nRendered by **marked**.';
Markdownはきいておらず、単なる文字列として出力されています。
いくつかMarkdownを試してみましたが、基本的なところは普通に出力できました。
日本語Markdownユーザー会
いくつかオプションも設定できるようです。
詳しくは調べられていないので、設定のコードを記載します。
また、ハイライトについては公式ドキュメントの例では、node-pygmentize-bundledとhighlight.jsと連携していました。
marked.setOptions({ // コードのハイライト(今回は割愛) highlight: function (code, lang, callback) { require('pygmentize-bundled')({ lang: lang, format: 'html' }, code, function (err, result) { callback(err, result.toString()); }); }, // Githubっぽいmd形式にするか gfm: true, // Githubっぽいmdの表にするか tables: true, // Githubっぽいmdの改行形式にするか breaks: false, // Markdownのバグを修正する?(よく分からなかったので、とりあえずdefaultのfalseで) pedantic: false, // HTML文字をエスケープするか sanitize: true, // スマートなリストにするか。pedanticと関わりがあるようなので、こちらもdefaultのtrueで。 smartLists: true, // クオートやダッシュの使い方。 smartypants: false, });
参考にさせていただいた記事では、vue.jsでプレビュー部分を作成していました。
私自身に知識がなかっただけなのですが、vue.jsはフレームワークでした。
他のフレームワーク(ReactやAngular)と比べ、軽量で、View層にだけ焦点を当てている分導入コストが低い、とのことです。
詳しくはドキュメントをご参照ください。
vue.jsのドキュメント
とはいえ、フレームワークであるならば、ふつうにJavaScriptやJQueryを使ってできるだろうと思い、勉強がてら作成してみました。
とはいえ、JQueryもそこまでゴリゴリ書けるわけではないので、下記サイト様を参考にさせていただきました。
jQueryで色々なフォーム入力値をリアルタイム取得する
まずはビューのHTMLです。
Railsのscaffoldで作成されるフォームを少し修正したくらいです。
<form> <%= form_for @post, :url => {:action => :create} do |f| %> <div class="form-group"> <%= f.text_field :title , class: 'form-control', placeholder: 'タイトルを入力してください'%> </div> <div class="form-group"> <div id='editor'> <textarea name="post[content]" class="form-control" rows="20"></textarea> <div id="marked-area"></div> </div> </div> <% end %> </form>
続いて、Javascript(JQuery)側です。
最終的にはマークダウン出力したいので、関数名はreplaceMarkdownとなっています。
$(function() { $("#editor textarea").each(function () { $(this).bind('keyup', replaceMarkdown(this)); }); function replaceMarkdown(elm) { var v, old = elm.value; return function () { if (old != (v = elm.value)) { old = v; str = $(this).val(); $("#marked-area").html(str); } } } });
まずは、対象のtextareaに関してkeyupイベントと関数をバインドさせます。
keyupはキーボードのキーが押され、上がった際に呼び出されます。
JQuery 日本語リファレンス -keyup-
keyupされたタイミングでreplaceMarkdownを呼びます。
ここで詰まったことが、この変数の宣言です。
参考にさせていただいたサイト様にてこの宣言をしておりました。
var v, old = elm.value;
勝手に勘違いしておりまして、`v = old = elm.value`で読み進めておりました。
JQueryでは変数をこのように宣言できます。
(どうりでページロード時に`v`を出力させると`undefined`になるわけです。。。)
var v; var old = elm.value; // ↓ var v, old = elm.value;
あとはvとoldを比較して、異なれば書き換えます。
function replaceMarkdown(elm) { var v, old = elm.value; return function () { if (old != (v = elm.value)) { old = v; str = $(this).val(); $("#marked-area").html(str); } } }
なお、marked.jsを使用してstrをマークダウンに変換すれば、ちゃんと出力されます。
$("#marked-area").html(marked(str));
マークダウンのプレビュー機能を調べている中で、RubyにもいろいろなGemがあることを知りました。
(例えば、Redcarpet)
今度はサーバーサイドでのマークダウン変換についても記事にしたいと思います!