11 điểm bởi GN⁺ 2026-03-11 | Chưa có bình luận nào. | Chia sẻ qua WhatsApp
  • Popover API thay thế bằng tính năng native của trình duyệt những phần vốn bắt buộc phải tự làm trong triển khai tooltip trước đây như JavaScript event listener, quản lý trạng thái và đồng bộ thủ công các thuộc tính ARIA
  • Chỉ với thuộc tính popoverpopovertarget, việc mở/đóng, xử lý phím Escđiều hướng bằng bàn phím sẽ tự động hoạt động
  • Tăng tính dễ dự đoán cho screen reader, tự động đồng bộ aria-expanded, khôi phục focus... nhờ đó loại bỏ hẳn cả một nhóm lỗi liên quan đến accessibility
  • Ở một số phần như điều khiển thời điểm hiển thị hay xác định hover intent, vẫn cần JavaScript, nhưng mô hình tương tác cốt lõi do trình duyệt đảm nhiệm
  • Với các design system quy mô lớn hoặc trường hợp cần định vị phức tạp, thư viện vẫn còn giá trị, nhưng mặc định đang chuyển sang native API

Vấn đề của tooltip truyền thống

  • Trước Popover API, trình duyệt không có khái niệm tooltip native hoạt động xuyên suốt với chuột, bàn phím và công nghệ hỗ trợ
  • Cùng một mẫu lặp đi lặp lại: phần tử trigger, phần tử tooltip ẩn và JavaScript để điều phối cả hai
  • Bề ngoài có vẻ đơn giản, nhưng khi triển khai cho người dùng thực tế thì lộ ra nhiều vấn đề
    • Người dùng bàn phím Tab vào trigger nhưng tooltip không hiện
    • Screen reader đọc hai lần hoặc không đọc gì cả
    • Di chuyển chuột quá nhanh gây nhấp nháy (flicker)
    • Nội dung chồng lấn trên màn hình nhỏ
    • Không thể đóng bằng phím Esc, bị mất focus
  • Càng về sau code càng phình to vì tích lũy event listener, xử lý riêng cho hover/focus, các trường hợp đặc biệt khi click bên ngoài, đồng bộ thủ công thuộc tính ARIA...

Vì sao phải dùng thư viện

  • Thư viện đảm nhiệm các công việc thực tế như định vị, lật hướng ở ranh giới viewport, điều phối sự kiện theo từng loại input, nhận biết cuộn trong layout phức tạp
  • Chỉ riêng việc định vị cũng đủ để biện minh cho dependency vì xử lý scroll container, transform và responsive layout rất phức tạp
  • Vấn đề thật sự nằm ở hành vi accessibility
    • Tooltip hiện chậm hoặc không hiện
    • Tab nhanh khiến tooltip bị bỏ qua
    • Việc đóng bằng Esc không ổn định
  • Người dùng chuột kỳ vọng tính tức thời, còn người dùng bàn phím kỳ vọng tính dễ dự đoán, nên khi hỗ trợ cả hai sẽ phát sinh độ trễ và nhiều edge case
  • Trên screen reader, tooltip có thể được đọc, không được đọc hoặc bị đọc hai lần, dẫn đến hành vi thiếu nhất quán
  • Chỉ cần sót một bước khi cập nhật thủ công thuộc tính ARIA là có thể gây rối cho cây accessibility hoặc tạo trạng thái không hiển thị

Vấn đề không nằm ở code mà ở giới hạn của nền tảng

  • Việc triển khai đã được kiểm thử và thư viện cũng rất chắc chắn, nhưng vấn đề cốt lõi là web platform thiếu affordance phù hợp
  • Trình duyệt không có cách nào để biết phần tử đó là tooltip; mọi thứ đều dựa trên quy ước (convention) như phần tử chung chung, event listener, ARIA thủ công, logic đóng tự tạo...
  • Theo thời gian, những thay đổi nhỏ cũng trở nên rủi ro và các chỉnh sửa vụn vặt dễ gây regression, tạo thành một cấu trúc mong manh
  • Mỗi khi thêm tooltip mới, toàn bộ độ phức tạp đó lại được kế thừa nguyên vẹn

