Tại sao Smooth Scroll bằng JavaScript đang phá nát trải nghiệm người dùng?

Mở bất kỳ theme WordPress cao cấp nào, đều thấy smooth scroll được quảng cáo như một tính năng đáng tự hào. Nhiều developer nghĩ rằng override scroll behavior mặc định của browser bằng JavaScript sẽ làm website trông chuyên nghiệp hơn. Nhưng thực tế, những script như SmoothScroll.js hay Locomotive Scroll đang tạo ra nhiều vấn đề hơn là giải quyết. Đây là lý do tại sao.

Tại sao Smooth Scroll bằng JavaScript đang phá nát trải nghiệm người dùng?

1. Reinventing The Wheel: Khi developer nghĩ mình làm tốt hơn trình duyệt

Nhìn vào source code của SmoothScroll.js, ta thấy hơn 800 dòng code chỉ để làm một việc: can thiệp vào scroll behavior tự nhiên của browser. Script lắng nghe wheel event, tính toán delta, xử lý acceleration, implement custom easing function, và cuối cùng dùng requestAnimationFrame để animate scroll từng frame một.

Vấn đề là: đội ngũ các trình duyệt đã làm việc hàng chục năm để tối ưu scroll behavior. Chrome có compositor thread riêng để handle scroll mượt mà ngay cả khi main thread bị block. Firefox có APZ (Asynchronous Pan/Zoom) cho phép scroll 60 FPS ổn định. Safari optimize scroll cho trackpad với momentum và rubber banding tự nhiên. Edge kế thừa engine Chromium với mọi ưu điểm về performance.

Tất cả những optimization này đều bị vứt đi khi bạn dùng JavaScript override scroll. Giờ mọi thứ phải chạy qua main thread, bị ảnh hưởng bởi garbage collection, bị block bởi heavy JavaScript khác, và không còn được hardware acceleration nữa. Bạn đang đổi một implementation đã được optimize cực kỳ kỹ lưỡng lấy 800 dòng JavaScript viết trong vài tuần.

2. Performance: Smooth Script làm Scroll không Smooth

SmoothScroll.js có frameRate mặc định là 150 Hz và animationTime là 400ms. Điều này có nghĩa là mỗi lần scroll, script phải chạy animation trong 400ms với khoảng 60 frame updates. Mỗi frame phải tính toán position mới, apply easing function, update scrollTop/scrollLeft, và schedule frame tiếp theo.

Trên desktop với CPU mạnh, điều này có thể chạy được. Nhưng trên mobile, đặc biệt là Android tầm trung, việc chạy JavaScript liên tục mỗi frame sẽ làm giảm frame rate xuống 20-30 FPS, tạo cảm giác giật lag rõ rệt. Script cố gắng làm scroll smooth lại làm nó janky.

Tệ hơn nữa, script này còn có pulse algorithm, acceleration logic, và cả một hệ thống cache phức tạp để track overflowing ancestors. Tất cả đều chạy trên main thread, cạnh tranh CPU với rendering, layout calculation, và JavaScript khác. Kết quả là scroll performance tệ hơn nhiều so với native browser scroll.

3. User Settings: Bị phớt lờ hoàn toàn

Trên Windows, người dùng có thể điều chỉnh scroll wheel settings trong Mouse Properties: một lần kéo chuột scroll bao nhiêu dòng. Có người thích scroll nhanh set 5-6 dòng, có người thích scroll chậm set 1-2 dòng. Đây là preference cá nhân mà họ đã cài đặt và quen thuộc trên toàn bộ hệ thống.

Smooth scroll JavaScript hoàn toàn ignore setting này. Script có stepSize cố định là 100px và arrowScroll là 50px, không quan tâm người dùng đã config như thế nào. Kết quả là scroll behavior trên website của bạn khác hoàn toàn so với mọi website khác và mọi ứng dụng khác trên máy họ. Người dùng scroll như bình thường nhưng tốc độ sai, momentum sai, cảm giác sai.

