カテゴリ絞り込み中に投稿の日時順が崩れる問題を直した

起きていたこと

タイムラインの上部メニューで「game」を選ぶと、投稿が次の順に並んでいた。

2026-09-27
2026-09-27
2026-07-19
2026-07-02
2026-09-26   ← ここで急に新しい日付に戻る
2026-09-25

新しい順に並ぶはずなのに、途中で 9 月に戻ってしまう。

原因

サーバーは毎回きちんと新しい順に並べて返していた。問題はブラウザ側にあった。

  1. 先に「movie」などで絞り込んでスクロールすると、7 月の動画付き投稿まで読み込まれる。この投稿にはゲーム情報も付いていた。
  2. 「game」に切り替えると、すでに読み込み済みの投稿からゲーム付きのものだけが表示される(9/27・7/19・7/2)。
  3. 同時に、取りこぼしを防ぐために「続きをどこから読むか」の目印が、絞り込みなしの位置(9/27 付近)まで巻き戻される。
  4. スクロールすると 9/26・9/25 が届くが、新しいカードは常に一番下に追加していたため、7/2 の後ろに並んでしまった。

「巻き戻して新しめの投稿を取り直す」のに「置き場所はいつも末尾」という、2 つの仕組みの食い違いが原因だった。

直し方

並び替え用の数値をカードに持たせる

各カードにはもともと日時の文字列が付いていたが、「2026/3/8」と「2026-03-08」のように書式が混ざっていて、そのままでは比較に使えない。そこでサーバーが並び替えのために計算している数値(タイムスタンプ)を、カードの属性として一緒に出力するようにした。

初回表示と無限スクロールの追加分は別のファイルで描画しているので、両方に同じ属性を足している。

正しい位置に差し込む

新しいカードを追加するとき、画面上のカードを上から見ていき、「自分より古い最初のカード」の直前に差し込む。見つからなければ従来どおり一番下に追加する。

const insertInOrder = (node: HTMLElement) => {
  const ts = Number(node.getAttribute('data-sort-ts')) || 0;
  if (!ts) { container.appendChild(node); return; }
  for (const el of container.querySelectorAll(':scope > .timeline-item[data-sort-ts]')) {
    const t = Number(el.getAttribute('data-sort-ts')) || 0;
    if (t && t < ts) { container.insertBefore(node, el); return; }
  }
  container.appendChild(node);
};

時刻が同じ場合は既存カードの後ろに入るので、これまでの並びは変わらない。

変えなかったこと