- องค์ประกอบ HTML
<dialog> ใช้สร้างกล่องโต้ตอบแบบ modal และ non-modal แบบเนทีฟของเบราว์เซอร์ โดย modal จะทำให้พื้นที่ส่วนที่เหลือของหน้าอยู่ในสถานะ inert เพื่อบล็อกการโต้ตอบ
- วิธีเปิดและปิดพื้นฐานคือ
showModal(), show(), close() และยังสามารถควบคุมแบบ declarative ได้ด้วย <form method="dialog">, Invoker Commands API และ Popover API
- แอตทริบิวต์
closedby แบ่งช่องทางที่ผู้ใช้สามารถปิดกล่องโต้ตอบได้เป็น any, closerequest, none และพฤติกรรมเริ่มต้นจะแตกต่างกันตามวิธีที่เปิด
- ด้าน accessibility สิ่งสำคัญคือ โฟกัสเริ่มต้น และปุ่มปิดที่ระบุชัดเจน โดยกล่องโต้ตอบที่เปิดด้วย
showModal() จะให้โฟกัสกับองค์ประกอบที่โฟกัสได้ตัวแรก และปิดด้วยปุ่ม Esc ได้โดยค่าเริ่มต้น
- การจัดสไตล์ใช้
:modal, :open, ::backdrop และสำหรับ animation ต้องจัดการ discrete transition เช่น display, overlay, @starting-style, transition-behavior: allow-discrete
บทบาทของ <dialog> และพฤติกรรมแบบ modal
- องค์ประกอบ HTML
<dialog> คือองค์ประกอบสำหรับสร้างกล่องโต้ตอบแบบ modal และ non-modal
- กล่องโต้ตอบแบบ modal จะบล็อกการโต้ตอบกับองค์ประกอบ UI อื่น และทำให้ส่วนที่เหลือของหน้าอยู่ในสถานะ
inert
- กล่องโต้ตอบแบบ non-modal ยังสามารถโต้ตอบกับส่วนที่เหลือของหน้าได้ต่อไปแม้จะเปิดอยู่
สถานะเปิดและนโยบายการปิด
<dialog> รองรับแอตทริบิวต์สากล แต่ ไม่ควรใช้แอตทริบิวต์ tabindex
- ตัว
<dialog> เองไม่ใช่องค์ประกอบแบบโต้ตอบและไม่รับโฟกัส
- เนื้อหาภายในและปุ่มปิดสามารถรับโฟกัสและโต้ตอบได้
- แอตทริบิวต์
open ระบุว่ากล่องโต้ตอบอยู่ในสถานะ active และสามารถโต้ตอบได้
- หากไม่มีแอตทริบิวต์
open ผู้ใช้จะมองไม่เห็น
- เมื่อต้องการแสดงกล่องโต้ตอบ แนะนำให้ใช้
.show() หรือ .showModal() มากกว่าแอตทริบิวต์ open
<dialog> ที่เปิดด้วยแอตทริบิวต์ open จะเป็นแบบ non-modal
- สามารถเปลี่ยนสถานะเปิด·ปิดของกล่องโต้ตอบแบบ non-modal ด้วยการ toggle แอตทริบิวต์
open ได้ แต่ไม่แนะนำ
- แอตทริบิวต์
closedby กำหนดว่าผู้ใช้สามารถปิดกล่องโต้ตอบได้ด้วยวิธีใด
any: ปิดได้ทั้งสามวิธี
- light dismiss เช่น คลิกหรือแตะด้านนอกกล่องโต้ตอบ
- การทำงานเฉพาะแพลตฟอร์ม เช่น ปุ่ม Esc, ปุ่มย้อนกลับบนมือถือ หรือ gesture สำหรับ dismiss
- วิธีที่นักพัฒนากำหนด เช่น ปุ่มที่เรียก
HTMLDialogElement.close() หรือการ submit <form method="dialog">
closerequest: ปิดได้ด้วยการทำงานเฉพาะแพลตฟอร์มและวิธีที่นักพัฒนากำหนด
none: ปิดได้เฉพาะวิธีที่นักพัฒนากำหนดเท่านั้น
- หากไม่มีค่า
closedby ที่ถูกต้อง เมื่อเปิดด้วย showModal() จะทำงานเหมือน closerequest ส่วนกรณีอื่นจะทำงานเหมือน none
การควบคุมด้วย JavaScript และแบบ declarative
- สามารถควบคุมการแสดงและการปิด
<dialog> ได้โดยตรงด้วย JavaScript
showModal(): แสดงกล่องโต้ตอบแบบ modal
show(): แสดงกล่องโต้ตอบแบบ non-modal
close(): ปิดกล่องโต้ตอบ
- ยังสามารถปิดได้ด้วยการ submit
<form> ภายใน <dialog> โดยตั้ง method="dialog"
- กล่องโต้ตอบแบบ modal ยังปิดด้วยปุ่ม Esc ได้ด้วย
- Invoker Commands API ทำให้เปิดและปิดกล่องโต้ตอบแบบ modal ได้ด้วยแอตทริบิวต์ของปุ่มเท่านั้น
- กำหนดแอตทริบิวต์
commandfor และ command ให้กับ <button>
- คำสั่งที่ใช้กับกล่องโต้ตอบได้คือ
"show-modal", "close", "request-close"
Open dialog
This dialog was opened using an invoker command.
Close
- Popover API ทำให้เปิด ปิด และ toggle กล่องโต้ตอบแบบ non-modal ได้ในเชิง declarative
- เพิ่มแอตทริบิวต์
popover ให้ <dialog> เพื่อทำให้เป็น popover
- กำหนด
popovertarget, popovertargetaction ให้ปุ่มหรือองค์ประกอบ input
- กล่องโต้ตอบแบบ popover เป็น non-modal จึงปิดได้ด้วยการคลิกด้านนอก
- หากไม่ระบุค่า
popover จะใช้ค่าเริ่มต้น "auto" และเปิดใช้ light dismiss
popover="manual" จะปิดใช้ light dismiss และต้องมีวิธีอื่น เช่น ปุ่มปิด
- หากละ
popovertargetaction จะใช้ค่าเริ่มต้น toggle
การ submit ฟอร์มและการจัดการปิด
<dialog> ทุกตัวจำเป็นต้องมีกลไกการปิด และต้องใช้งานได้แม้บนอุปกรณ์ที่ไม่มีคีย์บอร์ดจริง
- มีหลายวิธีในการปิด
- ตั้งค่า
method="dialog" ใน <form> ภายใน <dialog> แล้ว submit
- คลิกด้านนอกกล่องโต้ตอบเมื่อเปิดใช้ light dismiss
- กดปุ่ม Esc ในกล่องโต้ตอบที่อนุญาต
- เรียก
HTMLDialogElement.close()
method="dialog" ของ <form> หรือ formmethod="dialog" ของปุ่ม submit จะปิดกล่องโต้ตอบ
- สถานะของ form controls จะถูกบันทึกไว้ แต่จะไม่ถูก submit
returnValue จะถูกตั้งเป็นค่าของปุ่มที่ถูก activate
- ในฟอร์มที่มีค่าบังคับกรอก user agent จะไม่ให้ปิดกล่องโต้ตอบด้วยการ submit ปกติจนกว่าจะมีการกรอกค่า
- ใช้
formnovalidate กับปุ่มปิดเพื่อข้ามการตรวจสอบฟอร์มได้
- หรือเรียก
dialog.close() ใน JavaScript ก็ปิดได้
โฟกัสและ accessibility
- เมื่อเปิด
<dialog> ด้วย showModal() โดยค่าเริ่มต้นโฟกัสจะถูกตั้งไปที่องค์ประกอบที่โฟกัสได้ตัวแรกที่ซ้อนอยู่ภายใน
- หากต้องการกำหนดตำแหน่งโฟกัสเริ่มต้นที่เหมาะสมที่สุดสำหรับกล่องโต้ตอบหนึ่ง ๆ สามารถใช้แอตทริบิวต์
autofocus ได้
- หากไม่มีองค์ประกอบที่ต้องโต้ตอบทันที แนะนำให้ใส่
autofocus ที่ปุ่มปิด
- หากตำแหน่งโฟกัสเริ่มต้นไม่แน่นอน เช่น กล่องโต้ตอบที่ render แบบ dynamic ตัว
<dialog> เองอาจเป็นตำแหน่งโฟกัสเริ่มต้นที่เหมาะสมได้
- วิธีที่แข็งแรงที่สุดคือใส่ ปุ่มที่ชัดเจน เช่น ปุ่มยืนยัน ยกเลิก หรือปิด เพื่อให้ผู้ใช้ทุกคนปิดได้
- กล่องโต้ตอบที่เรียกด้วย
showModal() จะปิดด้วยปุ่ม Esc โดยค่าเริ่มต้น
- กล่องโต้ตอบแบบ non-modal โดยค่าเริ่มต้นจะไม่ปิดด้วยปุ่ม Esc
- ผู้ใช้คีย์บอร์ดคาดหวังว่ากล่องโต้ตอบแบบ modal จะปิดด้วยปุ่ม Esc ได้
- หากมีกล่องโต้ตอบแบบ modal เปิดอยู่หลายตัว ปุ่ม Esc ควรปิดเฉพาะกล่องโต้ตอบที่แสดงล่าสุด และเมื่อใช้
<dialog> เบราว์เซอร์จะจัดพฤติกรรมนี้ให้
<dialog> แบบเนทีฟให้ฟีเจอร์ด้าน usability และ accessibility ที่ต้องทำซ้ำเองเมื่อสร้าง custom dialog ด้วยองค์ประกอบอื่น
- เบราว์เซอร์ expose
<dialog> คล้ายกับ custom dialog ที่ใช้ ARIA role="dialog"
<dialog> ที่เรียกด้วย showModal() จะมี aria-modal="true" โดยนัย
<dialog> ที่แสดงด้วย show(), แอตทริบิวต์ open หรือการเปลี่ยน display พื้นฐาน จะถูก expose เป็น aria-modal="false"
- เมื่อ implement กล่องโต้ตอบแบบ modal ทุกอย่างนอกเหนือจาก
<dialog> และเนื้อหาภายในควรถูก render เป็น inert และเมื่อใช้ showModal() เบราว์เซอร์จะจัดพฤติกรรมนี้ให้
การจัดสไตล์ CSS และ backdrop
<dialog> สามารถเลือกด้วยชื่อองค์ประกอบได้เหมือนองค์ประกอบทั่วไป
- สำหรับการจัดสไตล์ตามสถานะ สามารถใช้ pseudo-class
:modal, :open ได้
- พื้นหลังของกล่องโต้ตอบแบบ modal สามารถจัดสไตล์ด้วย pseudo-element
::backdrop ได้
- ใช้กับ backdrop ที่ปรากฏอยู่ด้านหลัง
<dialog> เมื่อแสดงกล่องโต้ตอบด้วย showModal()
- สามารถทำให้เนื้อหาด้านหลังที่อยู่ในสถานะ inert เบลอ มืดลง หรือถูกบังได้
รูปแบบการใช้งานจากตัวอย่าง
- ตัวอย่าง Invoker Commands API เปิดกล่องโต้ตอบด้วยปุ่ม
command="show-modal" และปิดด้วยปุ่ม command="close"
- เปิดได้ด้วยปุ่ม “Open dialog”
- ปิดได้ด้วยปุ่ม “Close” หรือปุ่ม Esc
- ตัวอย่าง Popover API เปิดและปิดกล่องโต้ตอบแบบ non-modal ด้วย
popover, popovertarget, popovertargetaction
- ปิดได้ด้วยปุ่ม “Close”, ปุ่ม Esc หรือการเลือกด้านนอกกล่องโต้ตอบ
- หากใช้
popover="manual" จะปิดใช้ light dismiss
- ตัวอย่างแอตทริบิวต์
open สร้างกล่องโต้ตอบแบบ non-modal แบบ HTML-only ที่เปิดอยู่แล้วเมื่อโหลดหน้า
- ปิดได้ด้วยปุ่ม “OK” ของ
<form method="dialog">
- หลังจากปิดแล้วไม่มีวิธีเปิดใหม่ให้
- สำหรับการแสดงกล่องโต้ตอบแบบ non-modal นิยมใช้
HTMLDialogElement.show() มากกว่า
- ตัวอย่างกล่องโต้ตอบแบบ modal เปิดด้วย
.showModal() และปิดด้วย close()
- จัดสไตล์พื้นหลัง gradient ด้วย
::backdrop
- เมื่อกล่องโต้ตอบเปิดอยู่ ภายนอกกล่องโต้ตอบจะอยู่ในสถานะ inert และไม่สามารถโต้ตอบกับเอกสารได้
- ตัวอย่าง
returnValue จัดการค่าที่ส่งคืนของกล่องโต้ตอบด้วยฟอร์มและค่าของปุ่ม
- ค่า
returnValue เริ่มต้นเป็นสตริงว่าง หรือเป็นค่าของปุ่มที่ submit ฟอร์มภายในกล่องโต้ตอบ
- ปุ่ม “Confirm” ส่งค่าที่เลือกให้
close() เพื่อใช้เป็นค่าที่ส่งคืน
- หากปิดด้วยปุ่ม Esc ค่า
returnValue จะไม่ถูกอัปเดต และ event close ก็ไม่เกิดขึ้น ทำให้ข้อความ <output> ไม่ถูกอัปเดต
Animation ของ <dialog>
<dialog> ที่ซ่อนอยู่จะเป็น display: none และ <dialog> ที่แสดงอยู่จะเป็น display: block
- เมื่อสถานะการแสดงเปลี่ยน จะถูกเพิ่มเข้าไปหรือลบออกจาก top layer และ accessibility tree
- หากต้องการทำ animation ให้
<dialog> พร็อพเพอร์ตี display ต้องสามารถ animate ได้
- เบราว์เซอร์ที่รองรับจะจัดการ
display ด้วยวิธี discrete animation
- เมื่อเปลี่ยนจาก
none เป็น block จะสลับเป็น block ที่จุดเวลา 0% จึงมองเห็นตลอดทั้ง animation
- เมื่อเปลี่ยนจาก
block เป็น none จะสลับเป็น none ที่จุดเวลา 100% จึงมองเห็นตลอดทั้ง animation
-
ทำ animation ด้วย CSS transition
- หากต้องการทำ animation ให้
<dialog> ด้วย CSS transition ต้องมีฟีเจอร์ต่อไปนี้
@starting-style: ให้ค่าเริ่มต้นของ transition ทุกครั้งที่กล่องโต้ตอบเปิด
- transition ของ
display: คงค่า display ที่ทำให้มองเห็นกล่องโต้ตอบไว้ระหว่าง transition
- transition ของ
overlay: หน่วงการนำออกจาก top layer จนกว่า transition จะเสร็จ
transition-behavior: allow-discrete: เปิดใช้ discrete transition ของ display, overlay ซึ่งโดยปกติไม่ถูก animate
- ในเบราว์เซอร์ที่ไม่รองรับ pseudo-class
:open สามารถจัดสไตล์สถานะเปิดด้วย attribute selector dialog[open] ได้
- ทุกครั้งที่
<dialog> ถูกแสดง จะเปลี่ยนจาก display: none เป็น display: block ดังนั้นทุก entry transition จะเปลี่ยนจาก @starting-style ไปเป็นสไตล์ dialog:open
-
keyframe animation
- CSS keyframe animation มีข้อจำกัดต่างจาก transition
- ไม่มี
@starting-style
- รวมค่า
display ไว้ใน keyframe
- ไม่จำเป็นต้องเปิดใช้
allow-discrete อย่างชัดเจน
- ไม่จำเป็นต้องตั้งค่า
overlay ใน keyframe ด้วย
- backdrop fade-out ไม่สามารถ animate ได้ เพราะเมื่อกล่องโต้ตอบปิด backdrop จะถูกนำออกจาก DOM ทันที
สรุปทางเทคนิค
- หมวดหมู่เนื้อหา: flow content, sectioning root
- เนื้อหาที่อนุญาต: flow content
- ต้องมีทั้งแท็กเปิดและแท็กปิด และไม่มีการละแท็ก
- parent ที่อนุญาต: องค์ประกอบทั้งหมดที่รับ flow content
- บทบาท ARIA โดยนัย:
dialog
- บทบาท ARIA ที่อนุญาต:
alertdialog
- DOM interface:
HTMLDialogElement
- สเปก: HTML # the-dialog-element
ยังไม่มีความคิดเห็น