見出し画像

【Shopify】九十九悪嵐のうぇぶでざいんびぼうろく。~年齢制限をかけてみる編~【解説編】

解説とかどうでもいいからコードだけくれよって人はこちら↓

https://note.com/alarn99/n/n011863a03c98

それでは解説します~!

Sectionsフォルダにあるmain-cart-footer.liquid(HTML部分)では何をやってるの?

コード全体

<div class="page-width{% if cart == empty %} is-empty{% endif %}" id="main-cart-footer" data-id="{{ section.id }}">
  <!-- ここから -->
  {% for item in cart.items %}
  {% if item.product.type == 'お酒' %}
  <div id="ageCheck" class="cart-attribute__field">  
    <div class="cart-birth">
      <div class="cart-birth__field">
        <label>大切な確認事項</label>
        <p class="caution">生年月日を選択してください。</p>
        <select required="" class="required birthday" id="birthday-year" name="attributes[年] attr1">
        </select>
        <span>年</span>
        <select required="" class="required birthday" id="birthday-month" name="attributes[月]">
        </select>
        <span>月</span>
        <select required="" class="required birthday" id="birthday-day" name="attributes[日]">
        </select> 
        <span>日</span>
      </div>
      <div class="consent">
        <div class="consent_box">
          <input type="hidden" name="attributes[以下の注意事項に同意します(必須)。]" value="No">
          <input required="" class="age-check required" type="checkbox" name="attributes[以下の注意事項に同意します(必須)。]" value="Yes" checked="">
          <label>以下の注意事項に同意します(必須)。</label>
        </div>
        <div class="consent_detail">
          <ul class="consent_list">
            <li class="consent_txt">生年月日が未入力または未成年者の場合、お酒の販売はお断りしております。</li>
            <li class="consent_txt">妊娠中や授乳期の飲酒は、胎児・乳児の発育に悪影響を与えるおそれがあります。</li>
          </ul>
        </div>  
      </div>
    </div>
  </div>
  {% break %}
  {% endif %}
  {% endfor %}
  <!-- ここまで追記 -->
  <div>
<!-- 省略 -->

解説

  {% for item in cart.items %}
  ~~省略~~
  {% endfor %}

ここのfor文で、カート(cart)に入っている全ての商品(items)を参照しています。
「お酒」という商品タイプがあるかどうか見極めるためには、まずは商品全部を引っ張ってくる必要があるのです…。

  {% if item.product.type == 'お酒' %}
  ~~省略~~
  {% endif %}