Lần đầu áp dụng Popover API

  • Động lực chuyển đổi không phải vì muốn thử nghiệm cái mới, mà vì đã mệt mỏi với việc bảo trì hành vi tooltip vốn lẽ ra trình duyệt phải hiểu sẵn
  • Bắt đầu từ dạng nhỏ nhất: <button popovertarget="tip-1"> + <div id="tip-1" popover="manual" role="tooltip">
  • Không cần event listener, không cần theo dõi trạng thái, không cần cập nhật ARIA trong JavaScript
  • Khi button được focus thì tooltip hiện ra, nhấn Esc thì biến mất

Khác biệt cảm nhận được ngay

  • Mở/đóng không cần JavaScript: trình duyệt xử lý việc gọi hiển thị chỉ bằng HTML, và mối quan hệ giữa trigger với tooltip được khai báo tường minh
  • Phím Esc hoạt động tự động: không cần thêm key listener vì trình duyệt tự hiểu khả năng đóng popover
  • Tự động đồng bộ trạng thái ARIA: thuộc tính aria-expanded được tự cập nhật khi popover mở/đóng, không cần quản lý thủ công, loại bỏ nguy cơ trạng thái cũ
  • Quan trọng hơn cả việc giảm code là sự chuyển giao trách nhiệm: trước đây JavaScript phải “tạo ra” tooltip, còn giờ trình duyệt hiểu vai trò của nó trong markup và để nó tham gia vào mô hình focus, cây accessibility và quy tắc đóng native

Hiểu về Invoker Commands

  • popovertarget="id" liên kết button với phần tử popover
  • popovertargetaction dùng để chỉ định hành vi
    • show: chỉ mở
    • hide: chỉ đóng
    • toggle (mặc định): đang mở thì đóng, đang đóng thì mở
  • Có thể có nhiều trigger cho cùng một tooltip, và không cần JavaScript cho tương tác cơ bản

Lợi ích accessibility có được gần như miễn phí

  • Bàn phím “cứ thế mà chạy”

    • Trước đây đây là một cấu trúc nhiều tầng: focus phải kích hoạt, blur phải ẩn, Esc phải tự nối logic và thời điểm cũng phải căn đúng
    • Khi đặt thuộc tính popover (auto hoặc manual), trình duyệt xử lý mặc định: Tab/Shift+Tab hoạt động bình thường, Esc luôn đóng một cách chắc chắn
    • Có thể loại bỏ khỏi codebase các global keydown handler, logic dọn dẹp riêng cho Esc và kiểm tra trạng thái trong lúc điều hướng bàn phím
  • Tính dễ dự đoán cho screen reader

    • Đây là phần cải thiện lớn nhất; trước kia hành vi vẫn thay đổi dù đã cẩn thận với ARIA, và chỉ một thay đổi nhỏ cũng thành rủi ro
    • Với popover="manual" role="tooltip", hành vi trở nên ổn định và dễ dự đoán hơn nhiều
    • Sau khi chuyển đổi, Lighthouse không còn cảnh báo sai trạng thái ARIA nữa — vì đơn giản là không còn trạng thái ARIA tùy biến để mắc lỗi
  • Quản lý focus

    • Trước đây phải xử lý các quy tắc phức tạp như hiện tooltip khi trigger được focus, không đóng khi focus đi vào tooltip, xử lý blur, khôi phục focus thủ công...
    • Với Popover API, focus di chuyển vào popover một cách tự nhiên, và khi đóng thì tự động trả focus về trigger
    • Không phải thêm code khôi phục focus, mà là xóa nó đi

