前言
この記事は Implementing a pin-to-bottom scrolling element with only CSS を読んだ後、JavaScriptを使った方法を整理して紹介しています。
最近、ウェブページで新しいコンテンツを追加するたびにスクロール位置を最下部に調整するという動作が増えてきました。例えば、Twitchのコメントエリア:
または、YouTubeのコメントエリア:
このような動作を表す日本語の専門用語はまだ存在しないようですので、一旦「pin to bottom」と呼びましょう。
[JavaScript] 実装方法
function append(comment) {
const comments = document.querySelector(".comments")
comments.appendChild(comment)
comments.scrollTop = comments.scrollHeight
}
まず、コンテナを固定の高さに設定し、overflow-y: auto
を追加します。これにより、コンテンツがコンテナの高さを超えた場合にスクロールが可能になります。
新しい要素を追加するたびに、comments.scrollTop = comments.scrollHeight
を呼び出すだけで、scrollTop
が常にscrollHeight
と同じ高さになるようにします。
考慮すべき問題
この実装は非常に簡単ですが、問題が発生する可能性があります。例えば、ユーザーが他のコンテンツを見るためにスクロールしようとした場合でも、新しいコメントが表示されると自動的に最下部にスクロールされてしまいます。
この問題を解決するために、コンテナの現在のscrollTop
をチェックし、scrollHeight
との差が一定の範囲内である場合はユーザーのスクロールを補完しないようにします。
if (
comments.offsetHeight < comments.scrollHeight &&
comments.scrollTop + comments.offsetHeight + 150 > comments.scrollHeight
) {
comments.scrollTop = comments.scrollHeight - comments.offsetHeight
}
ここでのoffsetHeight
はコンテナの高さ(CSSのheight
)を表し、scrollHeight
はコンテナのスクロール全体の高さを表します。scrollTop
は現在のスクロール位置です。
offsetHeight
がまだ超えていない場合は、下方向へのスクロール動作を定義しません。- ユーザーが150px以上スクロールした場合は、自動的にスクロール補完を行いません。
また、ユーザーのスクロールを補完しない場合、通常はボタンやインジケーターを追加して、ユーザーが最新のコメントにジャンプできるようにします。
さらに、コメントリストが非常に長い場合、パフォーマンスの問題が発生する可能性があります。以下の方法で対処できます:
- 最新の500件のコメントのみを表示する(具体的な数字は要件に応じて調整)
- windowingなどの方法を使用して最適化する(ただし、高さが不定の場合には適用できるかどうかはわかりません)
overflow-anchor
今回紹介するのは、overflow-anchorを使用して同様の効果を実現する方法です。この属性は、Implementing a pin-to-bottom scrolling element with only CSSという記事で紹介されており、overflow-anchor
を使用して上記の効果を実現しています。
この属性は、CSS Scroll Anchoring Moduleで定義されており、ユーザーがコンテンツをスクロールする際に効率的にコンテンツを消費できるようにするために、overflow-anchor
の仕組みが定義されています。
詳細なアルゴリズムの定義はこちらにあります。
- スクロールコンテンツSがスクロールするたびに、
none
以外の属性を持つ子要素からアンカーノードを選択します。 - NがスクロールコンテンツS内にあり、表示されていない場合(元の文章ではexcluded subtreeおよびfully clippedと記述されていますが、要するにスクロールボックス内で見えない場合)、何も行いません。
- NがスクロールコンテンツS内で表示されている場合、Nをアンカーノードとして選択します。
- NがスクロールコンテンツS内で半分だけ表示されている場合、子要素に対して上記のアルゴリズムを再帰的に繰り返し、アンカーノードを見つけます。
実際の仕様ではさらに多くの動作が定義されていますので、興味があれば参照してみてください。ただし、何ができるかを理解するだけで十分です。
まず、コンテナにアンカーノードを追加します:
<ul class="comments">
<li class="anchor"></li>
</ul>
ここでのanchor
は、CSSのoverflow-anchor: auto
を指定して、ブラウザがアンカーノードとして認識するようにします。
.comments {
& > .comment {
overflow-anchor: none; // アンカーノードとして扱わない
}
.anchor {
overflow-anchor: auto; // アンカーノードとして扱う
}
}
そして、コメントの部分をinsertBefore
を使用してアンカーノードの前に追加するように変更します。
実測結果
左側は通常の方法で実装したもので、右側はoverflow-anchor
を使用して実装したものですが、アンカーノードをスクロールする必要があるようです。
余談ですが、記事中のテキストは以前に読んだ伊坂幸太郎の「汽油生活」を元に書いたもので、便宜上いくつかの段落をコピー&ペーストしました。
現在のブラウザのサポートはFirefox 66およびChrome 56であり、まだ一般的ではありませんが、将来的には同様の機能を実装する新しい方法が出てくるかもしれません。欠点はアンカーノードを追加する必要があることですが、純粋なCSSで実現できるという点は興味深い方法です。