ここのif文で、カートに入っている商品に商品タイプ「お酒」がないかチェックしています。
商品タイプ「お酒」の商品が入っていた場合は年齢確認用の文章(「大事な確認事項うんぬん」を出すようにしています。商品タイプ「お酒」の商品がなければこの文章は出ません。

もし他の商品タイプでも年齢確認したいよって人はここの'お酒'の後に年齢確認対象の商品タイプを入れてあげましょう(以下はサンプル)。

  {% if item.product.type == 'お酒' or item.product.type == 'アルコール飲料' %}
  ~~省略~~
  {% endif %}

また、以下のようにしてもいいです。

  {% if item.product.type contains 'お酒' %}
  ~~省略~~
  {% endif %}

「contains」は「~を含む」という意味を持つ演算子です。
なので、仮に「お酒入りチョコレート」という商品タイプがあったとしたら、「== 'お酒'」だと「or item.product.type == 'お酒入りチョコレート'」にする必要がありますが、「contains 'お酒'」にすると「お酒」という文字列が入っているので、わざわざ「or item.product.type == 'お酒入りチョコレート'」と書かなくて大丈夫です!便利!

  ~~省略~~
  <div id="ageCheck" class="cart-attribute__field">  
    <div class="cart-birth">
      <div class="cart-birth__field">
        <label>大切な確認事項</label>
        <p class="caution">生年月日を選択してください。</p>
        <select required="" class="required birthday" id="birthday-year" name="attributes[年] attr1">
        </select>
        <span>年</span>
        <select required="" class="required birthday" id="birthday-month" name="attributes[月]">
        </select>
        <span>月</span>
        <select required="" class="required birthday" id="birthday-day" name="attributes[日]">
        </select> 
        <span>日</span>
      </div>
      <div class="consent">
        <div class="consent_box">
          <input type="hidden" name="attributes[以下の注意事項に同意します(必須)。]" value="No">
          <input required="" class="age-check required" type="checkbox" name="attributes[以下の注意事項に同意します(必須)。]" value="Yes" checked="">
          <label>以下の注意事項に同意します(必須)。</label>
        </div>
        <div class="consent_detail">
          <ul class="consent_list">
            <li class="consent_txt">生年月日が未入力または未成年者の場合、お酒の販売はお断りしております。</li>
            <li class="consent_txt">妊娠中や授乳期の飲酒は、胎児・乳児の発育に悪影響を与えるおそれがあります。</li>
          </ul>
        </div>  
      </div>
    </div>
  </div>
  ~~省略~~

ここは年齢確認用の文章とかなので解説は省略します。

  ~~省略~~
  </div>
  {% break %}
  ~~省略~~

ここの{% break %}…実はとっっっても重要な役目を果たしています…。
{% break %}を忘れてしまうとどうなるか?

カートに入っている商品タイプ「お酒」の数だけ、年齢確認用の文章が増えてしまうのですッ!!

年齢確認用の文章が2、3個でも「なんであるんだろう…?」と不思議に感じますが、これが10個、20個…と増えていくと、不思議を通り越して気持ち悪いを通り越して怖いレベルです。
あと、恐らくJSが効かなくなる可能性があります(これは推測ですが)。

ですので、{% break %}を記述し、これ以上文章が増えないようにしてあげましょう。

Sectionsフォルダにあるmain-cart-footer.liquid(JS部分)では何をやっているの?

コード全体

{% endjavascript %}
<!-- ここから -->
{% for item in cart.items %}
{% if item.product.type == 'お酒' %}
<script>
  /**
   *
   * 変数
   *
  **/
  let i;  // 生年月日の数値用
  const cartSubmit = $('.cart__footer #checkout');  // 購入手続きボタン
  let birthdayYear = document.getElementById("birthday-year");  // 生年月日[年]
  let birthdayMonth = document.getElementById("birthday-month");  // 生年月日[月]
  let birthdayDay = document.getElementById("birthday-day");  // 生年月日[日]
  const ageCheck = $(' .age-check ');  // 同意チェックボックス
  
  /**
   *
   * 初期値
   *
  **/
  ageCheck.prop('checked', false);  // 同意チェックボックス(チェックなし)
  cartSubmit.prop('disabled', true);  // 購入手続きボタン(押下不可)
  
  /* 生年月日[年]の設定 */
  function $set_year() {
    const selectYear = new Date();
    // 「未選択」用
    let not_select = document.createElement('option');
    not_select.value = '';
    not_select.text = '未選択';
    not_select.hidden = 'hidden';
    birthdayYear.appendChild(not_select);
    // 年を生成
    for(i = 1900; i < selectYear.getFullYear(); i++){
      let op = document.createElement('option');
      op.value = i;
      op.text = i;
      birthdayYear.appendChild(op);
    }
  }
  
  // 生年月日[月]の設定
  function $set_month(){
    // 「未選択」用
    const not_select = document.createElement('option');
    not_select.value = '';
    not_select.text = '未選択';
    not_select.hidden = 'hidden';
    birthdayMonth.appendChild(not_select);
    // 月を生成
    for(i = 1; i <= 12; i++){
      let op = document.createElement('option');
      op.value = i;
      op.text = i;
      birthdayMonth.appendChild(op);
    }
  }
  
  // 生年月日[日]の設定
  function $set_day(){
    //日の選択肢を空にする
    let children = birthdayDay.children;
    while(children.length){
      children[0].remove();
    }
    // 日を生成(動的に変える)
    if(birthdayYear.value !== '' &&  birthdayMonth.value !== ''){
      const last_day = new Date(birthdayYear.value,birthdayMonth.value,0).getDate();
      for (i = 1; i <= last_day; i++) {
        let op = document.createElement('option');
        op.value = i;
        op.text = i;
        birthdayDay.appendChild(op);
      }
    }else {
      // 「未選択」用
      let not_select = document.createElement('option');
      not_select.value = '';
      not_select.text = '未選択';
      birthdayDay.appendChild(not_select);
    }
  }
  
  /**
   *
   * イベント
   *
  **/
  // 「同意する」チェックボックス押下イベント
  ageCheck.on('click', function() {
    if( $(this).prop('checked') == true ) {  // 「同意する」チェックボックスにチェックが入っている場合
      if(birthdayYear.value != '' && birthdayMonth.value != '' && birthdayDay.value != '') {  // 生年月日が空白ではない場合
        // 年齢確認
        let age = getUserAge( birthdayYear.value, birthdayMonth.value, birthdayDay.value );
        if( age < 20) {  // 年齢が20歳未満の場合
          ageCheck.prop('checked', false);
          cartSubmit.prop('disabled', true);
        }else {  // 年齢が20歳以上の場合
          cartSubmit.prop('disabled', false);
        }
      }else {  // 生年月日が空白の場合
        ageCheck.prop('checked', false);
        cartSubmit.prop('disabled', true);
      }
    }else {  // 「同意する」チェックボックスにチェックが入っていない場合
      cartSubmit.prop('disabled', true);
    }
  });
  
  // 「ご購入の手続きへ」ボタン押下イベント
  cartSubmit.on('click', function() {
    if(ageCheck.prop('checked') == true ) {  // 「同意する」チェックボックスにチェックが入っている場合
      if(birthdayYear.value != '' && birthdayMonth.value != '' && birthdayDay.value != '') {  // 生年月日が空白ではない場合
        // 年齢確認
        let age = getUserAge( birthdayYear.value, birthdayMonth.value, birthdayDay.value );
        if( age < 20) {  // 年齢が20歳未満の場合
          ageCheck.prop('checked', false);
          cartSubmit.prop('disabled', true);
        }else {  // 年齢が20歳以上の場合
          cartSubmit.prop('disabled', false);
        }
      }else {  // 生年月日が空白の場合
        ageCheck.prop('checked', false);
        cartSubmit.prop('disabled', true);
      }
    }else {  // 「同意する」チェックボックスにチェックが入っていない場合
      cartSubmit.prop('disabled', true);
    }
  });
  
  // load時、年月変更時に実行する
  window.onload = function(){
    $set_year();
    $set_month();
    $set_day();
    birthdayYear.addEventListener('change',$set_day)
    birthdayMonth.addEventListener('change',$set_day)
  }
  
  /**
   *
   * 関数
   *
  **/
  // 年齢確認
  function getUserAge( year, month, day ) {
    //誕生日を一旦 Date クラスに変換する
    const birthdayDate = new Date(year, month - 1, day);
    
    if( year != birthdayDate.getFullYear() || (month - 1) != birthdayDate.getMonth() || day != birthdayDate.getDate() ) { // 不正の値判定
      return null;
    }
    
    //今日の日付けを取得する
    var todayDate = new Date();
    //誕生日を計算する
    var userAge = todayDate.getFullYear() - birthdayDate.getFullYear();
    // 誕生日
    var currentYearDate = new Date(todayDate.getFullYear(), birthdayDate.getMonth(), birthdayDate.getDate());
    if(currentYearDate > todayDate) {  // 今年の誕生日を迎えていない場合
      userAge = ( userAge - 1 );
    }
    
    return userAge;  // 年齢を返す
  }
</script>
{% break %}
{% endif %}
{% endfor %}
<!-- ここまで追記 -->
{% schema %}

解説

{% for item in cart.items %}
~~省略~~
{% endfor %}

HTML部分と同じく、ここのfor文で、カート(cart)に入っている全ての商品(items)を参照しています。

{% if item.product.type == 'お酒' %}
~~省略~~
{% endif %}

ここもHTML部分と同じです。ここのif文で、カートに入っている商品に商品タイプ「お酒」がないかチェックしています。
カートに商品タイプ「お酒」があったら<script>~</script>が読み込まれるという仕組みです。

ここのif文はなくても支障はあまりないのですが、商品タイプ「お酒」がない場合のHTML部分は空っぽになるので、コンソール上ではエラーが吐き出されています。いわゆる「ちょっと何言ってるのか分からない」状態です。

<script>
  /**
   *
   * 変数
   *
  **/
  let i;  // 生年月日の数値用
  const cartSubmit = $('.cart__footer #checkout');  // 購入手続きボタン
  let birthdayYear = document.getElementById("birthday-year");  // 生年月日[年]
  let birthdayMonth = document.getElementById("birthday-month");  // 生年月日[月]
  let birthdayDay = document.getElementById("birthday-day");  // 生年月日[日]
  const ageCheck = $(' .age-check ');  // 同意チェックボックス
  
  /**
   *
   * 初期値
   *
  **/
  ageCheck.prop('checked', false);  // 同意チェックボックス(チェックなし)
  cartSubmit.prop('disabled', true);  // 購入手続きボタン(押下不可)
  
  /* 生年月日[年]の設定 */
  function $set_year() {
    const selectYear = new Date();
    // 「未選択」用
    let not_select = document.createElement('option');
    not_select.value = '';
    not_select.text = '未選択';
    not_select.hidden = 'hidden';
    birthdayYear.appendChild(not_select);
    // 年を生成
    for(i = 1900; i < selectYear.getFullYear(); i++){
      let op = document.createElement('option');
      op.value = i;
      op.text = i;
      birthdayYear.appendChild(op);
    }
  }
  
  // 生年月日[月]の設定
  function $set_month(){
    // 「未選択」用
    const not_select = document.createElement('option');
    not_select.value = '';
    not_select.text = '未選択';
    not_select.hidden = 'hidden';
    birthdayMonth.appendChild(not_select);
    // 月を生成
    for(i = 1; i <= 12; i++){
      let op = document.createElement('option');
      op.value = i;
      op.text = i;
      birthdayMonth.appendChild(op);
    }
  }
  
  // 生年月日[日]の設定
  function $set_day(){
    //日の選択肢を空にする
    let children = birthdayDay.children;
    while(children.length){
      children[0].remove();
    }
    // 日を生成(動的に変える)
    if(birthdayYear.value !== '' &&  birthdayMonth.value !== ''){
      const last_day = new Date(birthdayYear.value,birthdayMonth.value,0).getDate();
      for (i = 1; i <= last_day; i++) {
        let op = document.createElement('option');
        op.value = i;
        op.text = i;
        birthdayDay.appendChild(op);
      }
    }else {
      // 「未選択」用
      let not_select = document.createElement('option');
      not_select.value = '';
      not_select.text = '未選択';
      birthdayDay.appendChild(not_select);
    }
  }
  
  /**
   *
   * イベント
   *
  **/
  // 「同意する」チェックボックス押下イベント
  ageCheck.on('click', function() {
    if( $(this).prop('checked') == true ) {  // 「同意する」チェックボックスにチェックが入っている場合
      if(birthdayYear.value != '' && birthdayMonth.value != '' && birthdayDay.value != '') {  // 生年月日が空白ではない場合
        // 年齢確認
        let age = getUserAge( birthdayYear.value, birthdayMonth.value, birthdayDay.value );
        if( age < 20) {  // 年齢が20歳未満の場合
          ageCheck.prop('checked', false);
          cartSubmit.prop('disabled', true);
        }else {  // 年齢が20歳以上の場合
          cartSubmit.prop('disabled', false);
        }
      }else {  // 生年月日が空白の場合
        ageCheck.prop('checked', false);
        cartSubmit.prop('disabled', true);
      }
    }else {  // 「同意する」チェックボックスにチェックが入っていない場合
      cartSubmit.prop('disabled', true);
    }
  });
  
  // 「ご購入の手続きへ」ボタン押下イベント
  cartSubmit.on('click', function() {
    if(ageCheck.prop('checked') == true ) {  // 「同意する」チェックボックスにチェックが入っている場合
      if(birthdayYear.value != '' && birthdayMonth.value != '' && birthdayDay.value != '') {  // 生年月日が空白ではない場合
        // 年齢確認
        let age = getUserAge( birthdayYear.value, birthdayMonth.value, birthdayDay.value );
        if( age < 20) {  // 年齢が20歳未満の場合
          ageCheck.prop('checked', false);
          cartSubmit.prop('disabled', true);
        }else {  // 年齢が20歳以上の場合
          cartSubmit.prop('disabled', false);
        }
      }else {  // 生年月日が空白の場合
        ageCheck.prop('checked', false);
        cartSubmit.prop('disabled', true);
      }
    }else {  // 「同意する」チェックボックスにチェックが入っていない場合
      cartSubmit.prop('disabled', true);
    }
  });
  
  // load時、年月変更時に実行する
  window.onload = function(){
    $set_year();
    $set_month();
    $set_day();
    birthdayYear.addEventListener('change',$set_day)
    birthdayMonth.addEventListener('change',$set_day)
  }
  
  /**
   *
   * 関数
   *
  **/
  // 年齢確認
  function getUserAge( year, month, day ) {
    //誕生日を一旦 Date クラスに変換する
    const birthdayDate = new Date(year, month - 1, day);
    
    if( year != birthdayDate.getFullYear() || (month - 1) != birthdayDate.getMonth() || day != birthdayDate.getDate() ) { // 不正の値判定
      return null;
    }
    
    //今日の日付けを取得する
    var todayDate = new Date();
    //誕生日を計算する
    var userAge = todayDate.getFullYear() - birthdayDate.getFullYear();
    // 誕生日
    var currentYearDate = new Date(todayDate.getFullYear(), birthdayDate.getMonth(), birthdayDate.getDate());
    if(currentYearDate > todayDate) {  // 今年の誕生日を迎えていない場合
      userAge = ( userAge - 1 );
    }
    
    return userAge;  // 年齢を返す
  }
</script>

ほぼほぼコメントの通りなので、解説省略したい…!
解説しろよという方はお気軽におっしゃってください…解説します…。

{% break %}

ここもしっかりと書きましょうね~書かないと商品タイプ「お酒」の数だけ記述されちゃって気持ち悪いですからね~。

ここまでお付き合いいただき、ありがとうございました!
お疲れ様でした~。

この記事が気に入ったらサポートをしてみませんか?