『ラゴン・ジュルナル』に来てくださってありがとうございます!

「JavascriptをOn」にしていない端末やブラウザで閲覧しているかたに、このアラートが表示されるようになっております。Javascriptを許可してからおたのしみくださいませ。

Chrome「設定」→「詳細設定」→「プライバシーとセキュリティー」→「サイトの設定」→「Javascript」を許可 iPhone「設定」→「Safari」→「セキュリティ」→「Javascript」をオン iPad「設定」→「Safari」→「詳細」→「Javascript」をオン Android「メニュー」→「設定」→「高度な設定」→「Javascriptを有効」など

TOPページに記事をランダム表示するShuffledを追加しました。

サイト改善記録 #23

サイト

Lagon Journal

なにをどうしたか

やったことはだいたい以下のような感じです。ひとつずつご紹介していきます。

  1. 記事を必要なぶんだけJSONに書き出す
  2. JSでJSONを取得して、記事をランダムに並べ替える
  3. TOPページに記事をランダムに表示する

1. 記事を必要なぶんだけJSONに書き出す

HUGOにはJSONを書き出す機能がついています。なんという便利さ。このおかげで、静的サイトではやりにくそうなランダム表示ができるようになります。

HUGOでJSONを書き出すために、まずconfig.tomlのoutputsのところに以下を書き加えます。

[outputs]
    home = [ "HTML", "JSON" ]

これによって、一度にすべての記事のリストをJSONに書き出せるようになります。”HTML”をつけている理由は、ここに”JSON”とだけ書くとHTMLが吐き出されなくなるためです。試してはいないのですが、おそらくTOPページにアクセスしても何も出なくなります。

また、home以外にsectionとかpageとかにも”JSON”をつけることができます。それぞれのページに対応するJSONを吐き出すことができるようになるようです。一度に一部の記事だけがほしいときは、homeではなくsectionやpageなどに”JSON”をつけたほうがいいかもしれませんね。

次に、以下のファイルをlayouts/_defaultの中に追加します。

{
    "items" : [{{ range $i, $e := where .Site.RegularPages "Section" "not in" (slice "about" "event" "faq"...(長いので省略)) }}
        {{ if $i }}, {{ end }}{{ .Render "item" }}{{ end }}
    ]
}
{{ $english_title := "none" }}
{{ with .Params.englishtitle }}{{ $english_title = . }}{{ end }}
{{ $description := .Summary | printf "%s(...)" }}
{{ with .Params.description }}{{ $description = . }}{{ end }}
{{ $length := "none" }}
{{ with .Params.length }}{{ $length = . }}{{ end }}
{{ $authoricon := "none" }}
{{ with .Params.authoricon }}{{ $authoricon = . }}{{ end }}
{{ $eyecatch := "none" }}
{{ $eyecatch_data := partial "function/get_eyecatch_image" . }}
{{ if $eyecatch_data.eyecatch }}{{ $eyecatch = $eyecatch_data.eyecatch.Permalink }}{{ end }}


{{- dict "title" .Title "english_title" $english_title "section" .Section "authoricon" $authoricon "eyecatch" $eyecatch "description" $description "link" .Permalink "length" $length | jsonify -}}

ふざけているのかという感じのファイル名になっていますが、ファイル名を{pageKind}.{outputFormatName}.{extension}にするという風に決まっているため、こうなっています。{ページの種類}.{出力する形式}.{拡張子}です。今回は{リスト}.{JSONを出力}.{JSON}なので、list.json.jsonになります。

list.json.jsonでJSONに出力する記事を指定しています。「ラゴンジュルナルとは」や「よくある質問」など、ランダム表示の中に入れたくない記事を除いています。とにかく、除きたいものをひたすら羅列していきます。

その下にあるitem.json.jsonは、記事の何のデータを取ってくるかを指定するファイルです。たとえば今回であれば、タイトルやアイキャッチ画像のURLなど、TOPページに表示したい情報を取ってきています。HTMLを出力するときの記事ファイルのようなものです。

HTMLだとHTMLタグの中にHUGOの関数を入れ込んであれこれできるのですが、JSONではそうもいかないため、もしかしたら設定されていないかもしれない要素については、上のほうであらかじめ振り分けています。設定されていなければ空文字にするという風にしてもよかったのかもしれませんが、空文字の扱いには手こずった記憶しかないので、わかりやすく”none”という文字を設定するようにしています。

item.json.jsonの一番下では、いろいろなデータをJSON形式にして出力しています。list.json.jsonみたいに、

{
    title: ...,
    english_title: ...,
}

みたいに書いてもいいのですが、もし取ってきたデータにダブルクォーテーション「”」などが入っていたりするとうまく出力してくれなくなることがあるそうなので、HUGOにあるjsonify関数を使っています。この関数は、文字列をJSONに合うように変換してくれるものです。ダブルクォーテーションなどがあってもうまく処理してくれます。

