目次
ブログの記事一覧ページにソートボタンを付けたい。
ソートの内容は記事に付けたタグです。
そういう要件はありますでしょうか。
一般的にソートをするとしたら思いつくのは以下の実装でしょうか。
・カスタムクエリパラメーター: ?category=news&tag=information
・JavaScriptでのフィルタリング
・手動でのページネーション実装
めんどくさそうです。
しかしshopifyなら、taggedパラメーターという便利な独自機能があります。
shopify独自のtaggedパラメーター
ブログ一覧のテンプレートに仕込むと以下の機能が可能となります。
とても良いのはShopify標準機能だけで完結できることです。
- 独自のtaggedパラメーター: /blogs/blog-handle/tagged/tag-name
- 自動フィルタリング: そのタグを持つ記事のみを自動的に表示
- ページネーション連動: フィルタされた記事に対してページネーションも自動適用
- SEO対応: 検索エンジンにも適切にインデックスされる
リンクに仕込むと使えます。
<a href="{{ lang }}/blogs/{{ blog.handle }}/tagged/{{ ordered_tag | handleize }}">
※他にもあるshopify独自タグ
/collections/collection-handle/products/product-handle
/blogs/blog-handle/tagged/tag-name ←今回はこれ
/collections/collection-handle/tag-name
/search?q=search-term&type=article
サーバーサイドレンダリングで、JavaScriptのクリックイベントやDOM操作など一切必要ありません。
記事一覧ページのファイルにどのようにして仕込むのでしょうか。
AIに聞きくと答えてくれます。
【共有】方法を2種類
方法1はボツになった方法で「タグをソートボタンにする」方法。
方法2は「メニューで登録した単語でソートボタンにする」方法です。
方法1:タグをソートボタンにする
ブログ記事のタグがソートボタンとなります。

