ואם אמרתי לכם, יש יותר מאזור תצוגה אחד.
BRRRRAAAAAAAMMMMMMMM'
ואזור התצוגה שבו אתם משתמשים כרגע הוא למעשה אזור תצוגה בתוך אזור תצוגה.
BRRRRAAAAAAAMMMMMMMM'
ולפעמים הנתונים שה-DOM נותן לכם מתייחסים לאחד מאזורי התצוגה האלה ולא לשני.
BRRRRAAAAM... לחכות מה?
זה נכון, כדאי לראות:
אזור התצוגה של הפריסה לעומת אזור התצוגה החזותי
בסרטון שלמעלה רואים דף אינטרנט שמתבצעת גלילה ושינוי מרחק התצוגה, יחד עם מיני-מפה בצד שמאל שמראה את המיקום של אזורי התצוגה בדף.
העניינים זזים ממש קדימה במהלך גלילה רגילה. האזור הירוק מייצג את אזור התצוגה של הפריסה, שאליו נשמרים position: fixed
פריטים.
הדברים נעשים מוזרים כשמכניסים את מרחק התצוגה באמצעות צביטה. התיבה האדומה מייצגת את אזור התצוגה הוויזואלי, שהוא החלק בדף שאנחנו יכולים לראות בפועל. אזור התצוגה הזה יכול לנוע, ורכיבי position: fixed
נשארים במיקום שבו היו מחוברים לאזור התצוגה של הפריסה. אם מזיזים את הגבולות של אזור התצוגה של הפריסה, גוררים איתה את אזור התצוגה.
שיפור התאימות
לצערי ממשקי ה-API לאינטרנט לא עקביים מבחינת אזור התצוגה שאליו הם מתייחסים, והם גם לא עקביים בדפדפנים שונים.
לדוגמה, element.getBoundingClientRect().y
מחזיר את ההיסט בתוך אזור התצוגה של הפריסה. זה מגניב, אבל הרבה פעמים אנחנו רוצים את המיקום בדף,
אז נכתוב:
element.getBoundingClientRect().y + window.scrollY
עם זאת, דפדפנים רבים משתמשים באזור התצוגה הוויזואלי עבור window.scrollY
, כלומר הקוד שלמעלה נשבר כשהמשתמש מבצע תנועת צביטה להגדלת התצוגה.
Chrome 61 משנה את window.scrollY
כדי להפנות לאזור התצוגה של הפריסה,
כלומר הקוד שלמעלה פועל גם כשמשנים את מרחק התצוגה באמצעות צביטה. למעשה, הדפדפנים משנים באיטיות את כל מאפייני המיקום כדי להפנות לאזור התצוגה של הפריסה.
מלבד נכס חדש אחד...
חשיפת אזור התצוגה החזותי לסקריפט
API חדש חושף את אזור התצוגה הוויזואלי כ-window.visualViewport
. זהו מפרט טיוטה, עם אישור חוצה-דפדפנים, והוא נוחת ב-Chrome 61.
console.log(window.visualViewport.width);
זה מה ש-window.visualViewport
נותן לנו:
visualViewport מלונות |
|
---|---|
offsetLeft
|
המרחק בין הקצה השמאלי של אזור התצוגה הוויזואלי לבין אזור התצוגה של הפריסה, בפיקסלים של CSS. |
offsetTop
|
המרחק בין הקצה העליון של אזור התצוגה הוויזואלי לבין אזור התצוגה של הפריסה, בפיקסלים של CSS. |
pageLeft
|
המרחק בין הקצה השמאלי של אזור התצוגה החזותי לבין הגבול השמאלי של המסמך, בפיקסלים של CSS. |
pageTop
|
המרחק בין הקצה העליון של אזור התצוגה החזותי לבין הגבול העליון של המסמך, בפיקסלים של CSS. |
width
|
הרוחב של אזור התצוגה החזותי בפיקסלים של CSS. |
height
|
הגובה של אזור התצוגה החזותי בפיקסלים של CSS. |
scale
|
שינוי קנה המידה לפי תנועת צביטה להגדלת התצוגה. אם התוכן גדול פי שניים
עקב שינוי מרחק התצוגה, יוחזר הערך 2 . אין לכך השפעה על
devicePixelRatio .
|
יש גם כמה אירועים:
window.visualViewport.addEventListener('resize', listener);
visualViewport אירועים |
|
---|---|
resize
|
מופעל כשwidth , height או
scale השתנו.
|
scroll
|
מופעל כשoffsetLeft או offsetTop משתנים.
|
הדגמה (דמו)
הסרטון בתחילת המאמר הזה נוצר באמצעות visualViewport
,
אפשר לראות אותו ב-Chrome 61+. הוא משתמש
ב- visualViewport
כדי שהמיני-מפה נצמדת לפינה השמאלית העליונה של אזור התצוגה
הויזואלי, ותראה תמיד קנה מידה הפוך, כך שהיא תיווצר בהתאם.
הבנתי
אירועים מופעלים רק כשאזור התצוגה הוויזואלי משתנה
זה מרגיש כמו ברור מאליו, אבל זה תפס אותי כששיחקתי לראשונה עם visualViewport
.
אם גודל אזור התצוגה משתנה, אבל אזור התצוגה החזותי לא משתנה, לא מתקבל אירוע resize
. עם זאת, לא ניתן לשנות את גודל אזור התצוגה בפריסה בלי שאזור התצוגה הוויזואלי ישנה גם את הרוחב/גובה.
העיקרון האמיתי נגלל. אם מתבצעת גלילה, אבל אזור התצוגה החזותי נשאר סטטי יחסי לאזור התצוגה של הפריסה, לא מתקבל אירוע scroll
ב-visualViewport
, וזה נפוץ מאוד. במהלך גלילה רגילה של מסמכים, אזור התצוגה הוויזואלי נשאר נעול בפינה השמאלית העליונה של אזור התצוגה של הפריסה, כך ש-scroll
לא יופעל ב-visualViewport
.
אם אתם רוצים לקבל מידע על כל השינויים באזור התצוגה החזותי, כולל pageTop
ו-pageLeft
, תצטרכו להאזין גם לאירוע הגלילה של החלון:
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);
הימנעות מכפילויות של תכנים במספר מאזינים
בדומה להאזנה ל-scroll
ול-resize
בחלון, סביר להניח שכתוצאה מכך יופעל סוג כלשהו של פונקציית "עדכון". עם זאת, הרבה מהאירועים האלה מתרחשים בו-זמנית. אם המשתמש ישנה את גודל החלון, פעולה זו תפעיל את resize
, אבל לעיתים קרובות גם scroll
. כדי לשפר את הביצועים, מומלץ להימנע מטיפול בשינוי מספר פעמים:
// Add listeners
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
addEventListener('scroll', update);
let pendingUpdate = false;
function update() {
// If we're already going to handle an update, return
if (pendingUpdate) return;
pendingUpdate = true;
// Use requestAnimationFrame so the update happens before next render
requestAnimationFrame(() => {
pendingUpdate = false;
// Handle update here
});
}
דיווחתי על בעיה במפרט, כי נראה לי שיש דרך טובה יותר, למשל אירוע update
יחיד.
הגורמים המטפלים באירועים לא פועלים
בגלל באג ב-Chrome, האפשרות הזו לא עובדת:
באגי – משתמש במטפל באירועים
visualViewport.onscroll = () => console.log('scroll!');
במקום זאת:
Works – משתמש ב-event listener
visualViewport.addEventListener('scroll', () => console.log('scroll'));
ערכי הקיזוז מעוגלים
אני חושב (טוב, אני מקווה) שזה באג נוסף ב-Chrome.
הערכים offsetLeft
ו-offsetTop
מעוגלים, והדבר די לא מדויק כשהמשתמש מגדילים את התצוגה. אפשר לראות את הבעיות בהגדרה הזו במהלך ההדגמה – אם המשתמש מגדיל את התצוגה וזז לאט, המיני-מפה מוצמדת בין פיקסלים שלא הוגדלו.
קצב האירועים איטי
כמו אירועים אחרים של resize
ו-scroll
, הם לא מופעלים בכל פריים,
במיוחד בנייד. תוכלו לראות זאת במהלך ההדגמה. ברגע שעושים תנועת צביטה על מרחק התצוגה, המיני-מפה לא מצליחה להישאר נעולה באזור התצוגה.
נגישות
בהדגמה השתמשתי ב-visualViewport
כדי לבטל את שינוי המרחק מהתצוגה באמצעות צביטה. זה הגיוני עבור ההדגמה המסוימת הזו, אבל צריך לחשוב היטב לפני שמבצעים פעולה שגורמת לרצון המשתמש להגדיל את התצוגה.
אפשר להשתמש באפליקציה visualViewport
כדי לשפר את הנגישות. לדוגמה, אם המשתמש מגדיל את התצוגה, תוכלו לבחור להסתיר פריטים דקורטיביים של position: fixed
כדי להרחיק אותם מצידם. אבל שוב, היזהרו שלא להסתיר משהו
שהמשתמש מנסה לראות מקרוב.
תוכלו לשקול לפרסם פוסט בשירות ניתוח נתונים כשהמשתמש מגדילים את התצוגה. כך תוכלו לזהות דפים שבהם המשתמשים מתקשים, ברמת ברירת המחדל של מרחק התצוגה.
visualViewport.addEventListener('resize', () => {
if (visualViewport.scale > 1) {
// Post data to analytics service
}
});
זהו, סיימתם. visualViewport
הוא API קטן ונחמד לפתרון בעיות תאימות לאורך הדרך.