Hộp thoại

Phần tử hộp thoại là một phần tử hữu ích để biểu thị mọi loại hộp thoại trong HTML, hãy tìm hiểu cách phần tử này hoạt động.

Hộp thoại phương thức là một loại hộp bật lên đặc biệt trên trang web, làm gián đoạn người dùng để tập trung vào chính nó. Có một số trường hợp sử dụng hợp lệ để bật hộp thoại, nhưng bạn nên cân nhắc kỹ lưỡng trước khi làm như vậy. Hộp thoại phương thức buộc người dùng tập trung vào nội dung cụ thể và tạm thời bỏ qua phần còn lại của trang.

Hộp thoại có thể là hộp thoại phương thức (chỉ có thể tương tác với nội dung trong hộp thoại) hoặc hộp thoại không phương thức (vẫn có thể tương tác với nội dung bên ngoài hộp thoại). Hộp thoại phương thức xuất hiện ở trên cùng của nội dung còn lại trên trang. Phần còn lại của trang ở trạng thái trơ và theo mặc định, bị che khuất bởi một phông nền bán trong suốt.

Phần tử HTML ngữ nghĩa <dialog> để tạo hộp thoại đi kèm với ngữ nghĩa, hoạt động tương tác bằng bàn phím và tất cả các thuộc tính cũng như phương thức của giao diện HTMLDialogElement.

Sau đây là ví dụ về một phương thức <dialog>. Mở hộp thoại bằng nút "Mở hộp thoại phương thức". Sau khi mở, có 3 cách để đóng hộp thoại: nhấn phím thoát, gửi biểu mẫu bằng nút có thiết lập formmethod="dialog" (hoặc nếu chính biểu mẫu có thiết lập method="dialog") và phương thức HTMLDialogElement.close().

HTMLDialogElement có 3 phương thức chính, cùng với tất cả các phương thức được kế thừa từ HTMLElement.

dialog.show() /* opens the dialog */
dialog.showModal() /* opens the dialog as a modal */
dialog.close() /* closes the dialog */

<dialog> này được mở bằng phương thức HTMLDialogElement.showModal(), nên đây là một hộp thoại phương thức. Việc mở một hộp thoại phương thức sẽ vô hiệu hoá và che khuất mọi thứ khác ngoài hộp thoại đó. Nếu di chuột lên giao diện người dùng bên ngoài hộp thoại, bạn sẽ thấy tất cả các phần tử đều hoạt động như thể pointer-events: none; đã được đặt; ngay cả nút mở hộp thoại cũng không phản ứng với các hoạt động tương tác.

Khi hộp thoại mở ra, tiêu điểm sẽ di chuyển vào hộp thoại. Tiêu điểm được đặt vào phần tử đầu tiên theo thứ tự điều hướng tuần tự bằng bàn phím trong hộp thoại đó. Nếu bạn nhấn phím tab nhiều lần, chỉ nội dung trong hộp thoại mới có thể nhận tiêu điểm trong khi hộp thoại phương thức đang mở. Mọi thứ bên ngoài hộp thoại phương thức đều không hoạt động miễn là hộp thoại đang mở.

Khi một hộp thoại bị đóng, dù là hộp thoại phương thức hay không, tiêu điểm sẽ được trả về cho phần tử đã mở hộp thoại đó. Tránh mở hộp thoại theo cách lập trình mà không có hành động của người dùng. Nếu bạn phải làm vậy, hãy đảm bảo rằng tiêu điểm được đặt lại về vị trí trước khi hộp thoại mở ra, đặc biệt là nếu người dùng đóng hộp thoại mà không tương tác với hộp thoại đó.

Có một thuộc tính inert chung mà bạn có thể dùng để tắt một phần tử và tất cả các phần tử con của phần tử đó, trừ mọi hộp thoại đang hoạt động. Khi một hộp thoại phương thức được mở bằng showModal(), trạng thái không hoạt động hoặc trạng thái huỷ kích hoạt sẽ đi kèm với hộp thoại đó; thuộc tính này không được đặt một cách rõ ràng.

Bạn có thể tạo kiểu cho phông nền che khuất mọi thứ khác ngoài hộp thoại bằng cách sử dụng phần tử giả ::backdrop. Phông nền chỉ xuất hiện khi <dialog> xuất hiện bằng phương thức .showModal(). Phần tử giả này khớp với tất cả các phông nền, kể cả phông nền xuất hiện khi dùng FullScreen API, chẳng hạn như khi xem video ở chế độ toàn màn hình nhưng không có cùng tỷ lệ khung hình với màn hình hoặc màn hình máy tính.

Hộp thoại không theo phương thức

Tương tự, HTMLDialogElement.show() sẽ mở một hộp thoại nhưng không thêm phông nền hoặc khiến bất kỳ thành phần nào trở nên không hoạt động. Phím Escape không đóng các hộp thoại không theo phương thức. Do đó, bạn càng phải thêm một phương thức đóng hộp thoại không theo phương thức. Khi làm như vậy, nếu nút đóng nằm bên ngoài hộp thoại, hãy nhận ra rằng tiêu điểm sẽ chuyển đến phần tử đã mở hộp thoại. Điều này có thể không mang lại trải nghiệm tốt nhất cho người dùng.

