jQuery / 要素を順番に表示する

.delay(index * gap).fadeIn(duration)で開始時刻をずらせます。効果時間が間隔より長いと重なります。前の表示が完全に終わってから次へ進むなら、完了コールバックから次要素を実行します。

開始間隔と完了順

.delay()は各要素の列で次処理を待たせます。集合全体の共通列ではありません。同じ待ち時間だけでは同時期になり、indexを掛けると開始差になります。JavaScript全体は止めません。

間隔200ms、.fadeIn()時間400msです。時間差方式はAlpha完了前にBeta開始、逐次方式は完了後さらに200ms待ちます。ブラウザー負荷による差があるため厳密時刻の保証ではありません。

.each()は集合順です。#items直下.itemはAlpha・Beta・Gammaですが、並べ替えた集合やCSS配置では見える順と違う場合があります。読者の順序を決めます。

比較できる完全な例

全コードをUTF-8のappearance-demo.htmlへ保存し開きます。インターネットから公式jQuery完全版4.0.0を読みます。時間付きeffectsが必要でslimにしません。サーバーや導入は不要です。Windows Edge、3.7.1・4.0.0で順序と復元・再生を確認しました。

HTMLは表示状態で、準備スクリプト後に隠します。読込失敗だけでCSSにより内容が永久に隠れる構成ではありません。次は初期化後です。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Staggered versus strictly sequential appearance</title>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <style>
    body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 20px; }
    button, select { font: inherit; margin: 4px; }
    .item { border: 2px solid teal; padding: 12px; margin: 12px 0; background: #e7f5f1; }
    pre { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <label>Mode <select id="mode"><option value="stagger">Staggered starts</option><option value="serial">Wait for completion</option></select></label>
  <button type="button" id="start">Start</button>
  <button type="button" id="reset">Reset</button>
  <div id="items"><div class="item" id="alpha">Alpha</div><div class="item" id="beta">Beta</div><div class="item" id="gamma">Gamma</div></div>
  <pre id="result" aria-live="polite"></pre>
  <pre id="events"></pre>
  <script>
    jQuery(function ($) {
      const $items = $("#items > .item"), gap = 200, duration = 400;
      let generation = 0, busy = false, state = "ready", completed = [], events = [];
      function report() {
        $("#result").text(`mode=${$("#mode").val()}\nstate=${state}\nvisible=${$items.filter(":visible").length}/${$items.length}\ncompleted=${completed.join(",") || "(none)"}`);
        $("#events").text(events.join("\n") || "(none)");
      }
      function reset() {
        generation++; // Invalidate callbacks before settling old effects.
        $items.stop(true, true).hide();
        busy = false; state = "ready"; completed = []; events = [];
        $("#start").prop("disabled", false); report();
      }
      function start() {
        if (busy) return;
        reset(); busy = true; state = "running"; $("#start").prop("disabled", true);
        const run = generation, mode = $("#mode").val();
        const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
        if (reduce) {
          $items.show(); completed = $items.map(function () { return this.id; }).get();
          events = ["Reduced motion: shown immediately"];
          busy = false; state = "done"; $("#start").prop("disabled", false); report(); return;
        }
        function reveal(index, wait) {
          const $item = $items.eq(index), id = $item[0].id;
          $item.delay(wait).queue(function (next) {
            if (run !== generation) return;
            events.push("start " + id); next(); report();
          }).fadeIn(duration, function () {
            if (run !== generation) return;
            completed.push(id); events.push("done " + id);
            if (completed.length === $items.length) {
              busy = false; state = "done"; $("#start").prop("disabled", false);
            } else if (mode === "serial") reveal(index + 1, gap);
            report();
          });
        }
        if (!$items.length) { busy = false; state = "done"; $("#start").prop("disabled", false); report(); return; }
        if (mode === "stagger") $items.each(function (index) { reveal(index, index * gap); });
        else reveal(0, 0);
        report();
      }
      $("#start").on("click", start);
      $("#reset").on("click", reset);
      $("#mode").on("change", reset);
      reset();
    });
  </script>
</body>
</html>

初期は表示0、完了なしです。

mode=stagger
state=ready
visible=0/3
completed=(none)
  1. Staggered startsでStart。start betaがdone alphaより先に出て効果が重なり得ます。
  2. 終了はstate=done、visible=3/3、completed=alpha,beta,gammaでStart再使用可です。
  3. Wait for completionに変えると復元します。Start後はstart alpha → done alpha → start beta → done beta → start gamma → done gammaです。
  4. 途中Resetで初期非表示へ戻ります。待っても古い効果が復活せず、Startで再生できます。
  5. OSや開発者ツールで動きを減らしてStartすると即時表示しReduced motion: shown immediatelyです。

連打と初期化

実行中Startを無効化し関数入口でもbusyを確認します。コードからの再呼出しでも積みません。各要素完了を集め、一個の完了を全体完了としません。

.stop(true, true)は待機を消し現在効果を終端へ整理します。Resetは先にgenerationを変え旧コールバックを無効にし、その後整理・非表示にします。jumpToEndは完了を呼び得るため順番を逆にしません。

残った状態と列が再生へ混ざるのを防ぎます。clearQueueだけでは現在効果は止まりません。別setTimeoutなら別途取消しが必要で、例は基本fx列のdelayです。

ログ用.queue()は開始記録後next()を呼びfadeInへ進みます。省くと列が止まります。逐次方式は完了から次インデックスだけを開始します。

設定と実画面

prefers-reduced-motionがreduceなら待ちとフェードを省き最終状態にします。例だけの設定で全体効果は変えません。装飾のため重要情報へのアクセスを遅らせません。

display:noneから出すと空間が増え後続が動きます。位置を保つなら場所を確保するか別方式を検討します。入力などがある場合は隠したときのキーボードとフォーカスも設計します。

動的追加には集合と完了数を再定義します。$itemsは初期三つで自動追加しません。全部再生か新規だけか決め、復元と列管理を合わせます。