Trackpad trên MacBook có momentum scrolling và elastic overscroll bounce rất tự nhiên. Safari và Chrome đã optimize kỹ cho behavior này. Nhưng SmoothScroll.js với logic custom acceleration và pulse algorithm của nó sẽ override tất cả, làm scroll trở nên sluggish và không responsive với input gesture tự nhiên của người dùng.

4. Accessibility: Vấn đề bị lãng quên

Script này hoàn toàn không check prefers-reduced-motion. Người dùng có vestibular disorders bật setting này để tắt animation, nhưng SmoothScroll.js vẫn cứ chạy smooth animation bình thường. Họ sẽ bị chóng mặt, buồn nôn khi scroll trang của bạn.

Keyboard navigation cũng bị ảnh hưởng. Script override Page Up, Page Down, Space, Home, End keys với animation 400ms. Người dùng muốn jump nhanh xuống cuối trang bằng End key nhưng phải ngồi xem animation chạy. Họ muốn scroll từng đoạn bằng Space nhưng phải đợi animation xong mới tiếp tục được. Mọi thứ trở nên chậm chạp một cách không cần thiết.

5. Bugs và Edge Cases không hồi kết

Nhìn vào code, ta thấy đầy những special case handling: check Safari version cũ, fix cho IE Win7, handle YouTube embed, detect touchpad vs mouse wheel, workaround cho iframe. Đây chỉ là những bug đã được phát hiện và fix. Còn bao nhiêu edge case khác chưa gặp?

Script phải override scroll-behavior: smooth của CSS về auto để implement custom logic, rồi restore lại sau khi animation xong. Phải tạo dummy div để detect scrollRoot. Phải dùng MutationObserver để track DOM changes. Tất cả những hack này là dấu hiệu của việc cố ép một behavior không tự nhiên vào platform.

Back/Forward button trong browser cũng bị ảnh hưởng. Browser muốn restore scroll position ngay lập tức để người dùng thấy đúng chỗ họ đã xem, nhưng smooth scroll script bắt họ phải xem animation scroll từ top xuống. Một chi tiết nhỏ nhưng phá vỡ UX pattern mà người dùng đã quen thuộc suốt nhiều năm.

6. Scroll Anchoring: Tính năng thông minh bị phá hỏng

Các trình duyệt hiện đại đều có Scroll Anchoring: khi nội dung phía trên thay đổi kích thước (ví dụ ảnh lazy load xong, quảng cáo inject vào DOM), trình duyệt sẽ tự động giữ nguyên vị trí đọc hiện tại. Người dùng không bị “nhảy trang” bất ngờ.

Đây là một cơ chế rất tinh tế và cực kỳ quan trọng với UX, đặc biệt trên mobile hoặc trang có nhiều hình ảnh. Nhưng khi bạn override scroll bằng JavaScript và tự điều khiển scrollTop thủ công, browser không còn kiểm soát được nữa. Kết quả là trang có thể giật lên, giật xuống hoặc teleport sang vị trí khác khi layout thay đổi.

Một tính năng miễn phí, thông minh và đã được tối ưu sẵn bị vô hiệu hóa chỉ vì một hiệu ứng animation không cần thiết.

7. Passive Event Listeners: Tự tay bóp nghẹt hiệu năng

Để scroll mượt, các trình duyệt hiện đại mặc định coi wheel, touchstart, touchmove là passive events. Điều này cho phép trình duyệt cuộn ngay lập tức mà không cần chờ JavaScript xử lý xong. Đây là lý do native scroll có cảm giác “dính tay” và phản hồi tức thì.

Nhưng để chặn hành vi mặc định và implement smooth scroll thủ công, script buộc phải đăng ký event với passive: false và gọi preventDefault(). Điều này ép trình duyệt phải đợi JavaScript quyết định trước khi cuộn.

Chỉ riêng hành động đó đã đủ tạo thêm latency cho mỗi lần người dùng chạm hoặc lăn chuột. Bạn cố làm “smooth”, nhưng lại tự thêm delay vào pipeline input. Nghe đã thấy sai từ gốc.

8. So sánh trực quan: Native vs JavaScript Smooth Scroll