Mặc dù thông số kỹ thuật không chính thức yêu cầu phải có nút đóng hộp thoại, nhưng hãy coi đó là yêu cầu bắt buộc. Phím thoát sẽ đóng hộp thoại phương thức, nhưng không đóng hộp thoại không phải phương thức. Một nút có thể nhìn thấy và nhận được tiêu điểm sẽ cải thiện khả năng hỗ trợ tiếp cận và trải nghiệm người dùng.

Đóng hộp thoại

Bạn không cần phương thức HTMLDialogElement.close() để đóng hộp thoại. Bạn không cần JavaScript. Để đóng <dialog> mà không cần JavaScript, hãy thêm một biểu mẫu có phương thức hộp thoại bằng cách đặt method="dialog" trên <form> hoặc formmethod="dialog" trên nút.

Khi người dùng gửi bằng phương thức dialog, trạng thái của dữ liệu do người dùng nhập sẽ được duy trì. Mặc dù có một sự kiện gửi – biểu mẫu trải qua quá trình xác thực ràng buộc (trừ phi novalidate được đặt) – nhưng dữ liệu người dùng không bị xoá cũng không được gửi. Bạn có thể viết nút đóng không có JavaScript như sau:

<dialog open>
  <form method="dialog">
    <button type="submit" autofocus>close</button>
  </form>
</dialog>

Bạn có thể nhận thấy thuộc tính autofocus được đặt trên <button> đóng trong ví dụ này. Các phần tử có thuộc tính autofocus được đặt trong <dialog> sẽ không nhận được tiêu điểm khi tải trang (trừ phi trang được tải khi hộp thoại hiển thị). Tuy nhiên, các nút này sẽ được lấy tiêu điểm khi hộp thoại mở ra.

Theo mặc định, khi một hộp thoại được mở, phần tử đầu tiên có thể lấy tiêu điểm trong hộp thoại sẽ nhận được tiêu điểm, trừ phi một phần tử khác trong hộp thoại có thuộc tính autofocus được đặt. Việc đặt thuộc tính autofocus trên nút đóng sẽ đảm bảo nút này nhận được tiêu điểm khi hộp thoại mở ra. Tuy nhiên, bạn chỉ nên đưa autofocus vào trong <dialog> sau khi cân nhắc kỹ lưỡng. Tất cả các phần tử trong chuỗi xuất hiện trước phần tử được tự động lấy tiêu điểm đều bị bỏ qua. Chúng tôi thảo luận thêm về thuộc tính này trong bài học về tiêu điểm.

Giao diện HTMLDialogElement bao gồm một thuộc tính returnValue. Việc gửi biểu mẫu bằng method="dialog" sẽ đặt returnValue thành name (nếu có) của nút gửi dùng để gửi biểu mẫu. Nếu chúng ta viết <button type="submit" name="toasty">close</button>, thì returnValue sẽ là toasty.

Khi một hộp thoại được mở, thuộc tính boolean open sẽ xuất hiện, tức là hộp thoại đang hoạt động và có thể tương tác. Khi một hộp thoại được mở bằng cách thêm thuộc tính open thay vì bằng .show() hoặc .showModal(), hộp thoại sẽ không có phương thức. Thuộc tính HTMLDialogElement.open trả về true hoặc false, tuỳ thuộc vào việc hộp thoại có sẵn để tương tác hay không, chứ không phải là hộp thoại có phương thức hay không.

Mặc dù JavaScript là phương thức ưu tiên để mở hộp thoại, nhưng việc thêm thuộc tính open khi tải trang, rồi xoá thuộc tính đó bằng .close() có thể giúp đảm bảo hộp thoại luôn có sẵn ngay cả khi JavaScript không hoạt động.

Thông tin chi tiết khác

Không sử dụng tabindex

Phần tử được kích hoạt để mở hộp thoại và nút đóng có trong hộp thoại đó (và có thể là nội dung khác) có thể nhận tiêu điểm và có tính tương tác. Phần tử <dialog> không tương tác và không nhận tiêu điểm. Đừng thêm thuộc tính tabindex vào chính hộp thoại.

Vai trò ARIA

Vai trò ngầm định là dialog. Nếu hộp thoại là một cửa sổ xác nhận truyền đạt một thông báo quan trọng yêu cầu người dùng xác nhận hoặc phản hồi theo cách khác, hãy đặt role="alertdialog". Hộp thoại cũng phải có tên có thể truy cập. Nếu văn bản hiển thị có thể cung cấp tên hỗ trợ tiếp cận, hãy thêm aria-labelledby="idOfLabelingText".

CSS mặc định

Xin lưu ý rằng các trình duyệt cung cấp kiểu mặc định cho dialog. Firefox, Chrome và Edge đặt color: CanvasText; background-color: Canvas; và Safari đặt color: black; background-color: white; trong biểu định kiểu tác nhân người dùng. color được kế thừa từ dialog chứ không phải từ body hoặc :root, điều này có thể không như mong đợi. Thuộc tính background-color không được kế thừa.

Kiểm tra mức độ hiểu biết của bạn

Kiểm tra kiến thức của bạn về phần tử hộp thoại.

Làm cách nào để tạo kiểu cho vùng phía sau hộp thoại?

Với phần tử giả ::background.
Hãy thử lại.
Với phần tử giả ::backdrop.
Chính xác!
Với thuộc tính background.
Hãy thử lại.