ステップアコーディオン

バニラJSでステップアコーディオンアニメーションを実装。
Jqueryを使用しないことで軽量で且つどこでも動くフレキシブルなコードを実現。
採用ステップなど、説明としてはサイトに入れておきたいが、長いイメージを持たれたくない時に便利なアニメーション。

HTML

<div class="accordion1">
  <ol class="accordion1__items">
    <li class="accordion1__item">
      <div class="accordion1__itemInner">
        <p class="accordion1__txt"><span>STEP01</span>15分ヒアリング</p>
      </div>
    </li>
    <li class="accordion1__item js-accordion1Items">
      <div class="accordion1__itemInner">
        <p class="accordion1__txt"><span>STEP02</span>日程の調整</p>
      </div>
    </li>
    <li class="accordion1__item js-accordion1Items">
      <div class="accordion1__itemInner">
        <p class="accordion1__txt"><span>STEP03</span>初回面談</p>
      </div>
    </li>
    <li class="accordion1__item js-accordion1Items">
      <div class="accordion1__itemInner">
        <p class="accordion1__txt"><span>STEP04</span>面接後に内定</p>
      </div>
    </li>
  </ol>
  <span class="accordion1__more js-accordion1Btn">STEP2~を開く</span>
</div>

ポイント!

①<li>の隠すものに全て.js-accordion1Itemsを付与。
②ボタンは<ol>の外に置き.js-accordion1Btnを付与。

CSS

.accordion1__items {
  width: 300px;
  max-width: 100%;
  margin: 0 auto;
  position: relative;
 list-style: none;
 padding-left: 0;
}
.accordion1__item:after {
  margin: 20px auto;
  display: block;
  content: "";
  width: 0;
  height: 0;
  border-right: solid 12px transparent;
  border-left: solid 12px transparent;
  border-top: solid 10px #8F8F8F;
}
.accordion1__item:last-of-type:after {
  display: none;
}
.js-accordion1Items {
  /*最初は閉じておく*/
  display: none;
  opacity: 0;
}
.js-accordion1Btn.is-hide {
  /*ボタンが消える時*/
  opacity: 0;
}
.accordion1__itemInner {
  padding: 20px 5%;
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.2);
  background-color: #fff;
}
.accordion1__txt {
  display: flex;
  column-gap: 2em;
}
.accordion1__txt span {
  font-weight: bold;
}
.accordion1__more {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 100px;
  color: #fff;
  background-color: #63C560;
  width: 150px;
  height: 40px;
  font-size: 14px;
  margin: 20px auto 0;
  cursor: pointer;
  box-shadow: 0 5px 0 #4E9C4C;
  transition: all ease .3s;
}
.accordion1__more:hover {
  box-shadow: 0 0 0;
  transform: translateY(4px);
}

ここだけは必須記述!

.js-accordion1Items {
/*最初は閉じておく*/
display: none;
opacity: 0;
}
.js-accordion1Btn.is-hide {
/*ボタンが消える時*/
opacity: 0;
}

JS

document.addEventListener('DOMContentLoaded', () => {
  const btn = document.querySelector('.js-accordion1Btn');
  const items = Array.from(document.querySelectorAll('.js-accordion1Items'));
  if (!btn || items.length === 0) return;

  const STEP = 300;
  const DURATION = 300;
  const OPEN_START_DELAY = 80; // ← items側を少し遅らせる(調整用)
  const last = items.length - 1;

  // 初期テキストを保持(元に戻す用)
  const defaultText = btn.textContent;

  items.forEach((el) => {
    el.style.transitionProperty = 'opacity';
    el.style.transitionDuration = `${DURATION}ms`;
    el.style.transitionTimingFunction = 'ease';
  });

  let open = false;

  btn.addEventListener('click', () => {
    open = !open; 
    
    // ★テキスト切り替え
    btn.textContent = open ? '閉じる' : defaultText;

    if (open) {
      // ① ボタンを「即」消す(押し下げが見える前に消す)
      btn.style.transition = 'none';
      btn.classList.add('is-hide');
      // 次フレームで transition を戻す(再表示用)
      requestAnimationFrame(() => {
        btn.style.transition = '';
      });

      // ② 次のフレームで全itemsを先に表示して高さ確保(透明のまま)
      requestAnimationFrame(() => {
        items.forEach((el) => {
          el.style.transitionDelay = '0ms';
          el.style.display = 'block';
          el.style.opacity = '0';
        });

        // ③ ボタンが消えた後に items を動かし始める
        setTimeout(() => {
          items.forEach((el, i) => {
            el.style.transitionDelay = `${i * STEP}ms`;
            el.style.opacity = '1';
          });

          // ④ 全部出そろったらボタンをふわっと戻す
          const total = last * STEP + DURATION;
          setTimeout(() => {
            if (open) btn.classList.remove('is-hide');
          }, total);
        }, OPEN_START_DELAY);
      });

      return;
    }

    // ----- 閉じる(下→上) -----
    items.forEach((el, i) => {
      const delay = (last - i) * STEP;
      el.style.transitionDelay = `${delay}ms`;
      el.style.opacity = '0';

      setTimeout(() => {
        if (!open) el.style.display = 'none';
      }, delay + DURATION);
    });
  });
});

まるっとコピペでOK!

実際の動き

  1. STEP0115分ヒアリング

  2. STEP02日程の調整

  3. STEP03初回面談

  4. STEP04面接後に内定

STEP2~を開く
みんなにもシェアしてあげて!

この記事を書いた人

WEBサイトで使える“動くUI”を、コピペで実装可能にし、どこでも誰でも使えるコードに特化して発信しているフロントエンドエンジニア。
中学生で初めてコードに出会い初めてコーディングを知る。
その後、WEB制作会社のフロントエンドエンジニアとしてキャリアをスタートし、フリーランスを経て、現在はWEB制作会社の代表取締役。
コーディングによって人生を創り上げてきたからこそ、コードを通して誰かの役に立てたらという想いで、これまでの知識と経験を総動員したコードを公開中。

コメント

コメントする