tomisan.com

【便利】detailsとsummaryタグ【覚書】

【便利】detailsとsummaryタグ【覚書】

便利なタグですよね。

アコーディオンがこれだけで実装できるわけですから!

ただ、動きがちょっとねぇ、と思ったりする場合もあるかなと思うので、実際に使ったものを。

 

HTML

<details>
    <summary><h4><span>精霊の力</span></h4></summary>
    <div class="details-inner">
         <p>精霊は生まれ持った基礎力(単位:エコル)を持ち、人間でいう体力のようなもの。一般的な精霊は完成形で生まれるため成長せず、基礎力は100~150エコルほど。稀に200エコルに達する精霊は精霊王候補となる。リーフは成長するために生まれた特別な精霊で、テラと出会った頃には200エコルほどあったが、これは自身に枷を掛けていた状態であり、実際の力はその倍。リーフは守り人との血の契約で力がさらに何倍にもなる。</p>
    </div>
</details>

 

SCSS

details {
  position: relative;
  overflow: hidden;
  transition: height 0.5s ease;
  summary {
    cursor: pointer;
    display: block;
    padding-bottom: 1.5rem;
    &::-webkit-details-marker {
      display: none;
    }
    h4 {
      position: relative;
      margin-bottom: 0 !important;
      &:hover {
        color: $hrborder;
      }
      &::before {
        content: "+";
        display: inline-flex;
        align-items: center;
        justify-content: center;
        height: 2rem;
        width: 2rem;
        font-size: $font-size-base;
        line-height: $line-height1;
        transition: transform 0.2s ease;
        transform-origin: center center;
      }
    }
  }
  &[open] {
    summary {
      h4 {
        &::before {
          transform: rotate(45deg);
        }
      }
    }
  }
}

 

JavaScript

document.querySelectorAll('details').forEach(details => {
  const summary = details.querySelector('summary');
  const inner = details.querySelector('.details-inner');
  let isAnimating = false;

  summary.addEventListener('click', (e) => {
    e.preventDefault();
    if (isAnimating) return;
    isAnimating = true;

    if (details.open) {
      // 【閉じる時】
      details.style.height = `${details.offsetHeight}px`;

      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          details.style.height = `${summary.offsetHeight}px`;
        });
      });

      const onTransitionEnd = (event) => {
        if (event.propertyName === 'height') {
          details.removeAttribute('open');
          details.style.height = '';
          isAnimating = false;
          details.removeEventListener('transitionend', onTransitionEnd);
        }
      };
      details.addEventListener('transitionend', onTransitionEnd);

    } else {
      // 【開く時】
      const startHeight = summary.offsetHeight;
      details.open = true;

      requestAnimationFrame(() => {
        const endHeight = summary.offsetHeight + inner.offsetHeight;
        details.style.height = `${startHeight}px`;

        requestAnimationFrame(() => {
          details.style.height = `${endHeight}px`;
        });
      });

      const onTransitionEnd = (event) => {
        if (event.propertyName === 'height') {
          details.style.height = '';
          isAnimating = false;
          details.removeEventListener('transitionend', onTransitionEnd);
        }
      };
      details.addEventListener('transitionend', onTransitionEnd);
    }
  });
});

 

実際に使っているページはこちら。

https://tomisan.com/kizahana/world/

 

なんだかんだでゴリゴリ書いてますが、便利になったなぁ! と思うこの頃。

 

コメントは受け付けていません。