要件は以下です。
- 記事のタグがソートボタンとして出力される
- 選択されたタグにactiveクラスが適用される
- 12記事ごとのページ分割
- タグフィルター時も正しくページネーションが動作
main-blog.liquidはテンプレートによってファイル名が違うかもしれませんが、要はブログ一覧用のファイルです。
main-blog.liquid
{%- style -%}
/* タグフィルターボタンのスタイル */
.blog-tag-filter {
margin: 0 0 20px;
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.tag-btn {
background: #fff;
border: 1px solid #dddddd;
border-radius: 50px;
padding: 9px 20px 8px;
cursor: pointer;
font-family: "Helvetica Neue",
Arial,
"Hiragino Kaku Gothic ProN",
"Hiragino Sans",
Meiryo,
sans-serif;
font-weight: 700;
font-size: 12px;
letter-spacing: 0;
text-transform: uppercase;
color: #101010;
text-decoration: none;
display: inline-block;
transition: background .2s, color .2s, border .2s;
}
.tag-btn.active,
.tag-btn:hover {
background: #fff;
border: 1px solid #000A82;
color: #000A82;
}
/* スマホ対応:横スクロール */
@media screen and (max-width: 767px) {
.blog-tag-filter {
width: calc(100% + 40px);
margin: 0 0 20px -20px;
padding: 0 20px 0 0;
flex-wrap: nowrap;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
white-space: nowrap;
gap: 5px;
}
.blog-tag-filter .tag-btn {
flex: 0 0 auto;
white-space: nowrap;
}
.blog-tag-filter .tag-btn:first-child {
margin: 0 0 0 20px;
}
}
{%- endstyle -%}
{%- paginate blog.articles by 12 -%}
<div class="p-news-list__inner l-section__inner">
<div class="p-news-list__heading c-heading">
<h1 class="p-news-list__title c-heading__title u-font-en-h2">{{ blog.title | escape }}</h1>
</div>
<div class="blog-tag-filter">
{%- if request.locale.name == "English" -%}{% assign lang = '/en' %}{%- else -%}{% assign lang = '' %}{%- endif -%}
<a href="{{ lang }}/blogs/{{ blog.handle }}" class="tag-btn{% unless current_tags %} active{% endunless %}">ALL</a>
{% comment %} 全記事からタグを収集(フィルタリングされていない全記事から) {% endcomment %}
{% assign all_tags_string = '' %}
{% assign all_blog_articles = blogs[blog.handle].articles %}
{% for article in all_blog_articles %}
{% for tag in article.tags %}
{% assign all_tags_string = all_tags_string | append: tag | append: ',' %}
{% endfor %}
{% endfor %}
{% assign all_tags = all_tags_string | split: ',' | uniq %}
{% comment %} タグの表示順序を指定 {% endcomment %}
{% assign ordered_tags = 'INFORMATION,POP UP,CAMPAIGN,WOMEN,MEN,ITEMS,STORY,HOW TO' | split: ',' %}
{% for ordered_tag in ordered_tags %}
{% if all_tags contains ordered_tag %}
<a href="{{ lang }}/blogs/{{ blog.handle }}/tagged/{{ ordered_tag | handleize }}" class="tag-btn{% if current_tags contains ordered_tag %} active{% endif %}">{{ ordered_tag }}</a>
{% endif %}
{% endfor %}
{% comment %} 指定した順序以外のタグを表示 {% endcomment %}
{% for tag in all_tags %}
{% unless ordered_tags contains tag or tag == blank %}
<a href="{{ lang }}/blogs/{{ blog.handle }}/tagged/{{ tag | handleize }}" class="tag-btn{% if current_tags contains tag %} active{% endif %}">{{ tag }}</a>
{% endunless %}
{% endfor %}
</div>
<div class="p-news-list__list c-news {% if blog.handle == 'feature' %}p-feature-list__list c-feature{% endif %}">
{%- for article in blog.articles -%}
<div class="c-news__item {% if blog.handle == 'feature' %}c-feature__item{% endif %}">
{%- render 'article-card',
article: article,
-%}
</div>
{%- endfor -%}
</div>
{%- if paginate.pages > 1 -%}
{%- render 'pagination', paginate: paginate -%}
{%- endif -%}
</div>
{%- endpaginate -%}
{% schema %}
{
"name": "t:sections.main-blog.name",
"tag": "section",
"class": "l-section p-news-list",
"settings": [
]
}
{% endschema %}実装後、ソートボタンの表示順を変えたいと来ました。
なぜ?
デザインがそのようになっているのでとのことです。
ボツになったのは以下のコードが原因です。※84行目
{% assign ordered_tags = 'INFORMATION,POP UP,CAMPAIGN,WOMEN,MEN,ITEMS,STORY,HOW TO' | split: ',' %}
ボタンの順番をハードコーディング指定しています。
また順序を変えたい時、この方法は一般的に運用する人には荷が重いからという理由です。
タグ表示は全自動で表示非表示ができる反面、順番は自由にできません。
記事の公開順などで変わります。
方法2:メニューで登録した単語をソートボタンにする
メニューにする理由はソートボタン名を管理画面のメニューから登録できるからです。
並び順も自由にドラッグで変更できます。

ブログ記事へのタグ登録は、比べるために普通に必要です。

要件は以下です。
- メニューの単語からソートボタンにする
- 選択されたタグにactiveクラスが適用される
- 12記事ごとのページ分割
- タグフィルター時も正しくページネーションが動作
main-blog.liquid
{%- style -%}
/* 省略 方法1と同じ */
{%- endstyle -%}
{%- paginate blog.articles by 12 -%}
<div class="p-news-list__inner l-section__inner">
<div class="p-news-list__heading c-heading">
<h1 class="p-news-list__title c-heading__title u-font-en-h2">{{ blog.title | escape }}</h1>
</div>
<div class="blog-tag-filter">
{%- if request.locale.name == "English" -%}{% assign lang = '/en' %}{%- else -%}{% assign lang = '' %}{%- endif -%}
<a href="{{ lang }}/blogs/{{ blog.handle }}" class="tag-btn{% unless current_tags %} active{% endunless %}">ALL</a>
{% comment %} メニューからタグを取得(blog-menu-news、blog-menu-feature) {% endcomment %}
{% assign menu_handle = 'blog-menu-' | append: blog.handle %}
{% assign blog_menu = linklists[menu_handle] %}
{% assign menu_to_render = blog.handle %}
{% if blog_menu and blog_menu.links.size > 0 %}
{% for link in blog_menu.links %}
{% comment %} リンクURLからタグ名を抽出 {% endcomment %}
{% assign display_tag = link.title %}
{% comment %} 単語間のスペースはハイフンに変換 {% endcomment %}
{% assign linkurl = link.title | replace: ' ', '-' | downcase | url_encode %}
{% assign current_tags_lower = current_tags | replace: ' ', '-' | downcase %}
{% assign current_tags_array = current_tags_lower | split: ',' %}
{% assign is_active = false %}
{% assign current_tag_raw = current_tags_lower | split: ',' | first | strip %}
{% assign current_tag = current_tag_raw | remove: '[' | remove: ']' | remove: '"' | strip %}
<!-- current_tag:{{ current_tag }}-->
{% if current_tag == linkurl %}
<!-- current_tags_lower:{{ current_tags_lower }}-->
<!-- linkurl:{{ linkurl }}-->
{% assign is_active = true %}
{% endif %}
{% comment %} デバッグ用出力 {% endcomment %}
<!-- current_tags:{{ current_tags }}-->
<!-- linkurl:{{ linkurl }}-->
<!-- linktitle:{{ link.title }}-->
<a href="{{ lang }}/blogs/{{ menu_to_render }}/tagged/{{ linkurl }}" class="tag-btn{% if is_active %} active{% endif %}">{{ link.title }}</a>
{% comment %} デバッグ用出力 {% endcomment %}
<!--デバッグ用出力:{{ lang }}/blogs/{{ menu_to_render }}/tagged/{{ linkurl }}-->
{% endfor %}
{% endif %}
</div>
<div class="p-news-list__list c-news {% if blog.handle == 'feature' %}p-feature-list__list c-feature{% endif %}">
{%- for article in blog.articles -%}
<div class="c-news__item {% if blog.handle == 'feature' %}c-feature__item{% endif %}">
{%- render 'article-card',
article: article,
-%}
</div>
{%- endfor -%}
</div>
{%- if paginate.pages > 1 -%}
{%- render 'pagination', paginate: paginate -%}
{%- endif -%}
</div>
{%- endpaginate -%}
{% schema %}
{
"name": "t:sections.main-blog.name",
"tag": "section",
"class": "l-section p-news-list",
"settings": [
]
}
{% endschema %}デバッグ用出力コード込みです。
かなり時間を割いていましたね。
はい。WOMENとMENで、MENが両方入っているから?同じと判定されるのか、両方アクティブになってしまっていました。
この部分でメニューの単語を整理しています。
{% assign linkurl = link.title | replace: ' ', '-' | downcase | url_encode %}
{% assign current_tags_lower = current_tags | replace: ' ', '-' | downcase %}
{% assign current_tag = current_tag_raw | remove: '[' | remove: ']' | remove: '"' | strip %}
{% if current_tag == linkurl %}
{% assign is_active = true %}
{% endif %}
**処理内容**
1. メニュータイトルをURL用に変換(スペース→ハイフン、小文字化)
2. 現在のタグと比較してアクティブ状態を判定
3. 不要な文字(`[`, `]`, `"`)を除去して正確な比較を実現
処理内容の「3.不要な文字(`[`, `]`, `”`)を除去して正確な比較を実現」が、私には難しく、プログラムが得意な人に助けてもらいました。AIにどう聞いていいのかもわかりませんでした。
配列に不要な[や”が紛れて特殊だったとのこと。
この方法はshopifyの管理画面で、ボタンを増やしたいならメニューに追加するというきちんとした管理をしてもらうことになります。
【妄想】プログラマーの思考プロセス
問題: WOMENとMENが両方アクティブになる
1. 仮説を立てる
- 「MENが両方でアクティブになる」
- → 「文字列比較に問題がある?」
- → 「配列処理に問題がある?」
2. 段階的に確認
- 元データの確認
- 各変換ステップの確認
- 最終的な比較値の確認
3. 問題を特定
- 配列が文字列化されている
- 不要な文字([, ], “)が混入
- 文字列比較が正確でない
デバッグ手順
<!-- 1. 現在のタグを確認 -->
<p>current_tags 生データ: {{ current_tags }}</p>
<!-- 出力例: ["WOMEN", "MEN"] または "WOMEN,MEN" -->
<!-- 2. 分割処理を確認 -->
{% assign tags_array = current_tags | split: ',' %}
<p>分割後の配列:</p>
{% for tag in tags_array %}
<p> {{ forloop.index }}: "{{ tag }}"</p>
{% endfor %}
<!-- 3. 最初の要素を確認 -->
{% assign first_tag = tags_array.first %}
<p>first_tag: "{{ first_tag }}"</p>
<!-- 4. 文字列クリーンアップを確認 -->
{% assign clean_tag = first_tag | remove: '[' | remove: ']' | remove: '"' | strip %}
<p>クリーンアップ後: "{{ clean_tag }}"</p><!-- 実際の出力例 -->
current_tags 生データ: ["WOMEN", "MEN"]
分割後の配列:
1: "["WOMEN""
2: " "MEN"]"
first_tag: "["WOMEN""
クリーンアップ後: "WOMEN"
AIさんにプロセスを見える化してもらいました内容です。
これからこういう妄想路線で行くんですか?
【AI】イラストを描いてもらった
今回の記事のキャッチ画像で使わせてもらいます「Memeplex.app」で作成した画像です。誰でもgoogleアカウントでログインして使えます。
この記事にピッタリなイラストのための考えたリクエストは、「記憶が走馬灯のように目の前を過ぎていく。記憶の映像を操作するUIが異世界転生のアニメのように目の前に現れるコントロールパネル。」です。

星間旅路のメロディ
「宇宙の静けさに包まれながら、漂流する過去の音楽を捜し求め、銀河の奥底でその旋律に耳を傾ける。」
「この電波はどこの星からきたのだろうか。」
どこかで聞いたことがあるような。



