แผงการทำงานที่แสดงหรือกลุ่มปุ่มป๊อปโอเวอร์เป็นรูปแบบที่มีประโยชน์สำหรับผู้ใช้ในการเข้าถึงฟังก์ชันการทำงานเพิ่มเติมโดยใช้พื้นที่น้อยที่สุด รูปแบบการวางซ้อนนี้มาพร้อมกับความซับซ้อนของเลย์เอาต์ เนื่องจากแผงต้องเชื่อมโยงกับองค์ประกอบทริกเกอร์ขณะปรับให้เข้ากับข้อจำกัดของวิวพอร์ต โดยปกติแล้ว การดำเนินการนี้ต้องใช้ไลบรารี JavaScript ที่ซับซ้อน (เช่น Popper.js หรือ Floating UI) เพื่อคำนวณตำแหน่งและจัดการการชนกัน
การจัดตำแหน่ง Anchor ของ CSS เป็นวิธีที่ประกาศและเพิ่มประสิทธิภาพเพื่อจัดการความสัมพันธ์เหล่านี้ใน CSS ทั้งหมด ซึ่งช่วยให้เบราว์เซอร์จัดการตรรกะการจัดตำแหน่งและโอเวอร์โฟลว์ได้โดยตรง
1. กำหนดความสัมพันธ์ของปุ่มและแผง
ขั้นตอนแรกคือการสร้างปุ่มทริกเกอร์ที่เปิดคอนเทนเนอร์วางซ้อนโดยใช้ Popover API
ความแตกต่างด้านการช่วยเหลือพิเศษที่จำเป็น: รูปแบบนี้จำลองกลุ่มปุ่มที่แสดงภายในป๊อปโอเวอร์อย่างชัดเจน แทนที่จะเป็นเมนู ARIA จริง อย่าใช้ role="menu" หรือ role="menuitem" เว้นแต่คุณจะใช้สัญญาการไปยังส่วนต่างๆ ด้วยแป้นพิมพ์ที่เกี่ยวข้องอย่างเต็มรูปแบบ (เช่น การจัดการการไปยังส่วนต่างๆ ด้วยแป้นลูกศรเชิงพื้นที่ระหว่างรายการต่างๆ) ด้วยเหตุผลเดียวกันนี้ อย่าเพิ่ม aria-haspopup ลงในทริกเกอร์ เนื่องจากค่า (menu, listbox, tree, grid หรือ true เดิม) เป็นสัญญาว่าเป้าหมายจะแสดงบทบาทที่ตรงกัน ซึ่งรูปแบบนี้ไม่ได้ทำเช่นนั้น
<button popovertarget="action-panel">
Open Actions
</button>
<!-- Use the Popover API (`popover="auto"`) for the overlay to ensure it is placed in the top layer and handled accessibly by the browser. -->
<div id="action-panel" popover="auto" class="panel">
<button class="action-item" type="button">Edit</button>
</div>
ซึ่งจะสร้างการเชื่อมโยง Anchor โดยนัยระหว่างปุ่มกับแผง เพื่อให้แผงอยู่ในตำแหน่งที่สัมพันธ์กับปุ่มโดยค่าเริ่มต้น โดยไม่ต้องใช้ anchor-name ในปุ่มหรือ position-anchor ในแผง
2. การระบุตำแหน่งด้วย position-area
ใช้ position-area เพื่อวางเป้าหมายบนตารางกริด 3x3 ที่สัมพันธ์กับ Anchor แทนการใช้ค่าออฟเซ็ต top/left ด้วยตนเอง
.panel {
/*
Position the panel below the anchor (block-end),
aligned to the start of the anchor and spanning to its end (span-inline-end).
*/
position-area: block-end span-inline-end;
/* Reset insets to allow the grid to take control */
inset: auto;
}
ใช้คีย์เวิร์ดเชิงตรรกะ (span-inline-end, block-start) แทนคีย์เวิร์ดเชิงกายภาพ (left, top) เพื่อรองรับ RTL และโหมดการเขียนที่แตกต่างกันโดยอัตโนมัติ
บังคับ: อย่าผสมคีย์เวิร์ดทางกายภาพและเชิงตรรกะใน position-area
3. ใช้ความยืดหยุ่นของ Edge (การสำรอง)
เพื่อป้องกันไม่ให้แผงถูกตัดออกที่ขอบหน้าจอ ให้กำหนด "กลยุทธ์การลอง" ที่เบราว์เซอร์ควรพยายามทำหากตำแหน่งเริ่มต้นล้น
.panel {
/*
If the panel overflows the bottom, flip it to the top (flip-block).
If it overflows the inline edges, flip it horizontally (flip-inline).
If it overflows both (e.g., in a corner), flip on both axes (flip-block flip-inline).
*/
position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
}
หากเมนูแบบเลื่อนลงหรือเมนูแบบลอยมีลูกศรบอกทิศทางหรือเส้นขอบที่ต้องเปลี่ยนสไตล์ตามตำแหน่งสำรองที่เลือกไว้ โปรดดูเคล็ดลับเครื่องมือที่รับรู้ตำแหน่ง สำหรับการเรียกใช้กล่องโต้ตอบและป๊อปโอเวอร์แบบประกาศโดยไม่มี JavaScript โปรดดูการควบคุมกล่องโต้ตอบและป๊อปโอเวอร์แบบประกาศ
กลยุทธ์สำรอง
การรองรับเบราว์เซอร์และทางเลือกสำรองสำหรับ Popover
Popover API ส่วนใหญ่เป็นการเพิ่มประสิทธิภาพแบบต่อเนื่อง แต่ลักษณะการทำงานที่กำหนดไว้ เช่น การโปรโมตเลเยอร์บนสุด การปิดเมื่อคลิกนอกองค์ประกอบ และการเรียกใช้ popovertarget ไม่มี CSS ที่เทียบเท่า เบราว์เซอร์รุ่นเก่าต้องใช้ Polyfill หรือการสำรองข้อมูลด้วยตนเองหากคุณไม่ต้องการจัดส่ง
Polyfill: หากต้องการรองรับแอตทริบิวต์ popover ในเบราว์เซอร์รุ่นเก่า ให้โหลด @oddbird/popover-polyfill แบบมีเงื่อนไข จำเป็น: ตรวจหาฟีเจอร์โดยการตรวจสอบพร็อพเพอร์ตี้ popover ใน HTMLElement.prototype และโหลด Polyfill เฉพาะเมื่อไม่มีการรองรับแบบเนทีฟ อย่าโหลดโดยไม่มีเงื่อนไข
เมื่อใช้ Bundler หรือแผนที่การนำเข้า ให้ทำดังนี้
// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
import("@oddbird/popover-polyfill");
}
หากไม่มี Bundler ให้นำเข้าจาก CDN ภายใน <script type="module">:
<script type="module">
if (!("popover" in HTMLElement.prototype)) {
import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
}
</script>
ข้อควรระวังเกี่ยวกับรูปแบบ: Polyfill ไม่สามารถกำหนดคลาสจำลอง :popover-open จริงได้ จึงใช้คลาส .\:popover-open แทน จำเป็น: รวมทั้ง 2 รายการเข้าด้วยกันโดยใช้ :is() หรือ :where() ไม่เช่นนั้นเบราว์เซอร์ที่ไม่มี :popover-open จะทิ้งกฎทั้งหมด
[popover]:is(:popover-open, .\:popover-open) {
display: block;
}
หรือสำหรับทางเลือกสำรองแบบเดิมที่ไม่มี Polyfill ให้ใช้ position: fixed และคำนวณพิกัดด้วยตนเองผ่าน getBoundingClientRect() หรือใช้การกำหนดตำแหน่งเริ่มต้นด้วย inset: auto หากยอมรับได้สำหรับกรณีการใช้งาน
หากต้องการรองรับเบราว์เซอร์ที่ไม่มีการจัดตำแหน่ง Anchor คุณต้องตั้งค่าตำแหน่งที่เหมาะสม โดยค่าเริ่มต้น ป๊อปโอเวอร์จะอยู่ตรงกลางหน้าจอ ซึ่งอาจเหมาะกับกรณีการใช้งานของคุณ
ในบางกรณีการใช้งาน คุณอาจใช้ @oddbird/css-anchor-positioning Polyfill ได้ ซึ่งจะเพิ่มการรองรับกรณีการใช้งานการจัดตำแหน่ง Anchor บางกรณี แต่จะไม่รองรับ Anchor โดยนัย ดังนั้นคุณต้องเพิ่มชื่อ Anchor ลงในทริกเกอร์ นอกจากนี้ Polyfill ยังไม่รองรับ position-area ในป๊อปโอเวอร์ คุณจึงต้องใช้ anchor() ในส่วนแทรกที่ต้องการ
<!-- MANDATORY: Conditionally install the anchor positioning polyfill -->
<script type="module">
if (!("anchorName" in document.documentElement.style)) {
await import("https://unpkg.com/@oddbird/css-anchor-positioning");
}
</script>
.panel {
/* Mandatory: use explicit anchor name */
position-anchor: --kebab-anchor;
/* Mandatory: use insets rather than position-area for positioning */
bottom: auto;
right: auto;
top: anchor(bottom);
left: anchor(left);
margin: 0;
}