Shopifyでセクションを自作する

Shopifyでセクションを自作する

Photo by Alexander Naglestad / Unsplash

最近必死でShopifyの開発を行っているので、その内容をまとめます!

セクションとは

画像左側の「ヘッダー」「テキストオーバーレイ…」などがセクションと呼ばれるもので、ページの各パーツのことです

どんなものを作成するか

Shopifyに登録されたブログの最新記事を表示させるものを作ります

利用する画像をアップする

左下の設定 > ファイル > ファイルをアップロード
からファイルをアップすることができます

画像をアップロードすると、URLを取得できます

sectionファイルを作成する

次にsectionファイルを作成してきます。Shopifyのコード編集画面まで行くと、「Sections」というフォルダがあり、各セクションの.liquidファイルがこの中に入っています。「新しいsectionを追加する」をクリックしてファイルを新規作成します。

liquidファイル

<div class="tl_container page-width">
  {%- assign blog_1 = blogs[section.settings.blog_1] -%}
  {%- assign blog_2 = blogs[section.settings.blog_2] -%}
  {%- assign blog_3 = blogs[section.settings.blog_3] -%}
  {%- assign blog_4 = blogs[section.settings.blog_4] -%}
  {%- assign blog_5 = blogs[section.settings.blog_5] -%}
  <ul class="tl_list">
    <li><a href="#A" class="a ON">{{ blog_1.title }}</a></li>
    <li><a href="#B" class="b">{{ blog_2.title }}</a></li>
    <li><a href="#C" class=c>{{ blog_3.title }}</a></li>
    <li><a href="#D" class=d>{{ blog_4.title }}</a></li>
    <li><a href="#E" class=e>{{ blog_5.title }}</a></li>
  </ul>

  <section id="A" class="tl_section a">
    {% for article1 in blog_1.articles limit: 1 %}
    <div class="blog-sction-column">
      <h2>{{ article1.title }}</h2>
      <p>{{ article1.content }}</p>
      <button class="blog-detail" onclick="location.href='{{ article1.url }}'">ブログをもっと見る</button>
    </div>
    <div class="blog-sction-column">
      <img src="https://cdn.shopify.com/s/files/1/0277/3614/6018/files/2788204_s.jpg?v=1608208421" />
    </div>
    <div class="blog-sction-column">
      <img src="https://cdn.shopify.com/s/files/1/0277/3614/6018/files/949796_s.jpg?v=1608208420" />
    </div>
    {% endfor %}
  </section>

  <section id="B" class="tl_section b">
    {% for article2 in blog_2.articles limit: 1 %}
    <div class="blog-sction-column">
      <h2>{{ article2.title }}</h2>
      <p>{{ article2.content }}</p>
      <button class="blog-detail" onclick="location.href='{{ article2.url }}'">ブログをもっと見る</button>
    </div>
    <div class="blog-sction-column">
      <img src="https://cdn.shopify.com/s/files/1/0277/3614/6018/files/2788204_s.jpg?v=1608208421" />
    </div>
    <div class="blog-sction-column">
      <img src="https://cdn.shopify.com/s/files/1/0277/3614/6018/files/949796_s.jpg?v=1608208420" />
    </div>
    {% endfor %}
  </section>

  <section id=C class="tl_section c">
    {% for article3 in blog_3.articles limit: 1 %}
    <div class="blog-sction-column">
      <h2>{{ article3.title }}</h2>
      <p>{{ article3.content }}</p>
      <button class="blog-detail" onclick="location.href='{{ article3.url }}'">ブログをもっと見る</button>
    </div>
    <div class="blog-sction-column">
      <img src="https://cdn.shopify.com/s/files/1/0277/3614/6018/files/2788204_s.jpg?v=1608208421" />
    </div>
    <div class="blog-sction-column">
      <img src="https://cdn.shopify.com/s/files/1/0277/3614/6018/files/949796_s.jpg?v=1608208420" />
    </div>
    {% endfor %}
  </section>

  <section id=D class="tl_section d">
    {% for article4 in blog_4.articles limit: 1 %}
    <div class="blog-sction-column">
      <h2>{{ article4.title }}</h2>
      <p>{{ article4.content }}</p>
      <button class="blog-detail" onclick="location.href='{{ article4.url }}'">ブログをもっと見る</button>
    </div>
    <div class="blog-sction-column">
      <img src="https://cdn.shopify.com/s/files/1/0277/3614/6018/files/2788204_s.jpg?v=1608208421" />
    </div>
    <div class="blog-sction-column">
      <img src="https://cdn.shopify.com/s/files/1/0277/3614/6018/files/949796_s.jpg?v=1608208420" />
    </div>
    {% endfor %}
  </section>
  
  <section id=E class="tl_section e">
    {% for article5 in blog_5.articles limit: 1 %}
    <div class="blog-sction-column">
      <h2>{{ article5.title }}</h2>
      <p>{{ article5.content }}</p>
      <button class="blog-detail" onclick="location.href='{{ article5.url }}'">ブログをもっと見る</button>
    </div>
    <div class="blog-sction-column">
      <img src="https://cdn.shopify.com/s/files/1/0277/3614/6018/files/2788204_s.jpg?v=1608208421" />
    </div>
    <div class="blog-sction-column">
      <img src="https://cdn.shopify.com/s/files/1/0277/3614/6018/files/949796_s.jpg?v=1608208420" />
    </div>
    {% endfor %}
  </section>
