การแก้ไขลำดับ DOM ด้วย Tabindex
ลำดับแท็บเริ่มต้นที่ตำแหน่ง DOM ขององค์ประกอบเนทีฟ
ใช้ได้อย่างสะดวก แต่ก็มีบางครั้งที่คุณจะต้องแก้ไขลำดับแท็บ และการย้ายองค์ประกอบใน HTML ก็ไม่ใช่วิธีที่ดีที่สุดเสมอไปหรือแม้แต่วิธีแก้ปัญหาที่เป็นไปได้ สำหรับกรณีเหล่านี้ คุณใช้แอตทริบิวต์ HTML tabindex
เพื่อตั้งค่าตำแหน่งแท็บขององค์ประกอบได้อย่างชัดแจ้ง
สามารถใช้ tabindex
กับองค์ประกอบใดก็ได้ แม้ว่าอาจไม่มีประโยชน์กับทุกองค์ประกอบเสมอไป และจะใช้ช่วงของค่าจำนวนเต็ม เมื่อใช้ tabindex
คุณสามารถระบุลำดับที่ชัดเจนขององค์ประกอบของหน้าที่โฟกัสได้ แทรกองค์ประกอบที่ไม่สามารถโฟกัสได้ไว้ลงในลำดับแท็บ และนำองค์ประกอบออกจากลำดับแท็บ เช่น
tabindex="0"
: แทรกองค์ประกอบตามลำดับแท็บอย่างเป็นธรรมชาติ คุณจะโฟกัสองค์ประกอบได้ด้วยการกดแป้น Tab
และโฟกัสองค์ประกอบได้โดยเรียกใช้เมธอด focus()
<custom-button tabindex="0">Press Tab to Focus Me!</custom-button>
tabindex="-1"
: นำองค์ประกอบออกจากลำดับแท็บอย่างเป็นธรรมชาติ แต่จะยังโฟกัสองค์ประกอบได้ด้วยการเรียกใช้เมธอด focus()
// TODO: DevSite - Code sample removed as it used inline event handlers
// สิ่งที่ต้องทำ: DevSite - ตัวอย่างโค้ดถูกนำออกเนื่องจากใช้เครื่องจัดการเหตุการณ์แบบอินไลน์
tabindex="5"
: ดัชนีแท็บที่มากกว่า 0 จะข้ามองค์ประกอบไปหน้าลำดับแท็บปกติ หากมีหลายองค์ประกอบที่มีดัชนีแท็บมากกว่า 0 ลำดับแท็บจะเริ่มจากค่าต่ำสุดที่มากกว่า 0 และค่อยๆ เรียงตามลำดับขึ้นมา การใช้ดัชนีแท็บที่มากกว่า 0 จะถือเป็นรูปแบบป้องกัน
<button>I should be first</button>
<button>And I should be second</button>
<button tabindex="5">But I jumped to the front!</button>
โดยเฉพาะอย่างยิ่งกับองค์ประกอบที่ไม่ใช่อินพุต เช่น ส่วนหัว รูปภาพ หรือชื่อบทความ การเพิ่ม tabindex
ลงในองค์ประกอบประเภทดังกล่าวจะทำให้ประสิทธิภาพขัดแย้งกัน หากทำได้ วิธีที่ดีที่สุดคือการจัดเรียงซอร์สโค้ดเพื่อให้ลำดับ DOM แสดงลำดับแท็บที่สมเหตุสมผล หากใช้ tabindex
ให้จำกัดให้จำกัดไว้เฉพาะสำหรับการควบคุมแบบอินเทอร์แอกทีฟที่กำหนดเอง เช่น ปุ่ม แท็บ รายการแบบเลื่อนลง และช่องข้อความ กล่าวคือ องค์ประกอบที่ผู้ใช้คาดหวังว่าจะป้อนข้อมูลให้
ไม่ต้องกังวลว่าผู้ใช้โปรแกรมอ่านหน้าจอจะขาดเนื้อหาสำคัญเพราะ
โปรแกรมดังกล่าวไม่มี tabindex
แม้ว่าเนื้อหาจะสำคัญมาก อย่างเช่นรูปภาพ
หากนั่นไม่ใช่สิ่งที่ผู้ใช้สามารถโต้ตอบด้วยได้ ก็ไม่มีเหตุผลที่จะต้องโฟกัสได้ ผู้ใช้โปรแกรมอ่านหน้าจอยังคงเข้าใจเนื้อหาของรูปภาพได้ ตราบใดที่คุณให้การรองรับแอตทริบิวต์ alt
ที่เหมาะสม ซึ่งเราจะพูดถึงในไม่ช้า
การจัดการโฟกัสที่ระดับหน้าเว็บ
นี่คือสถานการณ์ที่ tabindex
ไม่เพียงแต่มีประโยชน์เท่านั้น แต่ยังจำเป็นอีกด้วย คุณอาจกำลังสร้างหน้าเว็บเดียวที่มีประสิทธิภาพด้วยส่วนเนื้อหาที่แตกต่างกัน ซึ่งไม่ใช่ทั้งหมดที่จะมองเห็นได้พร้อมกัน ในหน้าเว็บประเภทนี้ การคลิกลิงก์การนำทางอาจเปลี่ยนเนื้อหาที่มองเห็นได้โดยไม่ต้องรีเฟรชหน้าเว็บ
เมื่อเกิดกรณีนี้ขึ้น คุณอาจระบุพื้นที่ของเนื้อหาที่เลือก ใส่ tabindex
เป็น -1 เพื่อให้เนื้อหาไม่ปรากฏในลำดับแท็บตามปกติ และเรียกใช้เมธอด focus
เทคนิคนี้ซึ่งเรียกว่าการจัดการโฟกัสจะทำให้บริบทที่รับรู้ของผู้ใช้ซิงค์กับเนื้อหาภาพของเว็บไซต์
การจัดการโฟกัสในคอมโพเนนต์
การจัดการโฟกัสเมื่อเปลี่ยนแปลงบางสิ่งในหน้าเว็บมีความสำคัญ แต่บางครั้งคุณต้องจัดการโฟกัสที่ระดับการควบคุม เช่น หากคุณกำลังสร้างคอมโพเนนต์ที่กำหนดเอง
ลองพิจารณาองค์ประกอบดั้งเดิมของ select
วัตถุจะใช้โฟกัสพื้นฐานได้ แต่เมื่อถึงตำแหน่งนั้นแล้ว คุณสามารถใช้ปุ่มลูกศรเพื่อแสดงฟังก์ชันเพิ่มเติม (ตัวเลือกที่เลือกได้) หากคุณกำลังสร้างองค์ประกอบ select
ที่กำหนดเอง คุณจะต้องแสดงลักษณะการทำงานประเภทเดียวกันเหล่านี้เพื่อให้ผู้ใช้ที่ใช้แป้นพิมพ์เป็นหลักยังคงโต้ตอบกับตัวควบคุมของคุณได้
<!-- Focus the element using Tab and use the up/down arrow keys to navigate -->
<select>
<option>Aisle seat</option>
<option>Window seat</option>
<option>No preference</option>
</select>
การรู้ว่าลักษณะการทำงานของแป้นพิมพ์แบบใดอาจทำได้ยาก แต่ก็มีเอกสารที่เป็นประโยชน์ที่คุณสามารถอ้างอิงได้ คู่มือการฝึกเขียนโปรแกรม Accessible Rich Internet Applications (ARIA) จะแสดงรายการประเภทของคอมโพเนนต์และประเภทการทำงานของแป้นพิมพ์ที่รองรับ เราจะพูดถึง ARIA อย่างละเอียดในภายหลัง แต่ตอนนี้เราจะใช้คู่มือเพื่อช่วยเราเพิ่มการรองรับแป้นพิมพ์ให้กับคอมโพเนนต์ใหม่
บางทีคุณอาจกำลังสร้างองค์ประกอบที่กำหนดเองใหม่ๆ ที่คล้ายคลึงกับชุดปุ่มตัวเลือก แต่รูปลักษณ์และพฤติกรรมเฉพาะตัวของคุณ
<radio-group>
<radio-button>Water</radio-button>
<radio-button>Coffee</radio-button>
<radio-button>Tea</radio-button>
<radio-button>Cola</radio-button>
<radio-button>Ginger Ale</radio-button>
</radio-group>
หากต้องการดูว่าลูกค้าต้องการการรองรับแป้นพิมพ์ประเภทใด โปรดอ่านคู่มือแนวทางปฏิบัติในการเขียน ARIA ส่วนที่ 2 มีรายการรูปแบบการออกแบบ และในรายการดังกล่าวคือตารางลักษณะเฉพาะสำหรับกลุ่มวิทยุ ซึ่งเป็นคอมโพเนนต์ที่มีอยู่ซึ่งตรงกับองค์ประกอบใหม่มากที่สุด
อย่างที่เห็นในตาราง ลักษณะการทำงานของแป้นพิมพ์ทั่วไปอย่างหนึ่งที่ควรรองรับคือแป้นลูกศรขึ้น/ลง/ซ้าย/ขวา หากต้องการเพิ่มพฤติกรรมนี้ไปยังคอมโพเนนต์ใหม่ เราจะใช้เทคนิคที่เรียกว่า Roving Tabindex
ดัชนีแท็บ Roving ทำงานโดยการตั้งค่า tabindex
เป็น -1 สำหรับเด็กทุกคน ยกเว้นรายการที่ใช้งานอยู่ในปัจจุบัน
<radio-group>
<radio-button tabindex="0">Water</radio-button>
<radio-button tabindex="-1">Coffee</radio-button>
<radio-button tabindex="-1">Tea</radio-button>
<radio-button tabindex="-1">Cola</radio-button>
<radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>
จากนั้นคอมโพเนนต์จะใช้ Listener เหตุการณ์บนแป้นพิมพ์เพื่อระบุคีย์ที่ผู้ใช้กด เมื่อเกิดเหตุการณ์นี้ขึ้น คอมโพเนนต์ก็จะตั้งค่า tabindex
ของบุตรหลานที่โฟกัสก่อนหน้าเป็น -1, ตั้งค่า tabindex
ของบุตรหลานให้โฟกัสเป็น 0 และเรียกใช้วิธีการโฟกัสกับคอมโพเนนต์ดังกล่าว
<radio-group>
// Assuming the user pressed the down arrow, we'll focus the next available child
<radio-button tabindex="-1">Water</radio-button>
<radio-button tabindex="0">Coffee</radio-button> // call .focus() on this element
<radio-button tabindex="-1">Tea</radio-button>
<radio-button tabindex="-1">Cola</radio-button>
<radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>
เมื่อผู้ใช้ไปถึงรายการย่อยรายการสุดท้าย (หรือรายการแรก ขึ้นอยู่กับทิศทางที่ผู้ใช้ย้ายโฟกัส) คุณจะวนลูปและโฟกัสที่เด็กกลุ่มแรก (หรือสุดท้าย) อีกครั้ง
คุณสามารถดูตัวอย่างที่สมบูรณ์จากด้านล่างนี้ ตรวจสอบองค์ประกอบใน DevTools เพื่อดูว่า Tabindex ย้ายจากวิทยุหนึ่งไปยังอีกวิทยุหนึ่ง
// สิ่งที่ต้องทำ: DevSite - ตัวอย่างโค้ดถูกนำออกเนื่องจากใช้เครื่องจัดการเหตุการณ์แบบอินไลน์
คุณดูแหล่งที่มาทั้งหมดสำหรับองค์ประกอบนี้ได้ใน GitHub
วิธีการและกับดักแป้นพิมพ์
บางครั้งเมื่อคุณกำลังจดจ่อกับสิ่งที่ทำอยู่ คุณอาจตกอยู่ในสถานการณ์ที่ไม่เคยหยุดนิ่ง ลองใช้วิดเจ็ตเติมข้อความอัตโนมัติที่พยายามจัดการโฟกัสและจับภาพลักษณะการทำงานของแท็บ แต่ป้องกันไม่ให้ผู้ใช้ปล่อยไว้จนกว่าจะดำเนินการเสร็จสิ้น ซึ่งการดำเนินการดังกล่าวเรียกว่าเครื่องดักแป้นพิมพ์ และผู้ใช้อาจรู้สึกหงุดหงิดอย่างมาก ส่วนที่ 2.1.2 ของรายการตรวจสอบเว็บ AIM จะจัดการปัญหานี้ โดยระบุว่าไม่ควรล็อกหรือจับโฟกัสของแป้นพิมพ์ที่องค์ประกอบหนึ่งๆ ของหน้าเว็บหนึ่งๆ ผู้ใช้ควรจะสามารถไปยังส่วนต่างๆ และจากองค์ประกอบทั้งหมดของหน้าได้โดยใช้แป้นพิมพ์เท่านั้น
แต่ก็มีบางครั้งที่พฤติกรรมนี้เป็นไปตามที่ต้องการ เช่น ในหน้าต่างโมดัล โดยปกติแล้ว เมื่อโมดัลปรากฏขึ้น คุณไม่ต้องการให้ผู้ใช้เข้าถึงเนื้อหาที่อยู่เบื้องหลัง คุณอาจเพิ่มการวางซ้อนเพื่อให้ภาพบังหน้า แต่นั่นไม่ได้ทำให้โฟกัสของแป้นพิมพ์หลุดออกไปนอกโมดัลโดยไม่ได้ตั้งใจ
ในกรณีเช่นนี้ คุณสามารถใช้กับดักแป้นพิมพ์ชั่วคราวเพื่อตรวจจับโฟกัสเฉพาะในขณะที่โมดัลแสดงขึ้นเท่านั้น จากนั้นคืนค่าโฟกัสไปยังรายการที่โฟกัสก่อนหน้านี้เมื่อปิดโมดัล
มีข้อเสนอบางอย่างเกี่ยวกับวิธีทำให้การดำเนินการนี้ง่ายขึ้นสำหรับนักพัฒนาซอฟต์แวร์ ซึ่งรวมถึงองค์ประกอบ
<dialog>
แต่ยังไม่มีการรองรับเบราว์เซอร์อย่างแพร่หลายดูข้อมูลเพิ่มเติมเกี่ยวกับ
<dialog>
ได้ที่บทความ MDN นี้และตัวอย่างโมดัลนี้สำหรับข้อมูลเพิ่มเติมเกี่ยวกับหน้าต่างโมดัล
ลองใช้กล่องโต้ตอบโมดัลที่แสดงด้วยdiv
ซึ่งมีองค์ประกอบไม่กี่อย่าง และอีก div
แสดงการวางซ้อนพื้นหลัง มาดูขั้นตอนพื้นฐานต่างๆ ที่จำเป็นในการ
ติดตั้งกับดักแป้นพิมพ์ชั่วคราวในสถานการณ์นี้กัน
- เมื่อใช้
document.querySelector
ให้เลือก div แบบโมดัลและการวางซ้อน แล้วจัดเก็บการอ้างอิง - เมื่อโมดัลเปิดขึ้น ให้จัดเก็บการอ้างอิงไปยังองค์ประกอบที่โฟกัสไว้เมื่อโมดัลเปิดอยู่ เพื่อให้คุณกลับมาโฟกัสที่องค์ประกอบนั้นได้
- ใช้ Listener คีย์ดาวน์เพื่อจับคีย์ต่างๆ เมื่อมีการกดในขณะที่โมดัลเปิดอยู่ นอกจากนี้คุณยังสามารถฟังการคลิกบนพื้นหลังและ ปิดโมดัลหากผู้ใช้คลิกข้อความดังกล่าว
- ต่อไป ให้รับคอลเล็กชันขององค์ประกอบที่โฟกัสได้ภายในโมดัล องค์ประกอบแรกและองค์ประกอบสุดท้ายที่โฟกัสได้จะทำหน้าที่เป็น "เซนติเนล" เพื่อให้คุณทราบว่าเมื่อใดควรวนโฟกัสไปข้างหน้าหรือข้างหลังเพื่อให้อยู่ในโมดัลนี้
- แสดงหน้าต่างโมดัลและโฟกัสองค์ประกอบที่โฟกัสได้รายการแรก
- เมื่อผู้ใช้กด
Tab
หรือShift+Tab
ให้เลื่อนโฟกัสไปข้างหน้าหรือข้างหลัง โดยวนซ้ำที่องค์ประกอบสุดท้ายหรือองค์ประกอบแรกตามความเหมาะสม - หากผู้ใช้กด
Esc
ให้ปิดโมดัลนี้ การทำเช่นนี้มีประโยชน์มากเพราะช่วยให้ผู้ใช้ปิดโมดัลนี้ได้โดยไม่ต้องค้นหาปุ่มปิดที่เฉพาะเจาะจง และเป็นประโยชน์แม้แต่ผู้ใช้ที่กำลังใช้เมาส์ - เมื่อปิดโมดัลแล้ว ให้ซ่อนและการวางซ้อนพื้นหลัง แล้วคืนค่าโฟกัสไปยังองค์ประกอบที่โฟกัสก่อนหน้านี้ซึ่งบันทึกไว้ก่อนหน้านี้
กระบวนการนี้จะให้หน้าต่างโมดัลที่ใช้งานได้และไม่รบกวนการใช้งานที่ทุกคนใช้งานได้อย่างมีประสิทธิภาพ
ดูรายละเอียดเพิ่มเติมได้จากโค้ดตัวอย่างนี้และดูตัวอย่างจริงจากหน้าที่เสร็จสมบูรณ์แล้ว