7 điểm bởi GN⁺ 2025-04-05 | 1 bình luận | Chia sẻ qua WhatsApp
  • Liên kết neo có cấu trúc đơn giản là nhấp nút → cuộn đến tiêu đề, nhưng khi triển khai thực tế lại phát sinh vấn đề
  • Các tiêu đề nằm ở phía cuối không cuộn chính xác lên đỉnh viewport, từ đó làm giảm UX
  • Để giải quyết điều này, nhiều cách tiếp cận đã được thử nghiệm và dần phát triển thành những phương pháp ngày càng tinh vi và phức tạp hơn

Giải pháp đơn giản: thêm padding

  • Cách làm là thêm khoảng trống để các tiêu đề ở cuối có thể được bắt khi cuộn
  • Có thể giải quyết bằng cách tính delta rồi thêm padding
  • Nhưng đội thiết kế có thể không thích khoảng trắng không cần thiết

Giải pháp thực dụng: di chuyển đường kích hoạt

  • Di chuyển đường kích hoạt xuống gần đáy viewport để tiêu đề ở cuối có thể chạm tới
  • Vấn đề là tiêu đề sẽ nằm sát mép dưới viewport nên khả năng đọc giảm đi

Phương án cải tiến: tạo điểm kích hoạt ảo

  • Giữ nguyên vị trí tiêu đề thực tế, chỉ tạo một vị trí ảo đã được đẩy lên trên cho nơi phát sinh kích hoạt
  • Đảm bảo tính linh hoạt để áp dụng các mức điều chỉnh khác nhau cho từng tiêu đề
  • Nhưng tiêu đề đầu tiên lại bị đẩy lên quá cao, tạo ra vấn đề mới → cần điều chỉnh riêng lẻ

Cách tốt hơn: di chuyển vị trí kích hoạt theo tỷ lệ

  • Không di chuyển mọi trigger giống nhau, mà giữ nguyên tiêu đề đầu tiên, còn tiêu đề cuối cùng được di chuyển tối đa
  • Các tiêu đề ở giữa sẽ dịch chuyển theo tỷ lệ tùy theo vị trí
  • Đáp ứng các điều kiện giữ nguyên thứ tự tiêu đềđảm bảo có thể cuộn tới được
  • Cách này đơn giản và thực dụng, hoạt động phù hợp trong đa số trường hợp

Cách tiếp cận nâng cao: tối ưu hóa bằng hàm ánh xạ tùy chỉnh

  • Vì vị trí trigger được đặt tùy ý ở mức 25%, nên vị trí ảo có thể lệch quá xa so với vị trí gốc
  • Để giải quyết, tác giả đưa vào cách tiếp cận tối ưu hóa sử dụng MSE (Mean Squared Error)

Cấu thành hàm mất mát

  • Anchor Penalty: mức độ vị trí tiêu đề ảo lệch khỏi vị trí gốc
  • Section Penalty: mức độ thay đổi khoảng cách giữa các section (độ dài cuộn)
  • Điều chỉnh trọng số của hai giá trị này để suy ra vị trí trigger tối ưu

Các điều kiện ràng buộc

  • Duy trì trong phạm vi trang
  • Tiêu đề đầu tiên không bị đẩy lên trên
  • Bảo toàn thứ tự tiêu đề

Insight: giới hạn của việc dịch chuyển theo tỷ lệ đơn giản

  • Với những trang rất dài (ví dụ toàn bộ Kinh Thánh), sẽ phát sinh sự kém hiệu quả vì phải tích lũy các dịch chuyển nhỏ trên toàn bộ trang
  • Trang càng dài thì sai số càng lớn và có thể ảnh hưởng xấu đến UX

Giải pháp cuối cùng: hàm ánh xạ biến thiên dựa trên smoothstep

  • Chuẩn hóa vị trí của từng tiêu đề thành giá trị từ 0 đến 1, rồi dùng đó để tính tỷ lệ điều chỉnh
  • Sử dụng hàm Smoothstep (S(x) = 3x² - 2x³) để tạo chuyển tiếp mượt mà
  • Đặt vị trí bắt đầu điều chỉnh a để không dịch chuyển trước một ngưỡng nhất định, sau đó tăng dần một cách mượt mà
    • Ví dụ: nếu a = 0.4 thì 40% tiêu đề phía trên sẽ không dịch chuyển, còn 60% phía dưới sẽ được điều chỉnh dần dần
  • Kết quả là tiêu đề phía trên giữ nguyên vị trí gốc, còn tiêu đề phía dưới nhận mức điều chỉnh tối đa → mang lại UX tự nhiên