Nếu bỏ hết tranh luận cảm tính và chỉ nhìn thuần kỹ thuật, sự khác biệt gần như là một chiều. Một bên là engine của trình duyệt được tối ưu suốt hàng chục năm bởi hàng nghìn engineer. Bên còn lại là vài trăm dòng JavaScript chạy trên main thread. Kết quả gần như đã được định đoạt từ trước khi bạn viết dòng code đầu tiên.

Đặc điểm Native Scroll (Trình duyệt) Smooth Scroll (JavaScript)
Luồng xử lý Compositor Thread (tách biệt, async) Main Thread (dễ bị block)
Tiêu thụ CPU Cực thấp, hardware accelerated Cao, tính toán mỗi frame
Độ trễ (Latency) Gần như zero delay Có input lag do animation
User Settings Tôn trọng 100% cấu hình hệ thống Bị ghi đè bởi giá trị hardcoded
Accessibility Hỗ trợ mặc định (reduced motion, keyboard, OS integration) Thường bị bỏ sót hoặc phải tự implement lại
Bundle Size 0 KB + vài chục KB JavaScript
Độ ổn định Được browser guarantee Đầy edge case và hack workaround

Nhìn bảng trên là đủ hiểu: đây không phải trade-off. Đây là downgrade. Bạn đang đổi một hệ thống native tối ưu sang một bản mô phỏng chậm hơn, nặng hơn và dễ vỡ hơn.

9. CSS đã có sẵn, tại sao phải dùng JavaScript?

Nếu thực sự muốn smooth scroll cho anchor links, CSS có scroll-behavior: smooth. Một dòng code, không cần JavaScript, không có bundle size, hoạt động trên compositor thread, respect prefers-reduced-motion tự động, và được browser optimize kỹ lưỡng.

Nhưng phần lớn trường hợp, ngay cả CSS smooth scroll cũng không cần thiết. Native scroll behavior của browser đã rất tốt. Nó nhanh, responsive, consistent với mọi website và app khác, respect user settings, và không có bug. Tại sao phải thay đổi một thứ đã hoạt động tốt?

10. Developer Ego vs User Experience

Nhiều developer thêm smooth scroll vì nghĩ nó làm website trông “premium” hơn. Họ test trên MacBook Pro với trackpad mượt mà và nghĩ wow hiệu ứng này đẹp quá. Nhưng họ không test trên Android tầm trung với scroll giật lag. Không test với người dùng bật reduced motion. Không test với người dùng đã custom scroll settings trên Windows.

Kết quả là một tính năng được thêm vào vì developer thích, không phải vì user cần. Một tính năng làm giảm performance, phá vỡ accessibility, ignore user preferences, và tạo ra inconsistent behavior so với toàn bộ web platform. Tất cả chỉ để có được một hiệu ứng animation mà chỉ developer mới để ý.

Kết luận

Smooth scroll JavaScript như SmoothScroll.js là ví dụ điển hình của over-engineering. Hơn 800 dòng code để làm lại một thứ mà browser đã làm tốt hơn. Performance tệ hơn, accessibility tệ hơn, user experience tệ hơn, tất cả chỉ vì developer nghĩ mình biết hơn những người đã dành cả sự nghiệp để optimize scroll behavior.

Đội ngũ Chrome, Firefox, Safari, Edge đã làm việc hàng chục năm với hàng nghìn engineer để tạo ra scroll behavior mượt mà, nhanh, và respect user preferences. Họ có hardware acceleration, compositor thread, advanced heuristics để detect touchpad vs mouse, integration với OS-level scroll settings. Tất cả những thứ này bị vứt đi khi bạn dùng JavaScript override.

Người dùng không cần smooth scroll JavaScript. Họ cần scroll nhanh, responsive, consistent, và respect cài đặt họ đã chọn. Native browser scroll cho họ tất cả những điều đó. Smooth scroll JavaScript thì không.

Bình luận


  • Không có bình luận.

Init Toolbox

Nhấn Ctrl + \ trên máy tính, hoặc vuốt sang trái ở bất kỳ đâu trên mobile.

Đăng nhập





Đang tải...