overflow-anchor で、チャット欄を JS なしで常に最下部に張り付かせる
ログ内の要素のスクロールアンカーを全部切り、末尾に置いた高さ 1px の要素だけをアンカーにすると、新しいメッセージが増えても表示が最下部に追従する。
どんな時に使う?
- チャット、ログビューア、AI の応答ストリーミングなど、下に行が増えていく欄で「いま一番下を見ている人には新着を見せ続けたい」とき。
- 追加のたびに
scrollTop = scrollHeightを JS で書く処理を減らしたいとき。
コード
html
<div class="log">
<p>メッセージ1</p>
<p>メッセージ2</p>
<!-- 新しいメッセージは .pin の直前に追加する -->
<div class="pin"></div>
</div>css
.log {
overflow-y: auto;
height: 400px;
}
/* ログの中身はアンカー候補から外す */
.log * {
overflow-anchor: none;
}
/* 末尾の 1px だけをアンカーにする */
.log .pin {
overflow-anchor: auto;
height: 1px;
}ブラウザのスクロールアンカリングは、内容が増減しても「画面内にある基準の要素」の位置を保とうとする仕組み。基準になれるのが末尾の .pin だけなので、一番下までスクロールしている間は .pin が見え続けるよう位置が保たれ、結果として最下部に張り付く。利用者が上にスクロールして過去ログを読んでいる間は .pin が画面外なので、勝手に下へ戻されない。
注意点
- 最初の1回は下までスクロールしておく必要がある(初期表示で最下部にしたいなら、読み込み時に一度だけ JS でスクロールするか、
flex-direction: column-reverseなど別の手を併用する)。 - メッセージは必ず
.pinより前に挿入する。後ろに足すとアンカーが効かない。 - Safari はスクロールアンカリング(
overflow-anchor)に未対応の時期が長かったので、導入前に Can I use で対応状況を確認し、未対応環境では JS のスクロールで補う。