</div>

{% schema %}
  {
    "name": {
      "ja": "ブログタブ",
      "en": "blog tab"
	},
    "settings": [
    {
      "id": "blog_1",
      "type": "blog",
      "label": {
        "en": "Blog 1",
        "ja": "ブログ 1"
      }
    },
    {
      "id": "blog_2",
      "type": "blog",
      "label": {
        "en": "Blog 2",
        "ja": "ブログ 2"
      }
    },
    {
      "id": "blog_3",
      "type": "blog",
      "label": {
        "en": "Blog 3",
        "ja": "ブログ 3"
      }
    },
    {
      "id": "blog_4",
      "type": "blog",
      "label": {
        "en": "Blog 4",
        "ja": "ブログ 4"
      }
    },
    {
      "id": "blog_5",
      "type": "blog",
      "label": {
        "en": "Blog 5",
        "ja": "ブログ 5"
      }
    }
  ],
  "presets": [
    {
      "name": {
        "en": "Blog tab",
        "ja": "ブログタブ"
      },
      "category": {
        "en": "Blog",
        "ja": "ブログ"
      }
    }
    ]
  }
{% endschema %}

{% stylesheet %}

.tl_container {
  margin: 0 auto;
}

.tl_list {
  display: flex;
  padding: 0;
  margin: 0;
  align-items: center;
  justify-content: left;
  list-style: none;
}

.tl_list li {
  margin: 0;
  width: 33.3333%;
  text-align: center;
  border-radius: 4px 4px 0 0;
}

.tl_list a {
  color: #000000;
  padding: 1rem 0.5rem;
  display: block;
  border: none;
  text-decoration: none;
  text-shadow: none;
}
.tl_list a:hover,
.tl_list a:focus {
  text-shadow: none;
}

.tl_list a {
  border-bottom:solid #E3E3E3;
}

.tl_list a[aria-selected="true"] {
  border-bottom:solid #FDCF50;
}


/* White focus ring - so you can see when it's focussed (not required) */
.tl_section h2:focus {
  outline: none;
}


/* Section styling (optional) */

.tl_section {
  padding: 1rem 0;
  min-height: 8rem;
  /* border-radius: 0 0 4px 4px; */
  text-shadow: none;

  /* overflow: hidden; */

  display: flex;
  flex-wrap: wrap;
}
.tl_section h2 {
  margin-top: 1rem;
}

.tl_section .blog-sction-column {
  /* width: 30%;
  float: left; */
  padding: 0 10px;
  flex: 0 0 33.3333%;
  max-width: 33.3333%; /* IE11バグ対応 */
  position: relative;
}

@media (max-width: 800px) {
  .tl_section .blog-detail {
    flex: 0 0 100%;
    max-width: 100%; /* IE11バグ対応 */
  }
}

.tl_section .blog-sction-column img {
  height: 350px;
  object-fit: cover;
}


.tl_section .blog-detail {
  background-color: #DEDEDE;
  border: none;
  height: 50px;
  padding: 0 50px;
  border-radius: 25px;
  position: absolute;
  bottom: 10px;
}

/* 
Section styling (required)
Removes hidden sections from the keychain. 
Only used when JavaScript is available.
*/
[role="tabpanel"][aria-hidden="true"] {
  display: none;
}

{% endstylesheet %}

