Hugoでaタグとimgタグに属性を追加したかったのでRender hooksを作成した
HugoがMarkdownをHTMLへレンダリングする際に書き出すa
タグやimg
タグは、本当に必要最低限のタグでしかなくて。
ショートコードを使う頻度を下げたかったので、既存のレンダリングをオーバーライドするRender hooksを作成したよ、という備考録です。
目次
HugoのMarkdown
厳密に言えば、Hugoが書き出すのではなく、Hugoが標準使用するMarkdownレンダラーであるGoldmarkが最低限しか書き出さない、なんですけど。
どれだけ最低限かというと、例えばMarkdownでテキストリンクと画像挿入をこう書いたとして、
|
|
書き出されるHTMLタグはこう。
|
|
a
タグはtitle
属性はつけられるのに、target
属性はつけられず全部target="_self"
扱いでrel
属性もないのです。
ウチのサイトでは、内部リンクは_self
(つまり、target
属性の指定をしない)で、外部リンクは_blank
で開くようにしています。なので、target
属性を指定できないと不便。
Markdownによっては{:target="_blank"}
で別窓になるとかありますが、それは他のMarkdownの方言なのでHugoが採用しているGoldmarkでは使えません。
img
タグも、必須であるalt
属性はつけられるのに、それ以上に必須だと個人的に思うwidth
属性とheight
属性がない。
なんならloading
属性もない(これはブラウザ対応されたのがやや最近なので、なくても不思議じゃないけど)。
width
属性とheight
属性は本当に必要だと思うのですよね。
よく、サイトを見ている時にページを読み込んだと思って見ていたら途中でいきなり画像が出てきて、クリックするつもりもないどうでもいい広告バナーをクリックした、みたいな経験をしたことがある人ってかなりいると思うんですが。
あれはレイアウトシフトが起こっている状態のサイトによくある光景です。
画像が表示されるスペースが予め確保されていたら起こらない現象なので、画像のサイズを指定するwidth
属性とheight
属性はとても大事だと思うのです。
……と、こんな感じなので自作のショートコードを使っていたんですけどね。
本文を書く際、折角色々と取り回しが利くMarkdownで書いているのに、HTMLタグで書くのも本末転倒感があり、かといってショートコードを連発しすぎるのもなぁと思ってしまって。
そういえば組み込みのレンダリングの挙動を変えることができるものがあったなと思い出したので、そいつを一発ビシッと決めてみました。
ちな、上にある我が家のねこちゃんの画像はこれ。
あくびの瞬間を撮ってしまって、人相悪くなってるやつ。
Render hooksとは
HugoがMarkdownからHTMLをレンダリングする際のデフォルトの挙動を、カスタムテンプレートで上書きして挙動を変えられる機能です。
Hugoでは以下の7つの要素タイプをフックできるようで。
- Blockquotes(引用ブロック)
- Code blocks(コードブロック)
- Headings(見出し)
- Images(画像)
- Links(リンク)
- Passthrough elements(パススルー要素)
- Tables(テーブル)
挙動に不満があるのは、今のところ上記の中では画像とリンクだけなので、この2つをカスタマイズしていきます。
他のRender hooksの詳細はマニュアルをどうぞ。
aタグをカスタマイズする
まずはリンクタグをカスタマイズします。
Markdownで記述するリンクには3つ要素があります。
|
|
引用:https://gohugo.io/render-hooks/links/#markdown
リンクテキストになるText
要素、リンク先になるDestination
要素、リンクタイトルになるTitle
要素です。これらをカスタマイズ済みのHTMLのa
タグへ組み込んでいきます。
ウチのブログでは、用途によってCSSで装飾したリンク表示と、本文内に書くプレーンなリンクを使い分けています。つまり、用途によってHTMLの組み方も違うということで。
外部リンク(装飾リンク)、外部リンク(インラインリンク)、内部リンク(NOTE内記事にリンクする装飾リンク)と3種類あるので、それをどう振り分けるかをまず悩みました。
外部リンクと内部リンクはURLの行頭がhttp
ではじまるかどうかで振り分ければ簡単なんですけど、装飾のあるリンクとインラインリンクはどちらも外部リンクで使用するので、どこで分けるか、みたいな。
で、色々考えた結果、HTMLマークアップとしては邪道な使い方になるのですが、リンクタイトルをその判別に使用することにしました。
できたテンプレートはこちら。
|
|
リンクタイトルを使ってインラインリンクと装飾つき内部リンクを振り分けています。
なので、実際のリンクにはタイトルをつけていません。
まぁ、リンクタイトルってほとんどつけないし、いいかなって。
装飾つきリンクの方はa
タグだけ挙動が違うので、ここでもリンクタイトルを使って内部リンクか外部リンクかを判別して振り分けました。
これでMarkdownで
|
|
と書くと、HTMLでこう表示されます。
インラインリンクです。
これでMarkdownでのリンクまわりの記述がスッキリしたのでヨシ。
imgタグをカスタマイズする
Hugoにはfigure
タグで画像を挿入できる組み込みのショートコードがあるのですが、このfigure
タグは「本文のから参照される図版」を表すタグで。
つまり、本文から図を取り除いても文章が成立する場合でのみ使用することが望まれるもの で、図を伴った説明文などでの使用は避けた方がよいタグです。難しいよね。
なので、picture
タグで画像を挿入するショートコードを自作して使っていました。
WordpressからHexoに乗り替えた頃は、まだ一部の主要ブラウザでしかWebPが正式対応していなかったので、ショートコードで対応していたんですよ。
で、HexoからHugoへ乗り替える時も、そのショートコードをそのまま移植しただけで使っていました。
少し前にWebP対応を調べたら、もうほぼすべてのブラウザでWebP対応が済んでいたので、ショートコード内でpicture
タグでの画像表示をやめて、img
タグ+srcset
属性だけの簡潔なものへ変更したのです。
でも、img
タグだけにするならショートコードにしなくてもよくね?と思い当たって。
あとは、AppleデバイスのRetinaディスプレイ対応に本当に嫌気が差したのでsrcset
属性も取っ払って、単純に1枚だけ用意した画像を表示するimg
タグへ書き換えるついでに、Markdownの画像挿入記述のままでなんとかしてぇなと思ったのでした。
サイズが違うだけの同じ画像を何枚も用意するのバカらしくね?
いや、仕事で給料もらって運営するサイトならきっちりやりますけども、ここは個人の趣味サイトなのでそこまでせんでもよくね?って。
長々と語りましたが、次は待望の画像まわりのカスタマイズです。
Markdownで記述する画像挿入には3つ要素があります。
|
|
引用:https://gohugo.io/render-hooks/images/#markdown
画像の代替テキストになるDescription
要素、画像パスになるDestination
要素、画像タイトルになるTitle
要素です。
img
タグへ画像のサイズ(width
属性とheight
属性)とloading
属性を追加したHTMLタグへ組み込んでいきます。
ショートコードを使用していた時は、画像のサイズはwidth
属性やheight
属性へデフォルトの値を入れることができたのですけども。
フックするテンプレートでは、表示する画像のサイズを取得する関数を使って画像のサイズを挿入したいと思います。
全部を同じサイズに固定すればめんどくさくないんだけど、それも難しい場面とかあるしね。
また、ウチのサイトでは画像表示にFancyboxを使っているので、それにも対応するように組んでいきたいと思います。
まず画像のサイズを調べるための準備をします。
使用するリソースメソッドはassets
ディレクトリのみで使用できるメソッドなので、画像を置くディレクトリをassets
ディレクトリにしなければなりません。
でも、画像はstatic
ディレクトリへ置いておきたい。
ということで、assets
ディレクトリへstatic
ディレクトリをマウントします。
サイト設定(ここではhugo.yaml)へ、以下の設定を追加します。
|
|
これで、static
ディレクトリの画像などのリソースをグローバルリソースとして、リソースメソッドで操作できるようになりました。
次に、オーバーライドするRender hooksのテンプレートを作ります。
|
|
これから先は例として以下のMarkdownで説明します。
利用する画像のサイズはWidth=1600px、Height=900pxとします。
|
|
まずは、リソースメソッド操作するための画像ファイルパスをpath.Join関数で作ります。
|
|
画像ファイルはassets
ディレクトリにあるという前提なので、上の関数だと、assets
ディレクトリからの相対パス+Markdownからの画像パスになるDestination
要素を結合して、変数$noteImage
へ代入します。
$noteImage
の中は以下のパスが代入されています。
|
|
もうひとつ、Fancybox用のリンクのためのディレクトリパスの変数を作りました。
|
|
これはサイトのルートからの指定です。
次に、画像を表示するタグを作成。
|
|
a
タグのhref
属性とimg
タグのsrc
属性には$noteImgDir
+Destination
要素でパスを作ります。
|
|
Fancyboxのdata-caption
属性とimg
タグのalt
属性にDescription
要素を.PlainText
で挿入。
|
|
img
タグのwidth
属性とheight
属性はリソースメソッドの.Width
と.Height
で画像サイズを挿入するのですが、元画像は2倍サイズで作ってあって、ブログ記事内で表示するのはCSSで50%~縮小したサイズになります。なので、img
タグのwidth
属性とheight
属性には1/2のサイズを記載したい。
ということで、テンプレートの中で計算をしています。
該当箇所はここ。(見やすいように改行をいれています)
|
|
まず、対象になる画像のパスをresource
関数を使って変数から取得します。
次に、幅と高さそれぞれを除算のmath.Div関数で2で割ります。
上のMarkdownの例でレンダリングして書き出されたHTMLはこちら。
|
|
必要な要素がたっぷり詰まったHTMLタグになりました。
画像リンクもカスタマイズしたかったけど
ウチでは本文を書く際に、画像に外部へのWebリンクを貼る画像リンクも使用することがあります。
なので、今回のフックで画像リンクもカスタマイズできるのでは?と試行錯誤したのですけども。
結論から言えば、画像リンクはどう要素を取り出せばいいのか迷ってしまったので、今回はカスタマイズを見送り。
HugoのMarkdownでは画像リンクってこう書くんですよ。
でもって、要素的はこんな感じだと思うんですね。
|
|
コンテキストを取りだそうにも、リンクパスであるDestination
要素と画像パスであるDestination
要素が衝突してうまくいきませんでした。
プレースホルダー的に取り出せるならできそうなんだけど、その辺がよくわからなかったので、画像リンクはこれまでと同じくショートコードで対応することにしました。
総じて
最初はめんどくさそうだな~と思っていたRender hooksですが、作ってみると意外とあっさりしていて、簡単に実装できました。
Hugoの公式ドキュメントにあるコードは本当に必要最低限しか書かれていないので、もう少し例とか増やしてほしいな……って思いました。
淡泊にしか書かれてないから、逆に見づらいんだよね……。