Published on
-
4 mins read

Fix layout shift khi show/hide scrollbar trên Window

Authors
  • avatar
    Name
    Tuan Anh Huynh (Leo)
    Twitter
    @hta218
window

Khi nào thì gặp phải tình trạng ẩn/hiện scrollbar?

  • Mở 1 Modal hoặc Popup và prevent window scroll.
  • Toggle 1 Accordion hoặc Tab làm thay đổi chiều cao page dẫn đến hiện scrollbar.
  • ...

Nếu sử dụng Window thì các trường hợp này có thể dẫn đến layout shift hay page sẽ bị thụt vào trong 1 khoảng để dành chỗ cho scrollbar. Mình khá là không thích trải nghiệm này nên đã tìm ra 1 vài giải pháp đơn giản để khắc phục chỉ với CSS như sau

The classic fix

html {
overflow-y: scroll;
}

Cách cổ điển để fix chính là force scrollbar luôn xuất hiện (cho dù content ngắn hơn viewport - hay page không cần scroll) - lúc này scrollbar sẽ chuyển sang màu xám, và luôn đứng cố định, làm page không thể bị thò ra thụt vào được.

Dĩ nhiên mình không recommend cách này vì nó cũng làm page không đẹp như tình huống ban đầu!

Sử dụng view-width

Giải pháp này là thêm 1 khoảng margin-left (invisible) cho html element sao cho đúng bằng width của scrollbar, margin-left này sẽ tự động mất đi khi scrollbar ẩn và xuất hiện trở lại khi scrollbar hiện ra.

Vậy làm sao để tính toán được width của scrollbar?

Câu trả lời chính là nhờ vw! Nếu bạn chưa biết thì vw hay view-width là 1 đơn vị trong CSS tương tự như px, %, rem...

100vw sẽ tương ứng với 100% viewport width (bao gồm cả scrollbar!) còn 100% (cho html element) sẽ tương ứng 100% viewport width nhưng không bao gồm scrollbar

Như vậy chỉ cần kết hợp với calc() là có thể thêm được 1 khoảng margin-left đúng bằng scrollbar's width cho html element như này

html {
margin-left: calc(100vw - 100%);
}

margin-left sẽ bằng 0 khi không có scrollbar và bằng đúng width của scrollbar khi nó xuất hiện, điều này làm page không bị layout shift nữa!

Note

Nếu bạn show/hide scrollbar bằng cách set overflow-y: hidden; cho html khi mở 1 Popup với position: fixed; thì cũng nên set margin-left tương tự cho Popup's overlay để tránh Popup bị layout shift khi đóng nhé!

Ví dụ:

.overlay {
margin-left: calc(100vw - 100%);
}

Ngoài ra, còn 1 cách khác đó là set luôn width của html bằng đúng 100vw và chặn scroll ngang:

html {
width: 100vw;
overflow-x: hidden;
}

Dĩ nhiên cách này chỉ hợp lý khi content page của bạn không phải scroll ngang, ví dụ như blog chẳng hạn, và mình đang sử dụng cách này cho chính blog của mình! (Bật devtool lên là thấy nhé )

The no-code way

Có 2 cách không cần viết code để khắc phục tình trạng này:

  • 1 là bảo user không dùng Window nữa, đổi sang dùng MacOS (Với MacOS bạn có thể tùy chọn việc tự động ẩn hiện scrollbar)

  • 2 là... kệ cmn đấy không cần fix, đôi khi user không quá bận tâm đến những bug UI nhỏ đó như bạn nghĩ đâu, giá trị của page mang lại mới là thứ đáng quan tâm hơn. Chỉ là mình hay cầu toàn quá nên mới chú ý đến fix những cái đó thôi!


Nếu bạn có giải pháp nào khác cho vấn đề này thì chia sẻ với mình ở dưới phần comment nhé!

Happy styling