モーダルセクションを実装してみた

公開
モーダルセクションを実装して見た


今回実装するセクションはこちらです!

管理画面で設定可能な項目は以下になります

  • ブロック単位で画像の増減可能
  • 見出しの編集
  • 上下の余白(PC、SP個別に)

モーダルセクションのコード

早速ですが今回のコードはこちらです

LIquid(c_modal.liquid)

{{ 'c_modal.css' | asset_url | stylesheet_tag }}
{%- style -%}
  .section-{{ section.id }}-padding {
    padding-top: {{ section.settings.padding_top }}px;
    padding-bottom: {{ section.settings.padding_bottom }}px;
  }

  @media screen and (min-width: 750px) {
    .section-{{ section.id }}-padding {
      padding-top: {{ section.settings.padding_top-pc }}px;
      padding-bottom: {{ section.settings.padding_bottom-pc }}px;
    }
  }
{%- endstyle -%}

<div class="c_modal page-width section-{{ section.id }}-padding">
  <h2 class="section-title">{{ section.settings.title }}</h2>
  <ul class="c_modal__lists">
  {% for block in section.blocks %}
    <li class="c_modal__list js-modal">
      <div class="c_modal__inner">
        <div class="c_modal__thumbnail">
        {% if  block.settings.image  %}
          {{ block.settings.image | image_url: width: 1920 | image_tag: alt: block.settings.image.alt, loading: "lazy" }}
        {% endif %}
        </div>
        <h3 class="c_modal__title">{{ block.settings.title }}</h3>
      </div>
        
      <div class="c_modal__image--wrapper js-modal__content">
        <div class="c_modal__image">
        {% if  block.settings.image  %}
          {{ block.settings.image | image_url: width: 1920 | image_tag: alt: block.settings.image.alt, loading: "lazy" }}
        {% endif %}
        </div>
      </div>
    </li>
  {% endfor %}
  </ul>
</div>
  
<script>
  document.addEventListener("DOMContentLoaded", function () {
    const modalTrigger = document.querySelectorAll(".js-modal");

    modalTrigger.forEach(function (element, index) {
        const modalContent = element.querySelector(".js-modal__content");
        
        element.addEventListener("click", function (el) {
        modalContent.classList.toggle('is-open');
        document.body.classList.toggle("no-scroll");
      });
    });
  });
</script>

{% schema %}
{
  "name": "モーダル",
  "settings": [
    {
      "type": "text",
      "id": "title",
      "label": "見出し",
      "default": "モーダル"
    },
    {
      "type": "header",
      "content": "上下の余白"
    },
    {
      "type": "range",
      "id": "padding_top-pc",
      "min": 0,
      "max": 100,
      "step": 1,
      "unit": "px",
      "label": "上部の余白(pc)",
      "default": 32
    },
    {
      "type": "range",
      "id": "padding_bottom-pc",
      "min": 0,
      "max": 100,
      "step": 1,
      "unit": "px",
      "label": "下部の余白(PC)",
      "default": 32
    },
    {
      "type": "range",
      "id": "padding_top",
      "min": 0,
      "max": 100,
      "step": 1,
      "unit": "px",
      "label": "上部の余白",
      "default": 32
    },
    {
      "type": "range",
      "id": "padding_bottom",
      "min": 0,
      "max": 100,
      "step": 1,
      "unit": "px",
      "label": "下部の余白",
      "default": 32
    }
  ],
  "blocks" : [
    {
      "type": "image",
      "name": "画像",
      "settings": [
        {
          "type": "image_picker",
          "id": "image",
          "label": "画像"
        },
        {
          "type": "text",
          "id": "title",
          "label": "見出し"
        }
      ]
    }
  ],
  "presets":[
    {
      "name" : "モーダル",
      "category": "カスタムパーツ"
    }
  ]
}
{% endschema %}

CSS(c_modal.css)

/* リセット */
.c_modal img {
  width: 100%;
  height: auto;
  vertical-align: bottom;
}

.c_modal ul {
  margin: 0;
  padding: 0;
}

.c_modal li {
  list-style: none;
}

.c_modal a {
  text-decoration: none;
}

.no-scroll {
  overflow: hidden;
}

.c_modal__lists {
  display: flex;
  gap: 16px
}

.c_modal__list {
  width: calc(100% / 4 - 16px * 3 / 4)
  
}

/* モーダル */
.c_modal__image--wrapper {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgb(0, 0, 0, .8);
  z-index: 999;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease;
}

.c_modal__image--wrapper.is-open {
  display: block;
  opacity: 1;
  visibility: visible;
}

