ジャコ Lab

プログラミング関連のメモ帳的ブログです

コードブロックの表現に Highlight.js を使用する

このブログではシンタックスハイライトを表現するのに CSS を上書きしまくってなんとなく表示できるようになっています。
Highlight.js を使用しているつもりなのですが、上手く反映していたりしていなかったりしているのです。

YAML のコードブロックのキャプチャ
YAML のコードブロックのキャプチャ

いつも思ってはいたんですが、黒が濃いのと白が強いんですよね・・・
今回は、シンタックスハイライトの見直しをするついでに記事にします。

Before / After

BeforeAfter
劇的ビフォーアフター

はてなブログMarkdown でコードブロックを書いたとき

```yml
services:
  mysql:
    image: mysql:latest
    container_name: mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - ./db/data:/var/lib/mysql
    ports:
      - 3306:3306
```
例えばこうです。

プレビューとしては以下のような HTML に変換されます。

<pre class="code yml" data-lang="yml" data-unlink="">
services:
  mysql:
    image: mysql:latest
    container_name: mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - ./db/data:/var/lib/mysql
    ports:
      - 3306:3306
</pre>
これだと、ただの pre tag で囲っただけで、シンタックスハイライトを表現できる構成ではないです

Highlight.js を使うには

highlightjs.org

Highlight.js 本体と テーマ CSS が必要

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/atom-one-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"></script>

<script>
  document.addEventListener("DOMContentLoaded", () => {
    hljs.highlightAll();
  });
</script>
ここではテーマは「atom-one-dark」にします。

HTML タグの構造は以下である必要がある

<pre>
  <code class="language-ts">
    ...
  </code>
</pre>
はてなブログMarkdown が変換した HTML 構造と異なります。

色々改造していく

まずは変換された HTML を Highlight.js が希望する形に変換する

  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/atom-one-dark.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"></script>

  <script>
+   const convertHatenaMarkdownToHighlightFormat = () => {
+     const preElms = document.querySelectorAll("pre.code");
+     preElms.forEach((preElm) => {
+       const text = preElm.textContent;
+       const lang = preElm.getAttribute("data-lang");
+
+       const codeElm = document.createElement("code");
+       codeElm.textContent = text;
+       codeElm.classList.add(`language-${lang}`);
+
+       preElm.innerHTML = "";
+       preElm.appendChild(codeElm);
+     });
+   };

    document.addEventListener("DOMContentLoaded", () => {
+     convertHatenaMarkdownToHighlightFormat();
      hljs.highlightAll();
    });
  </script>

ここまででこのような絵になります。

シンタックスハイライトは効いた感
シンタックスハイライトは効いた感

デフォルト CSS が邪魔をしてくる

しかし、上記のように、デフォルト CSS によって白系に書き換えられてしまっています。

以下の通り、本当は .hljs の赤枠で囲われている CSS を適用したいです。

pre code な CSS の状態
pre code な CSS の状態

仕方ないので background: #282c34; だけは強制力を働かせます
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/atom-one-dark.min.css" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"></script>

+ <style>
+   .hljs {
+     background: #282c34 !important;
+   }
+ </style>

  <script>
    const convertHatenaMarkdownToHighlightFormat = () => {
      const preElms = document.querySelectorAll("pre.code");
      preElms.forEach((preElm) => {
        const text = preElm.textContent;
        const lang = preElm.getAttribute("data-lang");

        const codeElm = document.createElement("code");
        codeElm.textContent = text;
        codeElm.classList.add(`language-${lang}`);

        preElm.innerHTML = "";
        preElm.appendChild(codeElm);
      });
    };

    document.addEventListener("DOMContentLoaded", () => {
      convertHatenaMarkdownToHighlightFormat();
      hljs.highlightAll();
    });
  </script>

ここまでで背景色も Dark 系になりました

背景色も Highlight.js に合わせた状態
背景色も Highlight.js に合わせた状態

まとめ

こんな事しているからロードが遅くなったりするんですよね。