前言
這篇文章是看完 Implementing a pin-to-bottom scrolling element with only CSS 後,並且介紹使用 JavaScript 的做法整理而成。
現在網頁越上越常出現每次加入新的內容時,就將 scroll 的位置調整到最底下。像是 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 的方式,當發生 resize 或 scroll 行為,會自動偵測需要被錨點的內容。
詳細的演算法定義在這裡。
- 滾動內容 S 每次滑動時,會選擇屬性不為
none
的子元素中選擇一個錨點 node。 - 如果 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; // 當作錨點
}
}
然後將 comment 的部分改用 insertBefore
確保插入在 anchor 前面。
實測結果
左邊是用一般的方式來做,右邊則是用 overflow-anchor
的方式實作,不過我發現好像要滾動一下錨點的行為才會正確運作。
額外一提,裡頭的文字內容是我之前看完伊坂幸太郎的汽油生活後寫的文章,為了方便就隨便從裡頭抓幾段複製貼上。
目前的瀏覽器支援度是 firefox 66 以及 Chrome 56,還不算很普及,不過未來要實作類似的功能或許又有新的作法了,雖然缺點在於必須要額外加上一個錨點,但可以透過純 CSS 來做到還算是蠻有趣的方法。