การไปยังส่วนต่างๆ โดยโฟกัสตามทิศทาง (โดยใช้แป้นลูกศรหรือ D-Pad) ช่วยให้ผู้ใช้ที่ใช้แป้นพิมพ์เท่านั้นและผู้ใช้ที่ใช้อุปกรณ์ป้อนข้อมูลทางเลือกสามารถไปยังรายการแบบอินเทอร์แอกทีฟตามเลย์เอาต์ 2 มิติแบบภาพได้ ซึ่งมีประโยชน์อย่างมากสำหรับอินเทอร์เฟซผู้ใช้เชิงพื้นที่ (เช่น แถบสื่อ แผงแดชบอร์ด หรือแคตตาล็อกที่ฝัง) แต่ต้องติดตั้งใช้งานอย่างระมัดระวังเพื่อไม่ให้รบกวนลักษณะการทำงานของเบราว์เซอร์แบบเนทีฟ เช่น การเลื่อนวิวพอร์ต
คู่มือนี้ครอบคลุมการสร้างการนำทางตามทิศทางแบบ 2 มิติที่มีประสิทธิภาพและเข้าถึงได้ภายในวิดเจ็ตแบบผสม
หลักการทางสถาปัตยกรรมที่สำคัญ
เมื่อใช้โฟกัสเชิงพื้นที่ด้วยปุ่มลูกศรที่กำหนดเอง ให้ปฏิบัติตามกฎพื้นฐานต่อไปนี้
- จำกัดเฉพาะวิดเจ็ตแบบผสม: อย่าลบล้างแป้นลูกศรในขั้นตอนการแสดงข้อความเอกสารปกติหรือเลย์เอาต์แบบรายการแนวตั้งแบบง่ายที่คาดว่าจะมีการไปยังส่วนต่างๆ ตามลำดับด้วยปุ่ม Tab โดยค่าเริ่มต้น ดูคำแนะนำทั่วไปเกี่ยวกับขั้นตอนการจัดทำเอกสารได้ที่หลักเกณฑ์การเขียนโค้ดสำหรับการช่วยเหลือพิเศษ
- คงการเลื่อนขอบเขตแบบเนทีฟไว้: หากไม่มีองค์ประกอบที่โฟกัสได้ในทิศทางที่กด อย่าป้องกันลักษณะการทำงานเริ่มต้นของเบราว์เซอร์ ปล่อยให้เหตุการณ์เกิดการส่งต่อ (Bubble) เพื่อให้เบราว์เซอร์เลื่อนวิวพอร์ตได้โดยตรง การลบล้างแป้นลูกศรจะทำให้การช่วยเหลือพิเศษใช้งานไม่ได้โดยไม่มีเงื่อนไข (WCAG 1.4.10)
- ตรวจสอบระดับการมองเห็นและการจัดแนวของโฟกัส: เมื่อโฟกัสเปลี่ยนไป ให้ตรวจสอบว่าองค์ประกอบที่โฟกัสใหม่เลื่อนเข้ามาในมุมมอง (เช่น ใช้
scrollIntoView({ block: 'nearest', inline: 'nearest' })) เพื่อให้มองเห็นได้อย่างสมบูรณ์ - ประสานงานกับระบบเลย์เอาต์: กำหนดตำแหน่งผู้สมัครที่โฟกัสจากเรขาคณิตขอบเขตแบบเรียลไทม์ (
getBoundingClientRect()) ขณะจัดการการกดแป้นแต่ละครั้ง หลีกเลี่ยงการสันนิษฐานว่าลำดับ DOM หรือโครงสร้างตารางกริดแบบคงที่แสดงถึงเลย์เอาต์ภาพ ดูคำแนะนำเกี่ยวกับเลย์เอาต์สมัยใหม่ได้ที่เลย์เอาต์ CSS และการออกแบบที่ปรับเปลี่ยนตามอุปกรณ์
การจัดการโฟกัสที่เข้าถึงได้ง่าย
คอนเทนเนอร์เชิงพื้นที่ต้องแสดงจุดหยุดแท็บเดียวสำหรับการไปยังส่วนต่างๆ ของแท็บหน้าเว็บตามลำดับ ภายในวิดเจ็ต ให้จัดการโฟกัสของแป้นพิมพ์โดยใช้รูปแบบ Tabindex แบบเคลื่อนที่ ดังนี้
- รายการที่ใช้งานอยู่มี
tabindex="0" - รายการที่ไม่ได้ใช้งานทั้งหมดมี
tabindex="-1" - เมื่อไปยังส่วนต่างๆ ให้ย้ายโฟกัสโดยใช้โปรแกรมด้วยการเปลี่ยนแอตทริบิวต์
tabindex="0"และเรียกใช้focus()
การเลือกผู้สมัครแบบเห็นภาพ
ในเลย์เอาต์แบบไม่สมมาตร แบบตัดขึ้นบรรทัดใหม่ หรือแบบตารางกริด การแมปตามดัชนีจะล้มเหลว แต่ให้ดึงตัวเลือกจากเรขาคณิตภาพสดสำหรับการกดแป้นปัจจุบันแทน ดังนี้
- ตัวเลือก: ใช้รายการสืบทอดที่โฟกัสได้ของคอนเทนเนอร์วิดเจ็ตที่ผ่าน
checkVisibility()องค์ประกอบที่ซ่อนอยู่สามารถมีสี่เหลี่ยมผืนผ้าขนาดเป็น 0 ที่(0, 0)และต้องไม่กลายเป็นเป้าหมายupหรือleftโดยไม่ตั้งใจ - เรขาคณิต: อ่าน
getBoundingClientRect()สำหรับองค์ประกอบที่โฟกัสและตัวเลือกที่เป็นไปได้ทั้งหมดขณะจัดการการกดแป้น อย่าแคชสี่เหลี่ยมผืนผ้าเหล่านี้ทั่วโลก เนื่องจากเลย์เอาต์อาจเปลี่ยนแปลงได้จากหลายสาเหตุ และการอ่านเรขาคณิตจำนวนเล็กน้อยจะใช้ทรัพยากรน้อยเมื่อจัดการเหตุการณ์สำคัญที่ผู้ใช้กำหนดจังหวะ - ตัวกรองทิศทาง: ตัวเลือกจะอยู่ในทิศทางที่กดก็ต่อเมื่อขอบใกล้ของตัวเลือกอยู่ที่หรือเกินขอบไกลขององค์ประกอบปัจจุบันบนแกนนั้น โดยอนุญาตให้มีความคลาดเคลื่อนเล็กน้อยสำหรับการปัดเศษพิกเซลย่อย ตัวอย่างเช่น ผู้สมัครรับเลือกตั้งใน
rightต้องมีcandidate.left >= current.right - toleranceอย่าเปรียบเทียบจุดศูนย์กลางเพื่อพิจารณาว่าผู้สมัครอยู่ในทิศทางหรือไม่ - ผู้สมัครที่สอดคล้องกันก่อน: ในบรรดาผู้สมัครที่ผ่านตัวกรองทิศทาง ให้เลือกผู้สมัครที่มีขอบเขตบนแกนตั้งฉากซ้อนทับกับขอบเขตขององค์ประกอบปัจจุบัน เลือกช่องว่างที่เล็กที่สุดบนแกนการนำทาง โดยตัดความสัมพันธ์ด้วยออฟเซ็ตศูนย์กลางที่ตั้งฉากที่เล็กที่สุด ซึ่งจะช่วยให้การไปยังส่วนต่างๆ อยู่ในแถวหรือคอลัมน์ภาพที่อยู่ติดกัน แม้ว่ารายการจะกว้างผิดปกติก็ตาม
- การให้คะแนนสำรอง: หากไม่มีผู้สมัครที่ทับซ้อนกันบนแกนตั้งฉาก ให้ให้คะแนนผู้สมัครที่เหลือเป็น
gap + w * perpendicularCentreOffsetและเลือกคะแนนต่ำสุด ใช้น้ำหนักw >= 2เป็นจุดเริ่มต้น แล้วปรับแต่งสำหรับเลย์เอาต์ น้ำหนักที่มากขึ้นจะช่วยให้ผู้สมัครที่สอดคล้องกันมากขึ้น - ไม่มีตัวเลือกที่ตรงกัน: ไม่แสดงผลลัพธ์ใดๆ เครื่องจัดการเหตุการณ์ต้องไม่เรียกใช้
preventDefault()เพื่อให้เบราว์เซอร์เลื่อนได้ตามปกติ
การใช้งานจริงอาจแตกต่างกันไป แต่ให้ทำการทดสอบทิศทางตาม Edge และใช้ค่ากำหนดการทับซ้อนกันก่อนการสำรองแบบถ่วงน้ำหนัก นี่คือหัวใจสำคัญของอัลกอริทึม หลีกเลี่ยงการนำเสนอแคชหรือน้ำหนักคงที่ว่าถูกต้องเสมอ
การผสานรวมการกดแป้นพิมพ์และการทำงานร่วมกันของการเลื่อน
เราเตอร์เหตุการณ์ต้องป้องกันการเลื่อนวิวพอร์ตมาตรฐานเฉพาะเมื่อมีการเปลี่ยนโฟกัสเชิงพื้นที่ที่ถูกต้อง ดังนี้
- อย่าขัดจังหวะเหตุการณ์เมื่อมีการกดแป้นกดร่วมค้างไว้ เมื่อ
e.defaultPreventedเป็นจริงอยู่แล้ว หรือเมื่อเป้าหมายเหตุการณ์ที่อยู่ด้านในสุดใช้แป้นลูกศรเอง ใช้e.composedPath()[0]แทนที่จะใช้เพียงe.targetเนื่องจากe.targetสามารถกำหนดเป้าหมายใหม่ไปยังโฮสต์เงาได้ การตรวจสอบการควบคุม/การแก้ไขที่ใช้ได้จริงคือtarget.matches(':read-write, select, input[type="range"], [role="slider"]')เนื่องจากไม่สามารถตรวจสอบ Shadow Root แบบปิดได้ - กำหนดขอบเขต Listener ไปยังคอนเทนเนอร์วิดเจ็ต เพื่อให้วิดเจ็ตเชิงพื้นที่หลายรายการอยู่ร่วมกันในหน้าเว็บได้
- ดำเนินการเฉพาะเมื่อองค์ประกอบที่โฟกัสเป็นตัวเลือกเท่านั้น หากรายการมีการควบคุมที่โฟกัสได้ของตัวเอง ให้กำหนดให้การควบคุมเหล่านั้นเป็นตัวเลือกด้วย หรือปล่อยไว้ตามเดิม อย่าแก้ไขรายการหลักด้วย
closest() - เมื่อพบผู้สมัคร ให้เรียก
preventDefault()อัปเดตtabindexที่เคลื่อนที่ เรียกfocus()และใช้scrollIntoView({ block: 'nearest', inline: 'nearest' }) - เมื่อไม่พบตัวเลือกที่ตรงกัน ให้แสดงผลโดยไม่ต้องเรียกใช้
preventDefault()เพื่อให้การเลื่อนแบบเนทีฟดำเนินต่อไป
หลีกเลี่ยงการเพิ่มแป้นพิมพ์ลัดที่มีอักขระเดียว เช่น h/j/k/l หรือ W/A/S/D สำหรับการไปยังส่วนต่างๆ ตามพื้นที่ ปุ่มลูกศรไม่ใช่ปุ่มอักขระ และการคงการเลื่อนแบบเนทีฟไว้เมื่อไม่มีตัวเลือกจะช่วยหลีกเลี่ยงการบล็อกการเคลื่อนไหวของวิวพอร์ตโดยไม่จำเป็น