目次
ショッピングサイトのTOPページにあるメインビジュアルのswiperスライダーで、ボタンで女性/男性のビジュアルを切り替えたいと言う要件がありました。
以下のような実装をしていましたところ。
関連記事:【cookie】ジェンダー切り替えボタン
https://neo.astrowave.jp/blog/21533/
サンプル
https://astrowave.jp/amnesia_record/gender_cookie.php

これに修正依頼がありました。
どういう修正ですか?
「WOMENとMENのイベントの数が一緒とは限らない。」と言うのです。
つまり、WOMENとMENは必ず対になっているわけでなく、WOMENが1つで、MENが3つの時も想定されるので修正したい。という修正です。
この「男女対イベント想定」の仕様では破綻してしまいます。
スライド(1つ)の中にWOMEN/MENの画像を入れ込んで、それをボタンで切替え表示していました。
WOMENだけのイベント登録だと、切替えるとMENはグレー色?
イベントが必ず対になる想定なんて、普通しますかね。。
(;゚□゚)ガーン!!
WOMENとMENのイベントの数が違っても破綻しない、柔軟なジェンダー切り替えスライダーにしたいです。
【共有】実装手順とliquidコード
柔軟なジェンダー切り替えスライダーの実装コードを共有です。
要件は以下です。
- コピペで実装可能
- 柔軟なスライド数: WOMENが5枚、MENが3枚でもOK
- Shopify管理画面: 直感的な設定画面
- Cookie保存: ユーザーの選択を30日間記憶
- URLパラメータ対応: ?gender=men で直接アクセス可能
- 動画対応: 画像だけでなく動画スライドも可能
- Safari低電力モード対応: 自動検出機能付き
ステップ1: ファイル作成
- sections/slideshow.liquid を作成
ステップ 2: 管理画面設定
- テーマカスタマイザー → セクションを追加 → Slideshow
- ブロックを追加して画像・テキストを設定
ステップ 3: 動作確認
- https://yourstore.com/?gender=women → WOMEN表示
- https://yourstore.com/?gender=men → MEN表示
全て1つにまとめたコードが以下のslideshow.liquidです。
sections/slideshow.liquid
<!-- Swiper CDN追加 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
<style>
/* 既存のスタイル */
/* スライダー切り替え用のスタイル */
.p-top-hero__slider {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 0.5s ease, visibility 0.5s ease;
}
.p-top-hero__slider.slider-women {
position: relative;
}
.p-top-hero__slider.is-active {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
/* Safari省電力モード時の動画を隠す */
body.safari-low-power-mode .c-slider-hero__video {
display: none !important;
}
/* 他のセクション用のgender切り替えスタイル */
.gender-switch-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.gender-content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 0.5s ease, visibility 0.5s ease;
}
.gender-content.gender-women {
position: relative;
}
.gender-content.is-active {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
.c-slider-hero .swiper-slide::after {
content: "";
display: block;
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 15%;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0)), to(rgb(0, 0, 0)));
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.35) 100%);
opacity: 1;
z-index: 2;
}
.p-top-hero__slider .c-slider-hero__image img,
.p-top-hero__slider .c-slider-hero__image svg {
width: 100%;
height: 100vh;
object-fit: cover;
object-position: center top;
}
@media screen and (min-width: 768px) {
.c-slider-hero__content {
/* top: 50%;
transform: translateY(50%); */
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
.c-slider-hero__content a {
width: 93.75%;
align-items: center;
}
.c-slider-hero__title {
width: 100%;
font-family: helvetica-neue-lt-pro, Gothic MB101 DemiBold, sans-serif !important;
font-weight: 700;
font-size: 32px !important;
line-height: 110%;
letter-spacing: -0.01em;
text-shadow: 0px 0px 15px rgba(0, 0, 0, 0.05);
}
.c-slider-hero__button {
margin-top: 18px;
}
.c-link-parent__button {
min-width: 120px;
text-align: center;
padding-top: 12.5px;
padding-right: 20px;
padding-bottom: 11.5px;
padding-left: 20px;
border-radius: 100px;
border: 1px solid #fff;
background: transparent;
font-family: helvetica-neue-lt-pro, Gothic MB101 DemiBold, sans-serif;
font-weight: 600;
font-size: 12px;
line-height: 100%;
letter-spacing: 0.05em;
color: #fff;
text-decoration: none;
text-shadow: 0px 0px 15px rgba(0, 0, 0, 0.2);
background-color: rgba(255, 255, 255, 0.1);
transition: background-color .3s ease, color .3s ease;
}
@media(any-hover: hover) {
.c-link-parent__button:hover {
background-color: #fff;
}
.c-link-parent__button:hover {
color: #000A82;
}
}
.gender {
position: absolute;
bottom: 40px;
right: 40px;
z-index: 1;
display: flex;
width: 286px;
border: 0px solid #fff;
}
.gender li {
width: 50%;
}
.gender li a {
display: block;
width: 100%;
padding: 14px 0;
font-weight: 700;
font-size: 12px;
line-height: 1;
letter-spacing: 0.01em;
text-align: center;
background: #ffffff;
transition: all 0.3s ease;
}
.gender li a span {
opacity: 100%;
color: #adadad;
}
.gender li a.active{
font-family: helvetica-neue-lt-pro;
font-weight: 700;
font-size: 12px;
line-height: 1;
letter-spacing: 0.01em;
background: #000A82;
}
.gender li a.active span {
opacity: 100%;
color: #FFFFFF;
}
@media(any-hover: hover) {
.gender li a:hover {
background-color: #000A82;
}
.gender li a:hover span {
color: #FFFFFF;
}
.gender li a:hover.active {
background-color: #000A82;
}
.gender li a:hover.active span {
color: #FFFFFF;
}
}
/* ページネーション */
.special-swiper-container .swiper-pagination {
width: auto !important;
left: 15px !important;
bottom: 20px !important;
text-align: left;
}
.special-swiper-container .swiper-pagination-bullet{
position: relative;
width: 10px;
height: 10px;
border-radius: 50%;
border: 1px solid #FFF;
opacity: 1;
margin:0 var(--swiper-pagination-bullet-horizontal-gap,4.5px) !important;
}
.c-slider-hero__pagination {
position: absolute;
left: 0;
bottom: 38px;
right: 0;
text-align: left;
margin: 0 40px;
z-index: 2;
}
.c-slider-hero__pagination .swiper-pagination-bullet {
width: 8px;
height: 8px;
margin: 0 3px !important;
}
.special-swiper-container .swiper-pagination-bullet:not(.swiper-pagination-bullet-active) {
background: transparent !important;
border: 0px solid #FFF;
}
.special-swiper-container .swiper-pagination-bullet::before {
content: "";
position: absolute;
width:10px;
height: 10px;
background:#FFF !important;
top: 0;
left: 0;
border: 0px solid #FFF;
border-radius: 50%;
transition: transform .3s;
transform: scale(0.3)
}
.special-swiper-container .swiper-pagination-bullet.swiper-pagination-bullet-active::before{
opacity: 0;
transform: scale(1);
trasition: none;
}
}
@media screen and (max-width: 767px) {
.c-slider-hero__content {
/* top: 50%;
transform: translateY(50%); */
height: unset;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
.c-slider-hero__content a {
width: 93.75%;
height: unset;
min-height: 100px;
align-items: center;
}
.c-slider-hero__title {
width: 100%;
font-family: helvetica-neue-lt-pro, Gothic MB101 DemiBold, sans-serif !important;
font-weight: 700;
font-size: 24px;
line-height: 110%;
letter-spacing: -0.02em;
text-align: center;
text-shadow: 0px 0px 15px rgba(0, 0, 0, 0.05);
}
.c-slider-hero__button {
margin-top: 18px;
}
.c-link-parent__button {
min-width: 90px;
text-align: center;
padding-top: 9.5px;
padding-right: 15px;
padding-bottom: 9.5px;
padding-left: 15px;
border-radius: 100px;
border: 1px solid #fff;
background: transparent;
font-family: helvetica-neue-lt-pro, Gothic MB101 DemiBold, sans-serif;
font-weight: 600;
font-size: 11px;
line-height: 100%;
letter-spacing: 0.05em;
color: #fff;
text-decoration: none;
text-shadow: 0px 0px 10px rgba(0, 0, 0, 0.25);
background-color: rgba(255, 255, 255, 0.1);
transition: background-color .3s ease, color .3s ease;
}
.c-slider-hero__pagination .swiper-pagination-bullet {
width: 8px;
height: 8px;
margin: 0 3px !important;
}
.c-slider-hero__pagination {
position: absolute;
left: 0;
bottom: 58px;
right: 0;
text-align: center;
z-index: 2;
}
.gender {
position: absolute;
bottom: 0;
right: 0;
z-index: 1;
display: flex;
width: 100%;
border-top: 0px solid #fff;
border-bottom: 0px solid #fff;
}
.gender li {
width: 50%;
}
.gender li a {
display: block;
width: 100%;
padding: 14px 0;
font-weight: 700;
font-size: 12px;
line-height: 1;
letter-spacing: 0.01em;
text-align: center;
background: #ffffff;
transition: all 0.3s ease;
}
.gender li a span {
opacity: 100%;
color: #adadad;
}
.gender li a.active{
font-family: helvetica-neue-lt-pro;
font-weight: 700;
font-size: 12px;
line-height: 1;
letter-spacing: 0.01em;
background: #000A82;
}
.gender li a.active span {
opacity: 100%;
color: #FFFFFF;
}
}
</style>
<div class="l-section p-top-hero js-section-top-hero js-slider-hero2">
{% if template == 'index' %}
<h1 class="p-top-hero__logo">
<img src="{{ 'logo_white.svg' | asset_url }}" width="331" height="35" alt="Your Brand Name">
</h1>
{% endif %}
<!-- Women用スライダー -->
<div class="p-top-hero__slider slider-women swiper c-slider-hero js-slider-women is-active">
<div class="swiper-wrapper">
{%- for block in section.blocks -%}
{%- if block.settings.image != blank -%}
<div class="swiper-slide">
<div class="c-slider-hero__image">
{%- liquid
assign sizes = '100vw'
assign widths = '375, 550, 750, 1100, 1500, 1780, 2000, 3000, 3840'
assign fetch_priority = 'auto'
if section.index == 1
assign fetch_priority = 'high'
endif
-%}
{%- assign htmlalt = block.settings.heading | strip_html -%}
{%- if forloop.first %}
<picture>
<source
media="(max-width: 767px)"
srcset="
{{ block.settings.image_sp | image_url: width: 375 }} 375w,
{{ block.settings.image_sp | image_url: width: 550 }} 550w,
{{ block.settings.image_sp | image_url: width: 750 }} 750w,
{{ block.settings.image_sp | image_url: width: 1100 }} 1100w,
{{ block.settings.image_sp | image_url: width: 1500 }} 1500w,
{{ block.settings.image_sp | image_url: width: 1780 }} 1780w,
{{ block.settings.image_sp | image_url: width: 2000 }} 2000w,
{{ block.settings.image_sp | image_url: width: 3000 }} 3000w,
{{ block.settings.image_sp | image_url: width: 3840 }} 3840w,
">
{{
block.settings.image
| image_url: width: 3840
| image_tag: sizes: sizes, widths: widths, fetchpriority: fetch_priority,alt:htmlalt
}}
</picture>
{%- else -%}
<picture>
<source
media="(max-width: 767px)"
srcset="
{{ block.settings.image_sp | image_url: width: 375 }} 375w,
{{ block.settings.image_sp | image_url: width: 550 }} 550w,
{{ block.settings.image_sp | image_url: width: 750 }} 750w,
{{ block.settings.image_sp | image_url: width: 1100 }} 1100w,
{{ block.settings.image_sp | image_url: width: 1500 }} 1500w,
{{ block.settings.image_sp | image_url: width: 1780 }} 1780w,
{{ block.settings.image_sp | image_url: width: 2000 }} 2000w,
{{ block.settings.image_sp | image_url: width: 3000 }} 3000w,
{{ block.settings.image_sp | image_url: width: 3840 }} 3840w,
">
{{
block.settings.image
| image_url: width: 3840
| image_tag: loading: 'lazy', sizes: sizes, widths: widths,alt:htmlalt
}}
</picture>
{%- endif -%}
{% comment %} Women用動画URLが設定されている場合 {% endcomment %}
{%- if block.settings.video -%}
{%- if block.settings.video_sp -%}
<video class="js-video-common c-slider-hero__video c-slider-hero__video--responsive"
data-poster-pc="{{ block.settings.image | image_url: width: 3840 }}"
poster="{{ block.settings.image_sp | image_url: width: 3840 }}"
muted playsinline autoplay loop control="false">
<source media="(max-width: 767px)" src="{{ block.settings.video_sp.sources[1].url }}" type="video/mp4">
<source media="(min-width: 768px)" src="{{ block.settings.video.sources[1].url }}" type="video/mp4">
</video>
{%- else -%}
<video class="js-video-common c-slider-hero__video c-slider-hero__video--pc"
poster="{{ block.settings.image | image_url: width: 3840 }}"
muted playsinline autoplay loop control="false" src="{{ block.settings.video.sources[1].url }}"></video>
{%- endif -%}
{%- else -%}
{%- if block.settings.video_sp -%}
<video class="js-video-common c-slider-hero__video c-slider-hero__video--sp"
poster="{{ block.settings.image_sp | image_url: width: 3840 }}"
muted playsinline autoplay loop control="false" src="{{ block.settings.video_sp.sources[1].url }}"></video>
{%- endif -%}
{%- endif -%}
</div>
<div class="c-slider-hero__content">
{% if block.settings.link %}
<a class="c-link-parent" href="{{ block.settings.link }}">
{%- endif -%}
{%- if block.settings.heading != blank -%}
<h2 class="c-slider-hero__title u-font-en-h1 u-font-en-bold u-font-ja-bold">{{ block.settings.heading }}</h2>
{%- endif -%}
{%- if block.settings.button_label != blank -%}
<div class="c-slider-hero__button">
<span class="c-link-parent__button">
{{ block.settings.button_label }}
</span>
</div>
{%- endif -%}
{% if block.settings.link %}
</a>
{%- endif -%}
</div>
</div>
{%- endif -%}
{%- endfor -%}
</div>
<div class="c-slider-hero__pagination c-slider-hero__pagination--white js-slider-pagination-women"></div>
</div>
<!-- Men用スライダー -->
<div class="p-top-hero__slider slider-men swiper c-slider-hero js-slider-men">
<div class="swiper-wrapper">
{%- for block in section.blocks -%}
{%- if block.settings.image_men != blank -%}
<div class="swiper-slide">
<div class="c-slider-hero__image">
{%- liquid
assign sizes = '100vw'
assign widths = '375, 550, 750, 1100, 1500, 1780, 2000, 3000, 3840'
assign fetch_priority = 'auto'
if section.index == 1
assign fetch_priority = 'high'
endif
-%}
{%- assign htmlalt = block.settings.heading_men | strip_html -%}
{%- if forloop.first %}
<picture>
<source
media="(max-width: 767px)"
srcset="
{{ block.settings.image_sp_men | image_url: width: 375 }} 375w,
{{ block.settings.image_sp_men | image_url: width: 550 }} 550w,
{{ block.settings.image_sp_men | image_url: width: 750 }} 750w,
{{ block.settings.image_sp_men | image_url: width: 1100 }} 1100w,
{{ block.settings.image_sp_men | image_url: width: 1500 }} 1500w,
{{ block.settings.image_sp_men | image_url: width: 1780 }} 1780w,
{{ block.settings.image_sp_men | image_url: width: 2000 }} 2000w,
{{ block.settings.image_sp_men | image_url: width: 3000 }} 3000w,
{{ block.settings.image_sp_men | image_url: width: 3840 }} 3840w,
">
{{
block.settings.image_men
| image_url: width: 3840
| image_tag: sizes: sizes, widths: widths, fetchpriority: fetch_priority,alt:htmlalt
}}
</picture>
{%- else -%}
<picture>
<source
media="(max-width: 767px)"
srcset="
{{ block.settings.image_sp_men | image_url: width: 375 }} 375w,
{{ block.settings.image_sp_men | image_url: width: 550 }} 550w,
{{ block.settings.image_sp_men | image_url: width: 750 }} 750w,
{{ block.settings.image_sp_men | image_url: width: 1100 }} 1100w,
{{ block.settings.image_sp_men | image_url: width: 1500 }} 1500w,
{{ block.settings.image_sp_men | image_url: width: 1780 }} 1780w,
{{ block.settings.image_sp_men | image_url: width: 2000 }} 2000w,
{{ block.settings.image_sp_men | image_url: width: 3000 }} 3000w,
{{ block.settings.image_sp_men | image_url: width: 3840 }} 3840w,
">
{{
block.settings.image_men
| image_url: width: 3840
| image_tag: loading: 'lazy', sizes: sizes, widths: widths,alt:htmlalt
}}
</picture>
{%- endif -%}
{% comment %} Men用動画URLが設定されている場合 {% endcomment %}
{%- if block.settings.video_men -%}
{%- if block.settings.video_sp_men -%}
<video class="js-video-common c-slider-hero__video c-slider-hero__video--responsive"
data-poster-pc="{{ block.settings.image_men | image_url: width: 3840 }}"
poster="{{ block.settings.image_sp_men | image_url: width: 3840 }}"
muted playsinline autoplay loop control="false">
<source media="(max-width: 767px)" src="{{ block.settings.video_sp_men.sources[1].url }}" type="video/mp4">
<source media="(min-width: 768px)" src="{{ block.settings.video_men.sources[1].url }}" type="video/mp4">
</video>
{%- else -%}
<video class="js-video-common c-slider-hero__video c-slider-hero__video--pc"
poster="{{ block.settings.image_men | image_url: width: 3840 }}"
muted playsinline autoplay loop control="false" src="{{ block.settings.video_men.sources[1].url }}"></video>
{%- endif -%}
{%- else -%}
{%- if block.settings.video_sp_men -%}
<video class="js-video-common c-slider-hero__video c-slider-hero__video--sp"
poster="{{ block.settings.image_sp_men | image_url: width: 3840 }}"
muted playsinline autoplay loop control="false" src="{{ block.settings.video_sp_men.sources[1].url }}"></video>
{%- endif -%}
{%- endif -%}
</div>
<div class="c-slider-hero__content">
{% if block.settings.link_men %}
<a class="c-link-parent" href="{{ block.settings.link_men }}">
{%- endif -%}
{%- if block.settings.heading_men != blank -%}
<h2 class="c-slider-hero__title u-font-en-h1 u-font-en-bold u-font-ja-bold">{{ block.settings.heading_men }}</h2>
{%- endif -%}
{%- if block.settings.button_label_men != blank -%}
<div class="c-slider-hero__button">
<span class="c-link-parent__button">
{{ block.settings.button_label_men }}
</span>
</div>
{%- endif -%}
{% if block.settings.link_men %}
</a>
{%- endif -%}
</div>
</div>
{%- endif -%}
{%- endfor -%}
</div>
<div class="c-slider-hero__pagination c-slider-hero__pagination--white js-slider-pagination-men"></div>
</div>
<ul class="gender">
<li><a href="#" class="gender-btn active" data-gender="women"><span>WOMEN</span></a></li>
<li><a href="#" class="gender-btn" data-gender="men"><span>MEN</span></a></li>
</ul>
</div>
{%- if request.design_mode -%}
<script src="{{ 'theme-editor.js' | asset_url }}" defer="defer"></script>
{%- endif -%}
<!-- Swiper JS CDN -->
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<script>
// ページネーションアニメーション用の変数
var paginationAnimationId;
// ページネーションのスムーズアニメーション関数
function animatePaginationSmooth(activeBullet, duration) {
duration = duration || 7000;
if (!activeBullet) return;
var start = null;
if (paginationAnimationId) {
cancelAnimationFrame(paginationAnimationId);
}
function step(timestamp) {
if (!start) start = timestamp;
var elapsed = timestamp - start;
var progress = Math.min((elapsed / duration) * 100, 100);
activeBullet.style.background = 'conic-gradient(#FFF 0%, #FFF ' + progress + '%, transparent ' + progress + '%, transparent 100%)';
if (elapsed < duration) {
paginationAnimationId = requestAnimationFrame(step);
} else {
activeBullet.style.background = 'conic-gradient(#FFF 0%, #FFF 100%, transparent 100%, transparent 100%)';
}
}
paginationAnimationId = requestAnimationFrame(step);
}
// Cookie操作のヘルパー関数
function setCookie(name, value, days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + value + expires + "; path=/";
}
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
// ジェンダー切り替えの状態を保存する関数
function saveGenderState(gender) {
setCookie('selected_gender', gender, 30); // 30日間保持
}
// ジェンダー切り替えの状態を復元する関数
function restoreGenderState() {
var savedGender = getCookie('selected_gender');
if (savedGender && (savedGender === 'women' || savedGender === 'men')) {
return savedGender;
}
return 'women'; // デフォルトはwomen
}
// スライダーインスタンスを格納する変数
var womenSlider = null;
var menSlider = null;
// 初期化フラグ
var womenInitialized = false;
var menInitialized = false;
// スライダーの設定値
// ★ スライド速度を変更したい場合は、この値のみを変更してください ★
// ページネーションアニメーションも自動的に連動します
var SLIDER_AUTOPLAY_DELAY = 7000; // 単位: ミリ秒 (7000 = 7秒)
// ジェンダー切り替えの表示を更新する関数
function updateGenderDisplay(gender) {
// ボタンの状態を更新
var genderBtns = document.querySelectorAll('.gender-btn');
genderBtns.forEach(function(btn) {
if (btn.getAttribute('data-gender') === gender) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
// スライダーの表示を更新
var womenSliderEl = document.querySelector('.slider-women');
var menSliderEl = document.querySelector('.slider-men');
if (gender === 'women') {
if (womenSliderEl) {
womenSliderEl.classList.add('is-active');
}
if (menSliderEl) {
menSliderEl.classList.remove('is-active');
}
// Women用スライダーのautoplayを開始
if (womenSlider && womenSlider.autoplay) {
womenSlider.autoplay.start();
}
// Men用スライダーのautoplayを停止
if (menSlider && menSlider.autoplay) {
menSlider.autoplay.stop();
}
// Women用のページネーションアニメーションを開始(初期化済みの場合はスキップ)
setTimeout(function() {
if (!womenInitialized) {
var womenPagination = document.querySelector('.js-slider-pagination-women');
if (womenPagination) {
var activeBullet = womenPagination.querySelector('.swiper-pagination-bullet-active');
if (activeBullet) {
animatePaginationSmooth(activeBullet, SLIDER_AUTOPLAY_DELAY);
}
}
}
}, 100);
} else if (gender === 'men') {
if (womenSliderEl) {
womenSliderEl.classList.remove('is-active');
}
if (menSliderEl) {
menSliderEl.classList.add('is-active');
}
// Men用スライダーのautoplayを開始
if (menSlider && menSlider.autoplay) {
menSlider.autoplay.start();
}
// Women用スライダーのautoplayを停止
if (womenSlider && womenSlider.autoplay) {
womenSlider.autoplay.stop();
}
// Men用のページネーションアニメーションを開始(初期化済みの場合はスキップ)
setTimeout(function() {
if (!menInitialized) {
var menPagination = document.querySelector('.js-slider-pagination-men');
if (menPagination) {
var activeBullet = menPagination.querySelector('.swiper-pagination-bullet-active');
if (activeBullet) {
animatePaginationSmooth(activeBullet, SLIDER_AUTOPLAY_DELAY);
}
}
}
}, 100);
}
// 他のセクションのgender-switch-wrapper要素も更新
document.querySelectorAll('.gender-switch-wrapper').forEach(function(wrapper) {
var all = wrapper.querySelectorAll('.gender-content');
var target = wrapper.querySelector('.gender-content.gender-' + gender);
if (target && all.length > 0) {
fadeSwitch(target, all);
}
});
// デバッグ用ログ(開発時のみ)
if (window.location.hostname === 'localhost' || window.location.hostname.includes('dev')) {
console.log('Gender updated to:', gender, 'Found wrappers:', document.querySelectorAll('.gender-switch-wrapper').length);
}
}
// ジェンダー切り替え
function fadeSwitch(target, all) {
if (!target || !all || all.length === 0) return;
all.forEach(function(el) {
if (el === target) {
el.classList.add('is-active');
el.style.opacity = '1';
el.style.visibility = 'visible';
el.style.pointerEvents = 'auto';
} else {
el.classList.remove('is-active');
el.style.opacity = '0';
el.style.visibility = 'hidden';
el.style.pointerEvents = 'none';
}
});
}
// スライダーを初期化する関数
function initializeSliders() {
// Women用スライダーを初期化
var womenSliderEl = document.querySelector('.js-slider-women');
var womenPaginationEl = document.querySelector('.js-slider-pagination-women');
if (womenSliderEl && typeof Swiper !== 'undefined') {
var womenSlides = womenSliderEl.querySelectorAll('.swiper-slide');
if (womenSlides.length > 0) {
womenSlider = new Swiper(womenSliderEl, {
speed: 900,
slidesPerView: 1,
loop: womenSlides.length > 1,
threshold: 10,
autoplay: womenSlides.length > 1 ? {
delay: SLIDER_AUTOPLAY_DELAY,
disableOnInteraction: false,
pauseOnMouseEnter: false
} : false,
pagination: {
el: womenPaginationEl,
clickable: true
},
on: {
init: function() {
// 初期化時にアクティブなスライダーのみアニメーション開始(短めのアニメーション)
setTimeout(function() {
var womenSliderContainer = document.querySelector('.slider-women');
if (womenSliderContainer && womenSliderContainer.classList.contains('is-active') && womenPaginationEl) {
var activeBullet = womenPaginationEl.querySelector('.swiper-pagination-bullet-active');
if (activeBullet) {
womenInitialized = true;
// 初回のみ適切な長さのアニメーション(6.5秒)
animatePaginationSmooth(activeBullet, 6500);
}
}
}, 300);
},
autoplayStart: function() {
// autoplay開始と同時にアニメーション開始(初期化済みの場合はスキップ)
if (!womenInitialized && womenPaginationEl) {
var activeBullet = womenPaginationEl.querySelector('.swiper-pagination-bullet-active');
if (activeBullet) {
animatePaginationSmooth(activeBullet, SLIDER_AUTOPLAY_DELAY);
}
}
},
slideChangeTransitionStart: function() {
// スライド切り替え時のページネーションアニメーション
if (womenPaginationEl) {
// すべてのページネーションアイコンをリセット
var allBullets = womenPaginationEl.querySelectorAll('.swiper-pagination-bullet');
allBullets.forEach(function(bullet) {
bullet.style.background = '';
});
// アクティブなアイコンのアニメーション開始
var activeBullet = womenPaginationEl.querySelector('.swiper-pagination-bullet-active');
if (activeBullet) {
animatePaginationSmooth(activeBullet, SLIDER_AUTOPLAY_DELAY);
}
}
}
}
});
}
}
// Men用スライダーを初期化
var menSliderEl = document.querySelector('.js-slider-men');
var menPaginationEl = document.querySelector('.js-slider-pagination-men');
if (menSliderEl && typeof Swiper !== 'undefined') {
var menSlides = menSliderEl.querySelectorAll('.swiper-slide');
if (menSlides.length > 0) {
menSlider = new Swiper(menSliderEl, {
speed: 900,
slidesPerView: 1,
loop: menSlides.length > 1,
threshold: 10,
autoplay: menSlides.length > 1 ? {
delay: SLIDER_AUTOPLAY_DELAY,
disableOnInteraction: false,
pauseOnMouseEnter: false
} : false,
pagination: {
el: menPaginationEl,
clickable: true
},
on: {
init: function() {
// 初期化時にアクティブなスライダーのみアニメーション開始(短めのアニメーション)
setTimeout(function() {
var menSliderContainer = document.querySelector('.slider-men');
if (menSliderContainer && menSliderContainer.classList.contains('is-active') && menPaginationEl) {
var activeBullet = menPaginationEl.querySelector('.swiper-pagination-bullet-active');
if (activeBullet) {
menInitialized = true;
// 初回のみ適切な長さのアニメーション(6.5秒)
animatePaginationSmooth(activeBullet, 6500);
}
}
}, 300);
},
autoplayStart: function() {
// autoplay開始と同時にアニメーション開始(初期化済みの場合はスキップ)
if (!menInitialized && menPaginationEl) {
var activeBullet = menPaginationEl.querySelector('.swiper-pagination-bullet-active');
if (activeBullet) {
animatePaginationSmooth(activeBullet, SLIDER_AUTOPLAY_DELAY);
}
}
},
slideChangeTransitionStart: function() {
// スライド切り替え時のページネーションアニメーション
if (menPaginationEl) {
// すべてのページネーションアイコンをリセット
var allBullets = menPaginationEl.querySelectorAll('.swiper-pagination-bullet');
allBullets.forEach(function(bullet) {
bullet.style.background = '';
});
// アクティブなアイコンのアニメーション開始
var activeBullet = menPaginationEl.querySelector('.swiper-pagination-bullet-active');
if (activeBullet) {
animatePaginationSmooth(activeBullet, SLIDER_AUTOPLAY_DELAY);
}
}
}
}
});
}
}
}
document.addEventListener('DOMContentLoaded', function() {
// スライダーを初期化
initializeSliders();
// URLパラメータを優先し、なければCookieから復元
var urlParams = new URLSearchParams(window.location.search);
var urlGender = urlParams.get('gender');
var currentGender;
if (urlGender && (urlGender === 'women' || urlGender === 'men')) {
// URLパラメータがある場合はそれを優先
currentGender = urlGender;
// URLパラメータの状態もCookieに保存
saveGenderState(currentGender);
} else {
// URLパラメータがない場合はCookieから復元
currentGender = restoreGenderState();
}
// ページ全体のgender表示を更新
updateGenderDisplay(currentGender);
// ジェンダーボタンのクリックイベント
var genderBtns = document.querySelectorAll('.gender-btn');
genderBtns.forEach(function(btn) {
btn.addEventListener('click', function(e) {
e.preventDefault();
var gender = this.getAttribute('data-gender');
// ページ全体の表示を更新
updateGenderDisplay(gender);
// 状態をCookieに保存
saveGenderState(gender);
});
});
});
// ページ読み込み完了後に再度表示を更新(他のセクションの読み込み完了を待つ)
window.addEventListener('load', function() {
// スライダーが正しく初期化されていない場合は再初期化
if (!womenSlider || !menSlider) {
initializeSliders();
}
var currentGender = restoreGenderState();
updateGenderDisplay(currentGender);
// Safari低電力モード検出
detectLowPowerMode();
});
// Safari低電力モード検出関数(シンプル版)
function detectLowPowerMode() {
// Safari検出
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
if (!isSafari) return;
// 全ての動画要素を取得
const videos = document.querySelectorAll('.c-slider-hero__video');
videos.forEach(function(video) {
// autoplay試行
const playPromise = video.play();
if (playPromise !== undefined) {
playPromise.catch(function(error) {
// autoplay失敗 = 低電力モード
document.body.classList.add('safari-low-power-mode');
});
}
// 1秒後に再生状態をチェック
setTimeout(function() {
if (video.paused && video.currentTime === 0) {
// 再生されていない = 低電力モード
document.body.classList.add('safari-low-power-mode');
}
}, 1000);
});
}
</script>
{% schema %}
{
"name": "t:sections.slideshow.name",
"tag": "section",
"class": "section",
"disabled_on": {
"groups": ["header", "footer"]
},
"settings": [
{
"type": "header",
"content": "t:sections.slideshow.settings.accessibility.content"
},
{
"type": "text",
"id": "accessibility_info",
"label": "t:sections.slideshow.settings.accessibility.label",
"info": "t:sections.slideshow.settings.accessibility.info",
"default": "Slideshow about our brand"
}
],
"blocks": [
{
"type": "slide",
"name": "t:sections.slideshow.blocks.slide.name",
"limit": 10,
"settings": [
{
"type": "header",
"content": "WOMEN用スライド設定"
},
{
"type": "paragraph",
"content": "Women用スライダーに表示される内容を設定します。画像が設定されていない場合、このスライドはWomen用スライダーに表示されません。"
},
{
"type": "image_picker",
"id": "image",
"label": "画像(PC・タブレット用)"
},
{
"type": "image_picker",
"id": "image_sp",
"label": "画像(スマートフォン用)"
},
{
"type": "richtext",
"id": "heading",
"default": "<p>Image slide</p>",
"label": "見出しテキスト"
},
{
"type": "text",
"id": "button_label",
"default": "くわしく見る",
"label": "ボタンテキスト",
"info": "ボタンに表示されるテキストを入力してください"
},
{
"type": "url",
"id": "link",
"label": "リンク先URL"
},
{
"type": "video",
"id": "video",
"label": "動画(PC・タブレット用)",
"info": "画像の代わりに動画を表示します。横長サイズ推奨(16:9)"
},
{
"type": "video",
"id": "video_sp",
"label": "動画(スマートフォン用)",
"info": "スマートフォン用の動画。設定しない場合はPC用と同じ動画が表示されます。縦長サイズ推奨(9:16)"
},
{
"type": "header",
"content": "MEN用スライド設定"
},
{
"type": "paragraph",
"content": "Men用スライダーに表示される内容を設定します。画像が設定されていない場合、このスライドはMen用スライダーに表示されません。"
},
{
"type": "image_picker",
"id": "image_men",
"label": "画像(PC・タブレット用)"
},
{
"type": "image_picker",
"id": "image_sp_men",
"label": "画像(スマートフォン用)"
},
{
"type": "richtext",
"id": "heading_men",
"default": "<p>Image slide</p>",
"label": "見出しテキスト"
},
{
"type": "text",
"id": "button_label_men",
"default": "くわしく見る",
"label": "ボタンテキスト",
"info": "ボタンに表示されるテキストを入力してください"
},
{
"type": "url",
"id": "link_men",
"label": "リンク先URL"
},
{
"type": "video",
"id": "video_men",
"label": "動画(PC・タブレット用)",
"info": "画像の代わりに動画を表示します。横長サイズ推奨(16:9)"
},
{
"type": "video",
"id": "video_sp_men",
"label": "動画(スマートフォン用)",
"info": "スマートフォン用の動画。設定しない場合はPC用と同じ動画が表示されます。縦長サイズ推奨(9:16)"
}
]
}
],
"presets": [
{
"name": "t:sections.slideshow.presets.name",
"blocks": [
{
"type": "slide"
},
{
"type": "slide"
}
]
}
]
}
{% endschema %}
swiperのセットを読みこませるのを忘れずに。
【補足】修正後の要点の解説
修正前はswiperスライダーが1つでしたが、WOMEN用とMEN用でスライダーを2つに分けたということが大きな違いです。
html
<!-- Women用スライダー -->
<div class="p-top-hero__slider slider-women swiper js-slider-women is-active">
<div class="swiper-wrapper">
{%- for block in section.blocks -%}<!-- 👈 WOMEN用画ループ -->
{%- if block.settings.image != blank -%}<!-- 👈 WOMEN用画像があるかチェック -->
<div class="swiper-slide">
<!-- Women用コンテンツ -->
</div>
{%- endif -%}
{%- endfor -%}
</div>
</div>
<!-- Men用スライダー -->
<div class="p-top-hero__slider slider-men swiper js-slider-men">
<div class="swiper-wrapper">
{%- for block in section.blocks -%}<!-- 👈 MEN用ループ -->
{%- if block.settings.image_men != blank -%}<!-- 👈 MEN用画像があるかチェック -->
<div class="swiper-slide">
<!-- Men用コンテンツ -->
</div>
{%- endif -%}
{%- endfor -%}
</div>
</div>男女のswiperスライダーそれぞれに、forループをしています。
振り分けのテクニック
Shopifyの制約で「ブロックが1階層のみ」という仕様です。2階層目(ネストしたブロック)はできません。このことから、同じブロックデータを2回ループして、異なる条件で振り分けしています。
管理画面のテンプレート上では変更ありません。
1つのブロックに男女の1セット登録ができます。
Shopifyの制約
- セクションブロックは1階層のみ
- ネストしたブロック構造は作れない
- Liquidテンプレートでの処理が前提

以下、JSON(Schema)→ Liquid → HTML の流れです。
1. Schema(JSON)での定義
{
"settings": [
{
"type": "image_picker",
"id": "image", // ← WOMEN用画像のID
"label": "画像(PC・タブレット用)"
},
{
"type": "image_picker",
"id": "image_men", // ← MEN用画像のID
"label": "画像(PC・タブレット用)"
}
]
}2. 管理画面で登録されるデータ構造
// Shopify内部でこんなJSONデータが生成される
section.blocks = [
{
"id": "block1",
"settings": {
"image": "women_slide1.jpg", // WOMEN用画像
"image_men": "men_slide1.jpg", // MEN用画像
"heading": "WOMEN'S COLLECTION",
"heading_men": "MEN'S COLLECTION"
}
},
{
"id": "block2",
"settings": {
"image": "women_slide2.jpg", // WOMEN用のみ設定
"image_men": null, // MEN用は未設定
"heading": "SUMMER LIMITED",
"heading_men": null
}
}
]このjsonを読み取ってLiquidで振り分けです。
3. Liquidでの振り分け処理
<!-- 1回目:WOMEN用フィルタリング -->
{%- for block in section.blocks -%}
{%- if block.settings.image != blank -%}
<!-- WOMEN用スライド生成 -->
{%- endif -%}
{%- endfor -%}
<!-- 2回目:MEN用フィルタリング -->
{%- for block in section.blocks -%}
{%- if block.settings.image_men != blank -%}
<!-- MEN用スライド生成 -->
{%- endif -%}
{%- endfor -%}!= blankで画像があるかどうか見ています。
4. 実際の振り分け結果
2つ目のスライダー(block2)にMENを登録しなかった場合。
以下のような結果になると成功です。
想定出力(妄想)
<!--WOMEN用スライダー-->
<div class="js-slider-women">
<div class="swiper-wrapper">
<!-- block1: image があるので表示 -->
<div class="swiper-slide">
<img src="women_slide1.jpg">
<h2>WOMEN'S COLLECTION</h2>
</div>
<!-- block2: image があるので表示 -->
<div class="swiper-slide">
<img src="women_slide2.jpg">
<h2>SUMMER LIMITED</h2>
</div>
</div>
</div>
<!--MEN用スライダー-->
<div class="js-slider-men">
<div class="swiper-wrapper">
<!-- block1: image_men があるので表示 -->
<div class="swiper-slide">
<img src="men_slide1.jpg">
<h2>MEN'S COLLECTION</h2>
</div>
<!-- block2: image_men が null なので表示されない -->
</div>
</div>
方法は他にもあるかもしれませんが。
以上になります。
【AI】イラストを描いてもらった
今回の記事のキャッチ画像で使わせてもらいます「Google ImageFX」で作成した画像です。誰でもgoogleアカウントでログインして使えます。
この記事にピッタリなイラストのための考えたリクエストは、
「遺跡の祭壇でクリスタルに触れた時、眩しい光と共に目の前に、過去と未来の出来事が無限スクロールするように流れる様に驚く探検家。スクロール映像に寄りで、探検家は上半身の画角。探検家を男性に。」です。

トゥームレイダーみたいなの好きです。
星間旅路のメロディ
「宇宙の静けさに包まれながら、漂流する過去の音楽を捜し求め、銀河の奥底でその旋律に耳を傾ける。」
「この電波はどこの星からきたのだろうか。」
この惑星の夏は長かったようです。
局地的な現象かもしれませんね。



