前記事からの続き。コード編集によるセクションの自作方法について。
大抵の機能はアプリでどうにかできるShopifyだけど、作り方さえわかっていれば自分で作れてしまうものも多いのだ。
画像のカルーセルをセクションで自作したので過程をメモっておいた。
ライブラリはSwiperを使うことにします。
※ Dawnにはスライドショーのセクションが含まれているので、それを使うこともできる
セクションファイルの作成
section/image-carousel.liquid
を作成したら、Swiper規定のHTMLをコピペ。
その下にschemaでセクションの設定を入れる。
名前だけ決まってればいいのでとりあえずは日本語で直書きしておく。
<div class="image-carousel swiper">
<div class="image-carousel__wrapper swiper-wrapper">
<div class="image-carousel__slide swiper-slide">Slide 1</div>
<div class="image-carousel__slide swiper-slide">Slide 2</div>
<div class="image-carousel__slide swiper-slide">Slide 3</div>
</div>
<div class="image-carousel__pagination swiper-pagination"></div>
<div class="image-carousel__btn image-carousel__btn--prev swiper-button-prev"></div>
<div class="image-carousel__btn image-carousel__btn--next swiper-button-next"></div>
</div>
{% schema %}
{
"name": "画像のカルーセル",
"settings": [],
"blocks": [],
"presets": []
}
{% endschema %}
schemaで設定できる内容はドキュメント参照で。
これでセクションとして機能する状態になっている。
ホームに表示したいので、テンプレートの設定を変更する。templates/index.json
を開いて、先頭に作成したカルーセルのセクションを規定の形式で追加する。
"一意なID": { "type": "セクションのファイル名" }
{
"sections": {
"image_carousel": {
"type": "image-carousel"
},
"image_banner": {
.....
開発ストアにpush
※テーマが既にpush済みならこの手順は必要ない
CLIで shopify theme push
すると、コンソールにプレビューとオンラインストアエディタのURLが表示されるので、アクセスする。
設定に追加したセクションが追加されていたらOK。shopify theme serve
しておく。
ブロックの設定
カルーセルの中身は自由に追加できるようにしたい。そこでブロックの出番である。
これはセクションのschema
でblocks
に設定を追加するだけでできるのだが、
最初のうちは欲しい機能を持っている他のセクションからコピペする方がミスしにくい。
欲しい設定を section/multicolumn.liquid
が持っているので開いて、必要な部分をコピペする。label
とかは翻訳用にこのような記述になっているのだが、とりあえずそのままでいい。
type
をslide
にすること。
{% schema %}
{
"name": "画像のカルーセル",
"class": "section",
"settings": [
],
"blocks": [
{
"type": "slide",
"name": "t:sections.multicolumn.blocks.column.name",
"settings": [
{
"type": "image_picker",
"id": "image",
"label": "t:sections.multicolumn.blocks.column.settings.image.label"
},
{
"type": "text",
"id": "title",
"default": "Slide",
"label": "t:sections.multicolumn.blocks.column.settings.title.label"
},
{
"type": "url",
"id": "link",
"label": "t:sections.multicolumn.blocks.column.settings.link.label"
}
]
}
],
"presets": []
}
{% endschema %}
編集画面をリロードして「列を追加」が表示されていたらOK。
適当に設定をしておく。
ブロックコンテンツの表示
liquid
でセクションのHTMLを変更していく。
セクションにユニークなIDをつける
<div id="swiper-{{ section.id }}" class="image-carousel swiper">
ブロックをforループで処理する
{%- for block in section.blocks -%}
<div class="image-carousel__slide swiper-slide" {{ block.shopify_attributes }}>Slide 1</div>
{%- endfor -%}
ブロックの画像を表示する
<div class="image-carousel__slide swiper-slide">
{%- if block.settings.image -%}
<img
src="{{ block.settings.image | image_url: width: 1500 }}"
loading="lazy"
alt="{{ block.settings.image.alt | escape }}"
width="{{ block.settings.image.width }}"
height="{{ block.settings.image.width | divided_by: block.settings.image.aspect_ratio }}"
class="image-carousel__slide__img"
>
{%- endif -%}
</div>
非推奨のimg_url
とimage_url
はフィルタの書き方がだいぶ違うので注意。
srcset
などの書き方は image-banenr.liquid
あたりを参考にすると良い。
リンクをつける
{%- if block.settings.link != blank -%}
<a href="{{ block.settings.link }}" title="{{ block.settings.title }}">
{%- endif -%}
{%- if block.settings.link != blank -%}
</a>
{%- endif -%}
セクション用CSSの追加
assets/image-carousel.css
を作成。
/* Container */
.image-carousel {
width: 100%;
}
/* Image */
.image-carousel__slide__img {
width: 100%;
height: auto;
display: block;
}
sections/image-carousel.liquid
にCSSファイルのリンクを追加する。
{{ 'section-image-carousel.css' | asset_url | stylesheet_tag }}
SwiperのCSSトJavaScriptファイルはCDNリンクでもいいんだけど警告が出るので、圧縮ファイルをダウンロードしてaseetに追加したものをリンクする。
{{ 'swiper-bundle.min.css' | asset_url | stylesheet_tag }}
<script src="{{ 'swiper-bundle.min.js' | asset_url }}" defer></script>
scriptタグでdeferを使ったので、Swiperの呼び出しはDOMContentLoaded
イベントで行う。
<script>
document.addEventListener('DOMContentLoaded', () => {
new Swiper('#swiper-{{ section.id }}', {
loop: true,
pagination: {
el: '.swiper-pagination',
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
});
});
</script>
ここまでやると、カルーセルが動くようになる。
あとはスタイルの変更なんかをセクション用CSSでやるだけである。
セクション設定の追加
セクションには個別に設定項目を追加することができる。
各セクションごとに設定を持たせられるので、使いこなせればめちゃくちゃ便利な機能だ。
カルーセルの矢印の表示/非表示をチェックボックスで切り替える、という設定を例にする。
schemaのsettingsブロックに使いたい設定を追加する。
タイプはドキュメントに一覧がある。
"settings": [
{
"type": "checkbox",
"id": "swiper_navigation_display",
"default": true,
"label": "ナビゲーションの矢印を表示する"
}
],
これだけで管理画面には設定項目が表示される。
section.settings に設定が保存されている。
shcemaでつけたIDで値を呼び出すことができる。
{%- if section.settings.swiper_navigation_display == true -%}
<div class="image-carousel__btn image-carousel__btn--prev swiper-button-prev" aria-label="Prev">
</div>
<div class="image-carousel__btn image-carousel__btn--next swiper-button-next" aria-label="Next">
</div>
{%- endif -%}
Dawnのbase.cssで :empty
擬似要素をdisplay:none
してあるので、改行を追加している。
テーマエディタとの連動
ナビゲーションの矢印を表示するチェックボックスをON/OFFすると、カルーセルの機能が失われることに気づくと思う。
これは、セクションの編集を行った時にページ全体をリロードしてるっぽく見えるが、実際には操作されたセクションだけ再レンダリングが行われているために、DOMContentLoadedイベントのコールバックが実行されないためである。
解決方法はドキュメントに書いてあるとおり、Shopifyのテーマエディタが発行するカスタムイベントをトリガーにしてJavaScriptを実行するように修正する。
document.addEventListener('DOMContentLoaded', () => {
const slides = document.querySelectorAll(`#swiper-{{ section.id }} .image-carousel__slide`)
if (!slides.length) return;
const options = {
slidesPerView: "auto",
centeredSlides: true,
loop: true,
spaceBetween: 24,
pagination: {
el: '.swiper-pagination',
},
{%- if section.settings.swiper_navigation_display == true -%}
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
{%- endif -%}
}
let swiper = new Swiper('#swiper-{{ section.id }}', options);
{% if request.design_mode %}
// デザインモード時に実行
document.addEventListener('shopify:section:unload', (e) => {
if (swiper && e.detail.sectionId === '{{ section.id }}') {
console.log('shopify:section:unload')
swiper.destroy()
}
})
document.addEventListener('shopify:section:load', (e) => {
if (e.detail.sectionId === '{{ section.id }}'){
console.log('shopify:section:load')
swiper = new Swiper('#swiper-{{ section.id }}', options);
}
})
{% endif %}
});
ブロックの操作
設定済みのブロックを操作したり新しいブロックを追加した時に、カルーセルをそのブロックまで動かすには?
data属性の data-shopify-editor-block
がブロックIDの情報を持っているのでparseして比較すればいいんだと思う。
document.addEventListener('shopify:block:select', (e) => {
if (e.detail.sectionId !== '{{ section.id }}') return;
const slides = document.querySelectorAll(`#swiper-{{ section.id }} .image-carousel__slide`)
if (!slides) return;
const index = Array.from(slides).findIndex((el) => {
const info = el.getAttribute('data-shopify-editor-block')
if (!info) return
const data = JSON.parse(info)
return (data.id === e.detail.blockId)
})
if (index >= 0 && swiper) {
swiper.slideTo(index)
}
})
選択に合わせてスライドが動けばおk。
翻訳の追加と利用
locales/ja.schema.json
に追加したセクションの設定を追加する。locales/en.default.schema.json
にも同じ設定の英語版を追加しておく。
"image_carousel": {
"name": "画像のカルーセル",
"settings": {
"swiper_navigation_display": {
"label": "ナビゲーションの矢印を表示する"
}
},
"blocks": {
"slide": {
"name": "スライド",
"settings": {
"image": {
"label": "画像"
},
"title": {
"label": "見出し"
},
"link": {
"label": "リンク"
}
}
}
},
"presets": {
"name": "画像のカルーセル"
}
}
これにより、sections/image-carousel.liquid 内の schema
で翻訳が利用できるようになる。
{
"name": "t:sections.image_carousel.name",
"tag": "section",
"class": "section",
....
テーマのHTMLで利用する翻訳は locales/ja.json
のsections
内に追加する。locales/en.default.json
にも英語版を追加する。
"image_carousel": {
"pagination": "スライドページナビゲーション"
}
<div class="image-carousel__pagination swiper-pagination"
aria-label="{{ 'sections.image_carousel.pagination' | t }}"
></div>
参考
- https://shopify.dev/themes/architecture/sections/section-schema
- https://shopify.dev/api/liquid/objects/block
- https://shopify.dev/themes/architecture/templates/json-templates
- https://shopify.dev/themes/architecture/config
- https://shopify.dev/themes/architecture/locales
- https://shopify.dev/themes/architecture/settings
- https://shopify.dev/themes/architecture/sections/integrate-sections-with-the-theme-editor