【備忘録：HTML、CSS、jQuery】PCはタブ、SPはアコーディオン

## ワンソースでタブ、アコーディオン
ワンソース**「PCはタブ、SPはアコーディオン」**を表現しないといけなく、結構ツラかったので、、備忘録として残していきます。

### そもそもワンソースでタブ、アコーディオンは？
ワンソースでタブ、アコーディオンにするにはHTML構造的に結構無理があります。。

```html:tab.html
//ここはタブパネル
<div class="m-tab">
  <div class="m-tab-panel">
    <div class="is-current"><a href="#tab01" class="js-tab">hogehoge</a></div>
    <div><a href="#tab02" class="js-tab">hogehoge</a></div>
    <div><a href="#tab03" class="js-tab">hogehoge・<br class="m-hide-sp">hogehoge</a></div>
  </div><!-- /.m-tab-panel -->
//ここはタブコンテンツ
  <div class="m-tab-content m-tab-content-sp">
    <div id="tab01" class="m-tab-content-item is-show-tab">
     ここにタブコンテンツここにタブコンテンツここにタブコンテンツここにタブコンテンツ
    <!-- /.m-tab-content-item --></div>
    <div id="tab02" class="m-tab-content-item">
     ここにタブコンテンツここにタブコンテンツここにタブコンテンツここにタブコンテンツ
    <!-- /.m-tab-content-item --></div>
    <div id="tab03" class="m-tab-content-item">
     ここにタブコンテンツここにタブコンテンツここにタブコンテンツここにタブコンテンツ
    <!-- /.m-tab-content-item --></div>
  <!-- /.m-tab-content --></div>
<!-- /.m-tab --></div>
```

```html:acc.html
<div class="m-accordion">
  <div class="m-accordion-heading">
      <p><a href="#acc01" class="js-accordion"><span class="m-accordion-icon">hogehogehoge</span></a></p>
  <!-- /.m-accordion-hdg --></div>
  <div id="acc01" class="m-accordion-content">
      <div class="m-accordion-content-body">
          <h3 class="m-heading3">hogehogehoge</h3>
          <p class="m-heading3">ここにアコーディオンコンテンツ</p>
      <!-- /.m-accordion-content-body --></div>
  <!-- /.m-accordion-content --></div>

  <div class="m-accordion-heading">
    <p><a href="#acc02" class="js-accordion"><span class="m-accordion-icon">hogehogehoge</span></a></p>
  <!-- /.m-accordion-hdg --></div>
  <div id="acc02" class="m-accordion-content">
      <div class="m-accordion-content-body">
          <h3 class="m-heading3">hogehogehoge</h3>
          <p class="m-heading3">ここにアコーディオンコンテンツ</p>
      <!-- /.m-accordion-content-body --></div>
  <!-- /.m-accordion-content --></div>
<!-- /.m-accordion --></div>
```
このように、
タブは**「タブパネルが終わった下にコンテンツ群を配置」**
アコーディオンは**「アコーディオンタイトルのすぐ下にコンテンツを配置」**
という構造にしないと機能の実装が難しいからです。

今回tab.htmlの状態で組んでSP時アコーディオンという変更がなされたので、それをjQueryで対応しました。
htmlソースはtab.htmlママとしますが、**クラスが違うので念の為再度掲載します。**

```html:tabtoacc.html
//PC表示の場合のソース
<div class="m-tab">
  <div class="m-tab-panel m-tab-panel-sp">
    <div class="is-current"><a href="#tab01" class="js-tab js-accordion-sp">hogehogehoge</a></div>
    <div><a href="#tab02" class="js-tab js-accordion-sp">hogehogehoge</a></div>
    <div><a href="#tab03" class="js-tab js-accordion-sp">hogehogehoge<br class="m-hide-sp">hogehogehoge</a></div>
    <div><a href="#tab04" class="js-tab js-accordion-sp">hogehogehoge</a></div>
  <!-- /.m-tab-panel --></div>

  <div class="m-tab-content m-tab-content-sp">
    <div id="tab01" class="m-tab-content-item m-tab-content-item-sp is-show-tab is-show-accordion-sp">
        ここにタブコンテンツここにタブコンテンツここにタブコンテンツ
    <!-- /.m-tab-content-item --></div>
    <div id="tab02" class="m-tab-content-item m-tab-content-item-sp">
      ここにタブコンテンツここにタブコンテンツここにタブコンテンツ
    <!-- /.m-tab-content-item --></div>
    <div id="tab03" class="m-tab-content-item m-tab-content-item-sp">
      ここにタブコンテンツここにタブコンテンツここにタブコンテンツ
    <!-- /.m-tab-content-item --></div>
    <div id="tab04" class="m-tab-content-item m-tab-content-item-sp">
      ここにタブコンテンツここにタブコンテンツここにタブコンテンツ
    <!-- /.m-tab-content-item --></div>
  <!-- /.m-tab-content --></div>
<!-- /.m-tab --></div>
```