{% javascript %}
var accessibleTabs6 = (function () {

  "use strict";

  var d = document;
  var tabListClass;
  var onClass;
  var hoverableClass;

  var setTabsOff = function (tabList) {
    var i = tabList.tabs.length;
    while (i--) {
      setTab(tabList.tabs[i], false);
    }
  };

  var setTab = function (tab, switchOn) {
    if (tab && tab.panelId) {
      d.getElementById(tab.panelId).setAttribute("aria-hidden", !switchOn);
      tab.setAttribute("aria-selected", switchOn);
      tab.setAttribute("tabindex", switchOn ? "0" : "-1");
    }
  };

  var _activateTab = function (e) {
    var tab = e.target;
    if (e.preventDefault) {
      e.preventDefault();
      setTabsOff(tab.tabList);
      setTab(tab, true);
      d.getElementById(tab.panelId).children[0].focus();
    }
  };

  var _keypressed = function (e) {

    var tab = e.target;
    var newNo = -1;
    var tabs = tab.tabList.tabs;
    var maxNo = tabs.length - 1;

    if (e.keyCode === 37 || e.keyCode === 38) { // left / up arrow
      newNo = (tab.no === 0) ? maxNo : tab.no - 1;
    }
    if (e.keyCode === 39 || e.keyCode === 40) { // right arrow / down arrow
      newNo = (tab.no === maxNo) ? 0 : tab.no + 1;
    }

    if (newNo > -1) {
      setTabsOff(tab.tabList);
      setTab(tabs[newNo], true);
      tabs[newNo].focus();
    }
  };

  var _tabHovered = function (e) {
    var a = e.target;
    var useHover = a.tabList.classList.contains(hoverableClass);
    if (useHover) {
      _activateTab(e);
    }
  };

  var _events = function (tab) {
    tab.addEventListener("click", _activateTab, false);
    tab.addEventListener("keydown", _keypressed, false);
    tab.addEventListener("mouseover", _tabHovered, false);
  };

  var _initialiseAriaAttributes = function (tab) {

    var tabPanel = d.getElementById(tab.panelId);

    tab.parentNode.setAttribute("role", "presentation");
    tab.setAttribute("role", "tab");
    tab.setAttribute("aria-controls", tab.panelId);

    tabPanel.setAttribute("role", "tabpanel");
    tabPanel.setAttribute("aria-labelledby", tab.id);

    // Make first <section> object keyboard focussable
    // Preferably a heading
    // Tabindex=0 to work both forwards and backwards through the keychain
    tabPanel.children[0].setAttribute("tabindex", "0");

  };

  var _setUpConfig = function (cfg) {
    tabListClass = cfg.tabListClass || "tl_list";
    onClass = cfg.onClass || "ON";
    hoverableClass = cfg.hoverableClass || "tl-hoverable";
  };

  var _setUpTab = function (tab, panelId, count) {
    tab.no = count;
    tab.id = "tab-" + panelId;
    tab.panelId = panelId;
    _initialiseAriaAttributes(tab);
    _events(tab);
  };


  var _initialiseTabList = function (tabList) {

    var defaultTab = 0;
    var panelId;
    var tabPanel;
    var i;

    if (tabList) {

      tabList.setAttribute("role", "tablist");
      tabList.tabs = tabList.getElementsByTagName("a");
      i = tabList.tabs.length;

      while (i--) {

        tabList.tabs[i].tabList = tabList;

        panelId = tabList.tabs[i].href.slice(tabList.tabs[i].href.lastIndexOf("#") + 1);
        tabPanel = d.getElementById(panelId);

        if (tabPanel) {
          _setUpTab(tabList.tabs[i], panelId, i);
          if (tabList.tabs[i].classList.contains(onClass)) {
            defaultTab = i;

            // onClass only used to declare intial state, so remove from DOM
            tabList.tabs[i].classList.remove(onClass);
            d.getElementById(panelId).classList.remove(onClass);
          }
        }
      }
      setTabsOff(tabList);
      if (tabList.tabs[defaultTab] && tabList.tabs[defaultTab].panelId) {
        setTab(tabList.tabs[defaultTab], true);
      }
    }
  };

  var _isMustardCut = function (e) {
    // check browser feature support (IE10+)
    return (
      (typeof d.querySelectorAll === "function") &&
      d.addEventListener &&
      !!d.documentElement.classList // IE10+
    );
  };

  var init = function (cfg) {
    var tabLists;
    var i;

    if (_isMustardCut()) {
      _setUpConfig(cfg);
      tabLists = d.getElementsByClassName(tabListClass);
      i = tabLists.length;
      while (i--) {
        _initialiseTabList(tabLists[i]);
      }
    }
  };

  return {
    init: init
  };

}());


accessibleTabs6.init({
  tabListClass : "tl_list",   // default, may omit
  onClass : "ON",               // default, may omit
  hoverableClass : "tl-hoverable"  // default, may omit
});
{% endjavascript %}

・ファイルの前半部分
HTMLのコード

・{% schema %} 〜{% endschema %}で囲まれた部分
設定項目を定義しています。

まず、管理画面上で利用できるようにしたいため、以下を設定してください

"presets": [
    {
      "name": {
        "en": "Blog tab",
        "ja": "ブログタブ"
      },
      "category": {
        "en": "Blog",
        "ja": "ブログ"
      }
    }
 ]

そうするとこのように編集画面上から利用できるようになります。

今回はShopify内のブログを選択できるようにしたかったため、↓のような項目を5つ用意しています

{
  "id": "blog_1",
  "type": "blog",
  "label": {
    "en": "Blog 1",
    "ja": "ブログ 1"
  }
},

そうすると、sectionの設定からブログを選択できるようになります

選択したブログはliquidファイル上部のHTMLで

{%- assign blog_1 = blogs[section.settings.blog_1] -%}

このように記述すると、blog_1という名前で扱えるようになります。
取り出したblog objectの使い方はShopifyのdevelopersサイトから確認できます

・{% stylesheet %}〜{% endstylesheet %}
・{% javascript %}〜{% endjavascript %}
こちらはそれぞれ、cssとJavaScriptを定義できます

以上のように設定すると、このようにsectionを作成することができます!

おわり