本当はHTMLを書き出すときにbaseof.htmlを用意しているのと同じように、全体の基礎になるbaseof.json.jsonも用意したほうがHUGOの決まりに従っていていいのかもしれませんが、なくても問題なさそうだったため、抜きました。もしバージョンアップなどで不具合が出るようになったら追加します。

ここで、TOPページのURLに”/index.json”をつけると出力したJSONを見ることができます。Lagon Journalでも見ることができるので、ただ味気なく文字の羅列がされているだけですが、興味のあるかたは見てみてください。

アイキャッチ画像の取得に使っている"function/get_eyecatch_image"は、Lagon Journalで使わせていただいているHUGOテーマ"Basic Design Personal Blog"に組み込まれているものです。他のテーマを使われている場合はご注意ください。

2. JSでJSONを取得して、記事をランダムに並べ替える

まずはJSでJSONを取得するところから。

const all_articles_url = "./index.json";

fetch(all_articles_url)
.then(function(data) {
    return data.json();
})
.then(function(json) {
    //---------------
    //ここの処理は次に載せます。
    //---------------
}

指定したURLからデータを取ってきて、それをJSONとして扱うことにします。

次に、JSONとして扱うことにしたデータをあれこれします。まずはランダムに並べ替えます。

//---------------
//ここから上は省略します。
//---------------
.then(function(json) {
    const articles = json["items"];

    //random sort
    const item_length = articles.length;
    const articles_index = [...Array(item_length)].map((_, i) => i);
    const random_index = shuffle(articles_index);
    //---------------
    //ここの処理は次に載せます。
    //---------------
}

//function to shuffle array
const shuffle = ([...array]) => {
    for (let i = array.length - 1; i >= 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
    return array;
}

list.json.jsonで出力したJSONはすべて”data”の中に入っているものなので、”data”の中のデータ、つまり記事一覧をまるごと取ってきてarticlesに入れています。次に、記事の件数分ある、「0, 1, 2, …」となっている連番の配列をつくります。articlesを直接並び替えてもいいのかもしれませんが、なんとなく時間がかかりそうな気がしたので、一旦数字だけの配列をつくって、それを並べ替えることにしました。

ランダムに並べ替える関数については、こちらのサイト(別のページへ飛びます)で紹介されていたものをそのままお借りさせていただきました。おしゃれなやりかたなので、詳しく知りたいかたはぜひ見にいってみてください。「Fisher–Yates shuffle」というそうです。

3. TOPページに記事をランダムに表示する

//---------------
//ここから上は省略します。
//---------------
//random sort
const item_length = articles.length;
const articles_index = [...Array(item_length)].map((_, i) => i);
const random_index = shuffle(articles_index);

for (var i = 0; i < 2; i++) {
    const now_index = random_index[i];
    const now_article = articles[now_index];

    //title
    document.getElementById("random-title" + i).textContent = now_article.title;

    //english-title
    if (now_article.english_title == "none") {
        document.getElementById("englishtitle" + i).textContent = "";
    } else {
        document.getElementById("englishtitle" + i).textContent = now_article.english_title;
    }

    //permalink
    const links = document.getElementsByClassName("random-link" + i);
    for (const link_el of links) {
        link_el.href = now_article.link;
    }

    //eyecatch
    document.getElementById("eyecatch" + i).innerHTML = '<img class="header-eyecatch-img-list" src="' + now_article.eyecatch + '"></img>';

    //authoricon
    if (now_article.authoricon == "none") {
        document.getElementById("authoricon" + i).innerHTML = "";
    } else {
        document.getElementById("authoricon" + i).innerHTML = '<img src="images/icon/' + now_article.authoricon + '.jpg">';
    }

    //description
    document.getElementById("description" + i).textContent = now_article.description;

    //length
    if (now_article.length == "none") {
        document.getElementById("random-length" + i).innerHTML = "";
    } else {
        document.getElementById("random-length" + i).innerHTML = "<b>" + now_article.length + "</b> letters in this blog";
    }
}

急に長くなりましたが、やっていることはランダムに並べ替えた数字を配列のインデックスにして、HTMLの対応するところに入れていっているだけです。

並べ替えた連番の配列のうち、最初の2つの数字を取ります。その数字をそれぞれ記事のデータが入っているほうの配列(articles)のインデックスに指定して、HTMLに入れていきます。たとえば、0〜4までの連番の配列が並び替えられた結果「3, 1, 2, 0, 4」になっていたら、最初の3と1を取ってきて、articlesの中の3つめと1つめの記事を取ってきます。

偶然の出会い、楽しんでいただければさいわいです。

それでは、ここまで読んでくださり、ありがとうございました。