カランのブログ

Kalan 頭像照片,在淡水拍攝,淺藍背景

四零二曜日電子報上線啦!訂閱訂起來

ソフトウェアエンジニア / 台湾人 / 福岡生活
このブログはRSS Feed をサポートしています。RSSリンクをクリックして設定してください。技術に関する記事はコードがあるのでブログで閲覧することをお勧めします。

今のモード ライト

我會把一些不成文的筆記或是最近的生活雜感放在短筆記,如果有興趣的話可以來看看唷!

記事のタイトルや概要は自動翻訳であるため(中身は翻訳されてない場合が多い)、変な言葉が出たり、意味伝わらない場合がございます。空いてる時間で翻訳します。

オーバーフローアンカーによるボトムコンポーネントへのピンの実装

前言

この記事は 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は現在のスクロール位置です。

  1. offsetHeightがまだ超えていない場合は、下方向へのスクロール動作を定義しません。
  2. ユーザーが150px以上スクロールした場合は、自動的にスクロール補完を行いません。

また、ユーザーのスクロールを補完しない場合、通常はボタンやインジケーターを追加して、ユーザーが最新のコメントにジャンプできるようにします。

さらに、コメントリストが非常に長い場合、パフォーマンスの問題が発生する可能性があります。以下の方法で対処できます:

  1. 最新の500件のコメントのみを表示する(具体的な数字は要件に応じて調整)
  2. windowingなどの方法を使用して最適化する(ただし、高さが不定の場合には適用できるかどうかはわかりません)

overflow-anchor

今回紹介するのは、overflow-anchorを使用して同様の効果を実現する方法です。この属性は、Implementing a pin-to-bottom scrolling element with only CSSという記事で紹介されており、overflow-anchorを使用して上記の効果を実現しています。

この属性は、CSS Scroll Anchoring Moduleで定義されており、ユーザーがコンテンツをスクロールする際に効率的にコンテンツを消費できるようにするために、overflow-anchorの仕組みが定義されています。

詳細なアルゴリズムの定義はこちらにあります。

  1. スクロールコンテンツSがスクロールするたびに、none以外の属性を持つ子要素からアンカーノードを選択します。
  2. NがスクロールコンテンツS内にあり、表示されていない場合(元の文章ではexcluded subtreeおよびfully clippedと記述されていますが、要するにスクロールボックス内で見えない場合)、何も行いません。
  3. NがスクロールコンテンツS内で表示されている場合、Nをアンカーノードとして選択します。
  4. 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で実現できるという点は興味深い方法です。

次の記事

より安全なリクエストヘッダー-メタデータの取得リクエストヘッダー

前の記事

プロジェクトの環境変数を設定する-VIPER

この文章が役に立つと思うなら、下のリンクで応援してくれると大変嬉しいです✨

Buy me a coffee