תפריטים נפתחים שמותאמים לאזור התצוגה

תכונות לאתרים: מיקום עוגן חלון קופץ

חלונית פעולות או קבוצת לחצנים בחלון קופץ הן דרכים שימושיות לאפשר למשתמשים לגשת לפונקציות נוספות בלי לתפוס הרבה מקום. הפריסה של תבנית שכבת-העל הזו מורכבת, כי החלונית צריכה להישאר מקושרת לאלמנט הטריגר תוך התאמה למגבלות של אזור התצוגה. בעבר, כדי לחשב מיקומים ולטפל בהתנגשויות, היה צורך בספריות JavaScript מורכבות (כמו Popper.js או Floating UI).

מיקום עוגן ב-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-name בכפתור או ב-position-anchor בחלונית.

2. מיקום באמצעות position-area

במקום להזיז את היעד ידנית בציר top/left, אפשר להשתמש ב-position-area כדי למקם את היעד ברשת של 3x3 ביחס לעוגן.

.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).

חובה: אל תערבבו מילות מפתח פיזיות ולוגיות ב-position-area.

3. הטמעת עמידות בקצה הרשת (חלופות)

כדי למנוע מהחלונית להיחתך בקצה המסך, מגדירים "טקטיקות ניסיון" שהדפדפן צריך לנסות אם מיקום ברירת המחדל חורג מהמסך.

.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, אפשר לעיין במאמר Declarative Dialog and Popover Control.

אסטרטגיות חלופיות

תמיכה בדפדפנים וחלופות ל-Popover

‫Popover API הוא בעיקר שיפור הדרגתי (progressive enhancement), אבל להתנהגויות שמגדירות אותו – קידום לשכבה העליונה, סגירה בלחיצה מחוץ לאלמנט והפעלה של popovertarget – אין מקבילה ב-CSS בלבד. בדפדפנים ישנים יותר צריך להשתמש ב-polyfill, או להגדיר חלופה ידנית אם אתם מעדיפים לא להשתמש ב-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");
}

בלי כלי לאריזת קוד, מייבאים מ-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. חובה: צריך לשלב את שניהם עם :is() או :where(), אחרת דפדפנים שאין בהם :popover-open יתעלמו מהכלל כולו:

[popover]:is(:popover-open, .\:popover-open) {
  display: block;
}

לחלופין, כדי להשתמש ב-fallback מדור קודם בלי polyfill, אפשר להשתמש ב-position: fixed ולחשב קואורדינטות באופן ידני באמצעות getBoundingClientRect() או להסתמך על מיקום ברירת המחדל עם inset: auto אם זה מתאים לתרחיש לדוגמה.

כדי לתמוך בדפדפנים בלי מיקום עוגן, צריך להגדיר מיקום סביר. כברירת מחדל, חלונות קופצים מופיעים במרכז המסך, וזה יכול להתאים לתרחיש לדוגמה שלכם.

במקרים מסוימים, אפשר להשתמש ב-polyfill של @oddbird/css-anchor-positioning, שמוסיף תמיכה בחלק מהמקרים של מיקום עוגן. היא לא תומכת בעוגנים מרומזים, ולכן חובה להוסיף שמות של עוגנים לטריגר. בנוסף, position-area לא נתמך ב-popovers על ידי ה-polyfill, לכן חובה להשתמש ב-anchor() ב-insets הרצויים.

<!-- 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;
}