```javascript:index.js
//タブとアコーディオンの関数
function tabPc() {
タブの関数は割愛
}
  function accSp() {
    let $panelContentArray = $('.m-tab-content-sp').find('.m-tab-content-item-sp');//アコーディオンコンテンツを配列にする
    let $accSpTrigger= $('.js-accordion-sp');//SP時のアコーディオントリガー

    $('.m-tab-panel-sp').find('div').each(function(panelTabNum,panelTab){//アコーディオン見出しを探して、eachで取り出す
      let $paneltabArray = $(panelTab);//アコーディオン見出しを変数に格納しておく
      $($panelContentArray[panelTabNum]).clone(true).insertAfter($paneltabArray).addClass('js-acc-sp-clone');
      //アコーディオンコンテンツをアコーディオン見出し下にクローンして挿入する。後で消せるように.js-acc-sp-cloneのクラスをつける。をあるだけ回す
    });

    $('.js-accordion-sp').off('click');//アコーディオンのイベントが一回残るので削除

    $($accSpTrigger).on('click', function() {//SP時のアコーディオントリガーをクリックすると、
      let $accSpOpenHref = $(this).attr('href');//href属性を取得
      $($accSpTrigger).not($(this)).parent().next().slideUp();//アコーディオンが開いているところは閉じる
      $('.m-tab-panel-sp').find($accSpOpenHref).slideToggle();//クリックしたところを開く

      if($($accSpTrigger).parent().hasClass('is-current')){////アコーディオン見出しが.is-currentを持っていれば
        $($accSpTrigger).parent().removeClass('is-current');//.is-currentを削除
      } else {
        ($accSpTrigger).parent().addClass('is-current').siblings('div').removeClass('is-current');
        //クリックされたアコーディオン見出しに.is-currentをつけて兄弟要素に.is-currentがついていれば削除
      }
      return false;
    });
  }
}
```
こんな感じです。

何をしているかというと、
**・SP時は.m-tab-content-itemをタブパネルのdiv内にclone（コピー）をしている**
になります。

ちょっとわかりにくいのでイメージソースを以下に記載します。

```html:tabtoacc.html
//SP表示の場合のソース
<div class="m-tab">
  <div class="m-tab-panel m-tab-panel-sp">
    <div class="is-current">
      <a href="#tab01" class="js-tab js-accordion-sp">hogehogehoge</a>
      <div id="tab01" class="m-tab-content-item m-tab-content-item-sp is-show-tab is-show-accordion-sp">
        //タブコンテンツをタブパネル下にclone（コピー）し配置
        ここにタブコンテンツここにタブコンテンツここにタブコンテンツ
      <!-- /.m-tab-content-item --></div>
    </div>
    <div>
      <a href="#tab02" class="js-tab js-accordion-sp">hogehogehoge</a>
        <div id="tab02" class="m-tab-content-item m-tab-content-item-sp">
          //タブコンテンツをタブパネル下にclone（コピー）し配置
          ここにタブコンテンツここにタブコンテンツここにタブコンテンツ
        <!-- /.m-tab-content-item --></div>
    </div>
以下省略
  <!-- /.m-tab-panel --></div>
<!-- /.m-tab --></div>
```
という感じです。

PCタブ、SPアコーディオンの機能実装はできたので、レスポンシブに合わせた挙動にさせるようにしました。

```javascript:index.js
let $windowWidth = window.innerWidth;//画面幅取得

  if($windowWidth < 768){//最初のアクセス時の分岐
    accSp();//768px以下ならアコーディオン関数を発火
  } else {
    tabPc();//768px以上ならタブ関数を発火
  }

  $(window).resize(function() {//windowリサイズ時の処理
    let $breakPoint = window.matchMedia('screen and (max-width: 768px)');
    $('.js-acc-sp-clone').remove();//クローンしたアコーディオンを一度消す

    if($breakPoint.matches){// ウィンドウリサイズ時、767px以下の場合なら、
      accSp();//リサイズ後、768px以下ならアコーディオン関数を発火

    } else {// ウィンドウリサイズ時、767px以上の場合なら、
      tabPc();//リサイズ後、768px以上ならタブ関数を発火
    }
  });
```
ただ、cloneをするとPC時にサイズを戻した際に、ソースが残ってしまったので、
**・ウィンドウリサイズ時に必ずcloneした要素を削除し、リサイズ時幅を判定してタブかアコーディオンを発火させる**
という流れで対処をしました。

無理くり感はありましたが実装はできましたが、、
要件変更はしっかりヒアリングしないと実感したコーディングでした。