【コンポーネント】コンテンツを開閉させるもっと見るボタン

長いコンテンツを省略して表示し、ボタンを押すとすべて表示させるアノUI。

スマホでは省略表示、それ以外は全表示という実装が多いのですが、これが毎回地味に大変…:sweat:

というわけで、汎用的に利用できるコンポーネントを作成しました。

## 仕様

* 表示・非表示
  * `<input type="checkbox">` のチェックの有無で管理
* 省略時のコンテンツの高さ
  * CSS変数（Custom Properties）で管理
* スライドアニメーション
  * CSS の `transition` で `max-height` をアニメーション
  * `max-height` の値は JavaScript で要素の高さを取得し、CSS変数に設定

## HTML

:::note warn
親要素の `div` に設定している `class="js-showMore"` は JavaScript のトリガーです。
アニメーションさせない場合は不要です。
:::


```html:HTML
<div class="showMore js-showMore">
  <input class="showMore_trigger" id="showMore" type="checkbox">
  <label class="showMore_btn" for="showMore"></label>
  <div class="showMore_contentWrapper">
    <div class="showMore_content">
      <!-- ここにコンテンツを挿入 -->
    </div>
  </div>
</div>
```

## Sass

:::note warn
アニメーションさせない場合は、73行目の `--mask-height: auto;` のコメントアウトを解除してください。
:::

```scss:SCSS
.showMore {
  --mask-height: 500px;
  --mask-color: 40 42 54;
  --transition-easing: cubic-bezier(0.075, 0.82, 0.165, 1);
  --transition-duration: 0.5s;
  display: flex;
  flex-direction: column;
}
.showMore_btn {
  position: relative;
  display: block;
  order: 2;
  width: 50px;
  height: 50px;
  margin: 20px auto 0;
  font-size: 2rem;
  color: #fff;
  cursor: pointer;
  background-color: #000;
  border-radius: 50%;
  &::before,
  &::after {
    position: absolute;
    top: 50%;
    left: 50%;
    content: "";
    background-color: #fff;
    transform: translate(-50%, -50%);
  }
  &::before {
    width: 20px;
    height: 2px;
  }
  &::after {
    width: 2px;
    height: 20px;
  }
}
.showMore_contentWrapper {
  position: relative;
  order: 1;
  max-height: var(--mask-height);
  overflow: hidden;
  transition: max-height var(--transition-duration) var(--transition-easing);
  &::before {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 50px;
    content: "";
    background: linear-gradient(
      0deg,
      rgb(var(--mask-color) / 1) 0%,
      rgb(var(--mask-color) / 0) 100%
    );
  }
}
.showMore_trigger {
  // チェックボックスは常に非表示
  display: none;
  &:checked {
    // ボタンをマイナス表示
    + .showMore_btn {
      &::after {
        display: none;
      }
    }

    // コンテンツをすべて表示
    ~ .showMore_contentWrapper {
      // アニメーションさせない場合はコメント解除
      // --mask-height: auto;

      // マスク要素を非表示
      &::before {
        display: none;
      }
    }
  }
}
```

## JavaScript

:::note warn
アニメーションさせない場合は JavaScript は不要です。
:::

```javascript:JS
class ShowMoreShowLessButton {
  #defaults = {
    selector: '.js-showMore',
    customPropertyName: '--mask-height'
  }

  #options
  #showMoreList
  constructor(config) {
    this.#options = { ...this.#defaults, ...config }
    this.#showMoreList = document.querySelectorAll(this.#options.selector)
    this.#init()
  }

  #init() {
    for (const el of this.#showMoreList) {
      const trigger = el.querySelector('input[type="checkbox"]')
      const contentWrapper = el.querySelector('div')
      const content = contentWrapper.querySelector('div')
      const contentHeight = content.getBoundingClientRect().height
      trigger.addEventListener('change', () => {
        if (trigger.checked) {
          contentWrapper.style.setProperty(
            this.#options.customPropertyName,
            contentHeight + 'px'
          )
        } else {
          contentWrapper.removeAttribute('style')
        }
      })
    }
  }
}
const showMoreShowLessButton = new ShowMoreShowLessButton()
```

## DEMO

<p class="codepen" data-height="500" data-default-tab="result" data-slug-hash="abEKWaY" data-user="taqumo" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/taqumo/pen/abEKWaY">
  Show More / Less Content Button</a> by Takuya Mori (<a href="https://codepen.io/taqumo">@taqumo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>