はてなブログのコードブロックにテーマを設定する(シンタックスハイライト)
はてなブログで配布されているテーマをそのまま使っていたが、コードブロックが白と黒一色だったりしてぱっと見で変数や関数がわかりづらかったので、ちゃんとコードの構成要素に沿った形で色付け表示してみた。
highlight.jsというツールを使うことで簡単に導入可能で、少しはてなブログをカスタマイズするだけ。 まず、現在の状態を確認しておく。
highlight.jsを導入することでこのような表示になる。
導入方法
以下のコードをはてなブログ管理画面 → 設定 → 詳細設定 → headに要素を追加に貼り付ける。
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/styles/base16/tomorrow-night.min.css"> <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/highlight.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.8.0/highlightjs-line-numbers.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/languages/erb.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/languages/plaintext.min.js"> </script> <script> document.addEventListener('DOMContentLoaded', (event) => { document.querySelectorAll('pre.code').forEach((block) => { hljs.highlightBlock(block); hljs.lineNumbersBlock(block); var classes = block.classList; if(classes.length > 0){ if(classes[1].indexOf(':')){ var values = classes[1].split(':'); var filename = values[1]; if(filename){ block.setAttribute('data-filename', filename) block.classList.remove(classes[1]); block.classList.add(values[0]); var containerEle = document.createElement('div'); containerEle.classList.add('filename-container'); var filenameEle = document.createElement('span'); filenameEle.classList.add('filename'); filenameEle.append(document.createTextNode(filename)); containerEle.append(filenameEle) block.parentNode.insertBefore(containerEle, block); } } } // ちらつき解消 block.classList.add("visible"); }); }); </script> <style> /* ファイル名表示 */ .filename-container { position: relative; z-index: 10; top: 29px; } .filename { display: inline-block; vertical-align: top; max-width: 100%; background: rgba(177,197,247,.25); color: #fff; font-size: 0.8em; height: 24px; line-height: 24px; padding: 0 6px 0 8px; font-family: monospace; border-radius: 4px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* ちらつき解消 */ .entry-content pre.code { opacity: 0; transition: opacity 0.5s ease; } .entry-content pre.code.visible { opacity: 1; } .hljs-ln-numbers { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; text-align: center; color: #666; } /* 枠線の削除 */ .hljs-ln { margin-bottom: 0; border: none; } .hljs-ln tr, .hljs-ln td { border: none; } </style>
参考記事からそのまま貼り付けたものもあるので、もっとキレイに書けるはずです...。
行番号を表示したくない場合はこちら
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/styles/base16/tomorrow-night.min.css"> <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/highlight.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/languages/erb.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/languages/plaintext.min.js"> </script> <script> document.addEventListener('DOMContentLoaded', (event) => { document.querySelectorAll('pre.code').forEach((block) => { hljs.highlightBlock(block); var classes = block.classList; if(classes.length > 0){ if(classes[1].indexOf(':')){ var values = classes[1].split(':'); var filename = values[1]; if(filename){ block.setAttribute('data-filename', filename) block.classList.remove(classes[1]); block.classList.add(values[0]); var containerEle = document.createElement('div'); containerEle.classList.add('filename-container'); var filenameEle = document.createElement('span'); filenameEle.classList.add('filename'); filenameEle.append(document.createTextNode(filename)); containerEle.append(filenameEle) block.parentNode.insertBefore(containerEle, block); } } } // ちらつき解消 block.classList.add("visible"); }); }); </script> <style> /* ファイル名表示 */ .filename-container { position: relative; z-index: 10; top: 29px; } .filename { display: inline-block; vertical-align: top; max-width: 100%; background: rgba(177,197,247,.25); color: #fff; font-size: 0.8em; height: 24px; line-height: 24px; padding: 0 6px 0 8px; font-family: monospace; border-radius: 4px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* ちらつき解消 */ .entry-content pre.code { opacity: 0; transition: opacity 0.5s ease; } .entry-content pre.code.visible { opacity: 1; } </style>
何をやっているのか
特別難しいことはしていません。
CDN経由でhighlight.jsと拡張機能であるhighlightjs-line-number.jsを読みこんでカスタマイズし、あとはCSSで見た目を整えているだけ。
ただ、実装が少し複雑でこのポストの独自性があるところでいえば、ファイル名を表示させている箇所です。
QiitaやZennはコードブロックのコードソースを記述したあと、:
以降でファイル名を表示できます。
Markdownの記法には色々方言があるのですが、:
を使ってファイル名を表示されているこの記法は拡張されたものであり、はてなブログでは使えません。
どうにか実装する方法はないかと、こちらに行き着きましたが、要件は満たせているものの横スクロール時にファイル名がついてきてしまい断念...。
データ属性としてdata-filename
というものを用意しているのは便利なのですが、MarkdownがHTMLに出力されるときに使われるpre
要素の疑似要素としてファイル名を表示させているため、親要素であるpre
のスクロールに追従してしまうんですね。(回避方法はあるかと思いますが、思いつきませんでした。)
諦めてZennの実装ではどうなっているのか見てみると、ファイル名表示をpre
要素とは別にdiv
要素で実装していました。
「data-filename
のデータ属性を見つけたらpre
要素とは別でファイル名を表示するためのdiv
を生成すればいける!」とのことで、ソースの以下の箇所を実装しました。
var classes = block.classList;
if(classes.length > 0){
if(classes[1].indexOf(':')){
var values = classes[1].split(':');
var filename = values[1];
if(filename){
block.setAttribute('data-filename', filename)
block.classList.remove(classes[1]);
block.classList.add(values[0]);
var containerEle = document.createElement('div');
containerEle.classList.add('filename-container');
var filenameEle = document.createElement('span');
filenameEle.classList.add('filename');
filenameEle.append(document.createTextNode(filename));
containerEle.append(filenameEle)
block.parentNode.insertBefore(containerEle, block);
}
}
}
data-filename
が記載された要素を見つけ、その親との間にdiv
を新たに生成しています。
テーマを変える
以下のソースのbase16/tomorrow-night
の部分を好きなテーマの名前に変更します。
~ <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.1.0/styles/base16/tomorrow-night.min.css"> ~
highlight.jsのdemoに対応しているテーマがありますのでまずはテーマを選ぶ。
適当なURLではうまくcdnを取り込めないので、ここで選んだテーマの正確なURLをコピーして、上記ソースを書き換えてください。
参考文献
はてなブログで highlight.js を使う (Markdown記法向け) - ぺんぎんの布団
[Markdown] Qiitaのようにコードに自動でファイル名を付ける
highlight.jsに行番号を追加する方法 - Kotonoha
GitHub - wcoder/highlightjs-line-numbers.js: Line numbering plugin for Highlight.js
【JavaScript】要素を追加するinsertBeforeとappendChildについて - TASK NOTES
JavaScriptで親や兄弟要素を取得する | cly7796.net
JavaScript | 複数のノードをまとめて追加(DocumentFragment)
最後に
実装中にこんな記事を見かけてzennを併用している私としては、絶対こっちの方がおすすめです... zenn.dev