Kiểm chứng và kết luận

  • Cách triển khai cuối cùng là một giải pháp cân bằng giữa độ tinh vi trong thiết kếtính thực dụng
  • Tất nhiên, phản hồi từ phía designer có thể chỉ là: “...miễn là nó hoạt động tốt là được”
  • Nhưng ít nhất, bài blog này sẽ còn được nhớ mãi như một bản ghi chép về kỹ thuật được trau chuốt đến mức cực kỳ tinh vi

1 bình luận

 
GN⁺ 2025-04-05
Ý kiến trên Hacker News
  • Với tư cách là một lập trình viên backend, đôi khi thấy ngạc nhiên trước mức độ phức tạp khi nhìn vào công việc frontend

    • Đây là một bài viết hay và được làm tốt, nhưng vẫn tự hỏi liệu có cần đưa nhiều độ phức tạp như vậy vào một thao tác cuộn đơn giản hay không
  • Đặt câu hỏi về mục đích UX của việc hiển thị "anchor đang hoạt động" trong điều hướng bên cạnh

    • Khi người đọc ở giữa một phần dài, nó có thể nhắc họ đang ở phần nào thay cho tiêu đề hiện không còn trên màn hình
    • Điều này có nghĩa là nó hoạt động dựa trên phần đang hiển thị trên màn hình, chứ không phải tiêu đề đã được cuộn tới
    • Nếu một phần nhỏ không chiếm phần lớn màn hình, chỉ báo đang hoạt động có thể không hữu ích
  • Chức năng UX quan trọng nhất của anchor link là phải có thể gửi cho người khác và lưu bookmark

    • Khả năng bookmark một phần cụ thể tiện hơn nhiều so với việc bắt đầu từ đầu trang rồi cuộn xuống hoặc nhấp vào anchor link
    • Trang web này không dùng URL #anchor-name nên không cung cấp được tính năng đó
  • Đã nhấp vào vì khó chịu với anchor/permalink của Jira, nhưng đây là cách làm tương tự mà vẫn khác

    • Không thể dùng bàn phím để di chuyển tới anchor
    • Câu hỏi cho tác giả: tại sao lại dùng JS event listener trên phần tử không tương tác thay vì phần tử HTML ``
  • Lý tưởng nhất là thêm padding dưới nội dung của trang chính

    • Giải quyết vấn đề phần cuối nội dung bị dính vào đáy viewport
    • Trên di động, margin 90vh là phù hợp; với màn hình lớn hơn, 50vh là hợp lý
    • Trên desktop, margin 90vh có thể trông kỳ cục
  • Trong các trình duyệt hiện đại, có thể dùng text fragment để làm nổi bật một phần cụ thể của trang

    • Trên Chrome, chỉ cần bôi đen văn bản, nhấp chuột phải và chọn "sao chép liên kết"
    • Dùng hằng ngày để làm nổi bật một đoạn văn bản cụ thể thay vì dùng anchor
  • Cũng có thể cho phép nhiều trạng thái "đang hoạt động"

    • Nếu nội dung dài, tiêu đề của hai phần đều có thể ở trạng thái "đang hoạt động"
    • Với nội dung ngắn, có thể khiến quá nhiều phần bị nhấn mạnh
  • Đọc các bình luận khác cũng khá thú vị

    • Trên di động, thiết kế trang thú vị và cách giải quyết vấn đề được truyền đạt rõ ràng
    • Thật mới mẻ khi đọc một blog nói về nội dung kỹ thuật mà không có popup
  • Trên Firefox desktop, "giải pháp đẹp" làm nổi bật "phần ở giữa"

    • Kết luận hiện ra đầy đủ ngay cả khi chưa cuộn tới cuối trang
    • Có lẽ câu trả lời là làm nổi bật mọi anchor đang hiển thị trên màn hình
  • Bài viết gọn gàng và thiết kế blog còn thú vị hơn

    • Không thích cách căn phải, nhưng việc kích hoạt inline ở popup bên trái rất ngầu