Những phần Popover API vẫn còn thiếu

  • Thời điểm hiển thị tooltip

    • Popover native mở và đóng ngay lập tức, nên nếu di chuyển chuột hơi nhanh hoặc chỉ lướt qua trigger thì tooltip có thể nhấp nháy và mang lại cảm giác thiếu ổn định
    • Vẫn cần điều khiển độ trễ giữa hover/focus và lúc mở
    • Hành vi mở/đóng mặc định do trình duyệt và HTML invoker commands đảm nhiệm, còn JavaScript chỉ dùng để cải thiện hành vi có chủ đích, như hủy ẩn khi con trỏ đi vào tooltip
    • CSS cũng đang được khám phá cho phần này; các công việc liên quan đến interest/invoker đang hướng tới việc cho phép biểu đạt trực tiếp độ trễ vào/ra bằng CSS
  • Hover intent và Invoker Commands

    • Trình duyệt không thể biết vì sao ai đó hover lên một phần tử — là có chủ đích hay chỉ đang lướt con trỏ qua, nên không thể tự suy đoán
    • Vì invoker commands đã xử lý phần mở/đóng cốt lõi, JavaScript không còn sở hữu toàn bộ mô hình tương tác nữa mà chỉ bổ sung intent ở phía trên
    • JavaScript chỉ còn dùng cho những hành vi mà trình duyệt không thể suy ra, như thêm độ trễ ngắn trước khi ẩn hoặc hủy đóng khi con trỏ di chuyển vào tooltip
  • Manual Popover và focus

    • Với popover="manual", khác với auto popover, trình duyệt không tự động khôi phục focus
    • Khi tooltip mở bằng focus và đóng bằng blur, cần trả focus về trigger một cách tường minh
    • Đây là ranh giới rõ ràng giữa hành vi của nền tảng và ý định của người dùng
  • Đánh giá thẳng thắn

    • Popover API không giải quyết tooltip một cách thần kỳ, nhưng nó giúp ngừng việc phải dựng lại một hạ tầng mong manh
    • Vẫn phải dùng JavaScript và vẫn phải nghĩ tới edge case, nhưng giờ có thể tập trung vào giải quyết bài toán sản phẩm thay vì tái tạo UI primitive

Khi nào vẫn cần thư viện tooltip

  • Design system lớn hoặc đã trưởng thành

    • Với design system quy mô lớn được nhiều đội dùng chung, thư viện vẫn hợp lý để có hành vi tập trung, pattern được tài liệu hóa và các mặc định nhất quán
    • Việc thay đổi mô hình tương tác nền không chỉ là quyết định kỹ thuật mà còn là quyết định ở cấp tổ chức
    • Nó cũng cung cấp guardrail cho những thành viên chưa quen với các sắc thái của accessibility
  • Yêu cầu định vị phức tạp

    • Nếu cần phát hiện va chạm giữa nhiều scroll container lồng nhau, logic lật hướng tùy biến hoặc kiểm soát tinh chỉnh offset/biên, các thư viện như Floating UI vẫn có lợi thế
    • CSS anchor positioning đang bắt đầu bao phủ phần lớn bài toán này — cho phép định vị tương đối theo trigger, nhận biết viewport và lật cạnh chỉ bằng CSS thuần
    • Đây vẫn là tính năng mới và còn các vấn đề đã biết, nhưng đã được đưa vào Interop nên có triển vọng được hỗ trợ đầy đủ và nhất quán giữa các trình duyệt
    • Nếu hiện tại cần hành vi cross-browser ổn định, thư viện vẫn là lựa chọn thực tế
  • Đội ngũ thiếu kinh nghiệm về accessibility

    • Với các đội thiếu kiến thức accessibility, một thư viện tốt đóng vai trò như lưới an toàn — không đảm bảo hoàn hảo, nhưng giúp tránh các lỗi phổ biến
    • Popover API mang lại mặc định tốt hơn, nhưng vẫn cần biết khi nào phải bổ sung role, label, quản lý focus và kiểm thử
    • Nếu không hiểu rõ, ngay cả công cụ native cũng có thể bị dùng sai

Kết luận

  • Nhờ Popover API, tooltip không còn là thứ phải mô phỏng nữa mà trở thành phần tử được trình duyệt hiểu
  • Việc mở, đóng, hành vi bàn phím, xử lý Esc và phần lớn accessibility được chính nền tảng cung cấp
  • Trong các design system phức tạp, nhu cầu tùy biến cao hay ràng buộc legacy, thư viện vẫn còn giá trị, nhưng mặc định đã thay đổi
  • Đây là lần đầu tiên tooltip đơn giản nhất cũng có thể là tooltip chính xác nhất
  • Hãy thử thay chỉ một tooltip trong sản phẩm bằng Popover API, bạn sẽ thấy điều gì biến mất khỏi code

Chưa có bình luận nào.

Chưa có bình luận nào.