.c_modal__image {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

コードの解説

モーダルの部分のみ解説します

簡単に説明すると以下のロジックで制御しています

  1. js-modalクラスを持った要素をクリック
  2. クリックした要素にis-openクラスが付与される
  3. モーダル画像が表示される

ブロック単位で画像を出力

以下のコードがブロック単位で画像を表示しています

<div class="c_modal page-width section-{{ section.id }}-padding">
  <h2 class="section-title">{{ section.settings.title }}</h2>
  <ul class="c_modal__lists">
  {% for block in section.blocks %}
    <li class="c_modal__list js-modal">
      <div class="c_modal__inner">
        <div class="c_modal__thumbnail">
        {% if  block.settings.image  %}
          {{ block.settings.image | image_url: width: 1920 | image_tag: alt: block.settings.image.alt, loading: "lazy" }}
        {% endif %}
        </div>
        <h3 class="c_modal__title">{{ block.settings.title }}</h3>
      </div>
        
      <div class="c_modal__image--wrapper js-modal__content">
        <div class="c_modal__image">
        {% if  block.settings.image  %}
          {{ block.settings.image | image_url: width: 1920 | image_tag: alt: block.settings.image.alt, loading: "lazy" }}
        {% endif %}
        </div>
      </div>
    </li>
  {% endfor %}
  </ul>
</div>
  
<script>
  document.addEventListener("DOMContentLoaded", function () {
    const modalTrigger = document.querySelectorAll(".js-modal");

    modalTrigger.forEach(function (element, index) {
        const modalContent = element.querySelector(".js-modal__content");
        
        element.addEventListener("click", function (el) {
        modalContent.classList.toggle('is-open');
        document.body.classList.toggle("no-scroll");
      });
    });
  });
</script>

ブロックで画像を読み込んでいるのでfor文の中に書いていきます!

{% for block in section.blocks %}
<!-- ココに書いていく -->
{% endfor %}

block.settings.imageで画像が読み込めるのでimage_urlフィルターとimage_tagフィルターを使って画像を出力していきます!
※block.settings.imageはブロックで読み込んでいるので必ずfor文の中に書いてくださいね!

では次に画像の出力部分のについて解説します!

サムネイルとモーダルの画像は同じもの

①あらかじめ表示されている画像(サムネイル)のコード

<div class="c_modal__thumbnail">
	<!-- サムネイル -->
  {% if  block.settings.image  %}
    {{ block.settings.image | image_url: width: 1920 | image_tag: alt: block.settings.image.alt, loading: "lazy" }}
  {% endif %}
</div>

②モーダルで出力される画像のコード

<div class="c_modal__image--wrapper js-modal__content">
  <div class="c_modal__image">
		<!-- モーダル -->
    {% if  block.settings.image  %}
      {{ block.settings.image | image_url: width: 1920 | image_tag: alt: block.settings.image.alt, loading: "lazy" }}
    {% endif %}
  </div>
</div>

見比べてもらうとわかるんですが以下のコードは①と②で同じコードを書いています!
※正確にはサムネイルのwidth部分は1920よりも小さくて良いがわかりやすくする為同じにしてます

サムネイル画像とモーダル画像は同じ画像なので以下の同じコードになります!

{% if  block.settings.image  %}
  {{ block.settings.image | image_url: width: 1920 | image_tag: alt: block.settings.image.alt, loading: "lazy" }}
{% endif %}

モーダル画像はあらかじめCSSで非表示に


.c_modal__image--wrapper {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgb(0, 0, 0, .8);
  z-index: 999;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease;
}

.c_modal__image--wrapper.is-open {
  display: block;
  opacity: 1;
  visibility: visible;
}

c_modal__image--wrapperをあらかじめ非表示にしておいて
is-openがつくと表示される仕組みです!

以下が非表示にしているコードです

opacity: 0;
visibility: hidden;

要素の非表示によく使われるdisplay:noneを使っていないのは
transitionが効かない為使っていません(フェードインで表示をしたいので)

モーダルのJavaScript

<script>
document.addEventListener("DOMContentLoaded", function () {
  const modalTrigger = document.querySelectorAll(".js-modal");

  modalTrigger.forEach(function (element, index) {
    const modalContent = element.querySelector(".js-modal__content");
    
    element.addEventListener("click", function (el) {
      modalContent.classList.toggle('is-open');
      document.body.classList.toggle("no-scroll");
    });
  });
});
</script>

①modalTrigger変数にjs-modalクラスを持った要素を格納

const modalTrigger = document.querySelectorAll(".js-modal");

②modalTrigger変数は配列になるのでforEachを使ってループを回す

modalTrigger.forEach(function (element, index) {});

③addEventListenerを使ってクリックイベントに対してクラスのつけ外しを実行

  1. クリックされた要素にis-openクラスをつけてモーダルを表示
  2. モーダル表示の際はスクロールされないようにno-scrollクラスをbodyタグに付与
element.addEventListener("click", function (el) {
  modalContent.classList.toggle('is-open');
  document.body.classList.toggle("no-scroll");
});

まとめ

今回のセクションはスキーマのブロックを使った実装になります

スキーマのブロックを使って構築すると何がいいかというと

サイト公開後、
画像を変更したい、順序を入れ替えたいってのはもちろんですが
画像増やしたい、もしくは減らしたいって要望があるかと思います

その度に毎回コード触るのは大変ですよね。。

こちらが全て管理画面でできるようになります!!!

便利ですよねー

ぜひ一度実装して見てください!

ブログ一覧に戻る