סקירה כללית
רמזים חזותיים, כמו צללים או מעברי צבע, עוזרים למשתמשים להבין שהם יכולים לגלול כדי לראות עוד תוכן. במדריך הזה מוסבר איך ליצור את הרמזים האלה באמצעות CSS container-scroll-state-queries, שמאפשר לעצב רכיבים על סמך המצב של המאגר שניתן לגלילה בלי להסתמך על מאזינים או משקיפים לגלילה ב-JavaScript.
הטמעה
1. הגדרת מאגר הגלילה
צריך להצהיר על מאגר הגלילה כמאגר שאילתות של מצב גלילה.
.scroller {
overflow-y: auto;
/* Establish this element as a scroll-state query container */
container-type: scroll-state;
position: relative;
}
2. עיצוב האינדיקטורים
ממקמים את רכיבי האינדיקטור (כמו צללים, שיפועים או חצים) בתוך הקונטיינר ומעצבים אותם. כברירת מחדל, הם לא אמורים להיות גלויים. כשהן מוצגות, הן לא צריכות להיות אינטראקטיביות, על ידי הגדרת pointer-events: none.
.indicator-top, .indicator-bottom {
position: sticky;
left: 0;
right: 0;
height: 20px;
opacity: 0;
transition: opacity 0.2s;
pointer-events: none; /* Let clicks pass through */
}
.indicator-top {
top: 0;
background: linear-gradient(to bottom, rgba(0,0,0,0.2), transparent); /* Example: Shadow */
}
.indicator-bottom {
bottom: 0;
background: linear-gradient(to top, rgba(0,0,0,0.2), transparent); /* Example: Shadow */
}
3. שליחת שאילתה לגבי מצב הגלילה
משתמשים בכלל @container עם הפונקציה scroll-state. בודקים אם אפשר לגלול את הקונטיינר למעלה או למטה כדי להציג את האינדיקטור המתאים.
/* Show top indicator when the user can scroll up */
@container scroll-state(scrollable: top) {
.indicator-top {
opacity: 1;
}
}
/* Show bottom indicator when the user can scroll down */
@container scroll-state(scrollable: bottom) {
.indicator-bottom {
opacity: 1;
}
}
אסטרטגיות חלופיות
גיבוי בסיסי
אם התכונה לא נתמכת, האינדיקטורים יישארו מוסתרים. מכיוון שמדובר ברמזים ולא בפונקציונליות קריטית, אפשר להשמיט אותם בדפדפנים שלא נתמכים.
חלופה מתקדמת (Intersection Observer)
אם הרמזים נדרשים, משתמשים ב-IntersectionObserver כדי להחליף בין מחלקות כשאלמנטים של Sentinel בחלק העליון והתחתון של הגלילה נכנסים ויוצאים מיציאת הגלילה.
<!-- Sentinel elements placed at the ends of the scroller -->
<div class="sentinel-top"></div>
<!-- Content goes here -->
<div class="sentinel-bottom"></div>
/* Marker styling to ensure it does not affect layout */
.sentinel-top, .sentinel-bottom {
height: 0;
width: 0;
visibility: hidden;
}
.scroller.scrolled-down .indicator-top {
opacity: 1;
}
.scroller.can-scroll-down .indicator-bottom {
opacity: 1;
}
if (!CSS.supports('container-type', 'scroll-state')) {
const topSentinel = document.querySelector('.sentinel-top');
const bottomSentinel = document.querySelector('.sentinel-bottom');
const scroller = document.querySelector('.scroller');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.target === topSentinel) {
// If top sentinel is not intersecting, we have scrolled down
scroller.classList.toggle('scrolled-down', !entry.isIntersecting);
}
if (entry.target === bottomSentinel) {
// If bottom sentinel is intersecting, we reached the bottom
scroller.classList.toggle('can-scroll-down', !entry.isIntersecting);
}
});
}, { root: scroller });
observer.observe(topSentinel);
observer.observe(bottomSentinel);
}