Cumulative Layout Shift (CLS)

תמיכה בדפדפן

  • 77
  • 79
  • x
  • x

מקור

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

שינוי פתאומי בפריסה גורם למשתמש לאשר הזמנה גדולה שהוא התכוון לבטל.

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

ההבדלים בין אופן הפעולה של אתר שנמצא בפיתוח לבין החוויה של המשתמשים בו הופכים את הבעיה להחמרה. למשל:

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

המדד Cumulative Layout Shift (CLS) עוזר לך לטפל בבעיה הזו על ידי מדידת התדירות שבה היא מתרחשת אצל משתמשים אמיתיים.

מה זה CLS?

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

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

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

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

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

מהו ציון CLS טוב?

כדי לספק חוויית משתמש טובה, אתרים צריכים שציון ה-CLS יהיה 0.1 לכל היותר. כדי לוודא שאתם עומדים ביעד הזה עבור רוב המשתמשים שלכם, ערך סף טוב למדידה הוא האחוזון ה-75 של טעינות דפים, שמפולח בין מכשירים ניידים ומחשבים.

ערכי CLS טובים הם 0.1 או פחות, ערכים נמוכים גבוהים מ-0.25 וכל דבר שביניהם דורש שיפור
ערכי CLS טובים הם 0.1 או פחות. הערכים החלשים גדולים מ-0.25.

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

שינויי פריסה מפורטים

שינויי הפריסה מוגדרים על ידי Layout Instability API, שמדווח על רשומות layout-shift בכל פעם שרכיב גלוי בתוך אזור התצוגה משנה את מיקום ההתחלה שלו (לדוגמה, המיקום העליון והשמאלי במצב הכתיבה שמוגדר כברירת מחדל) בין שתי מסגרות. אלמנטים כאלה נחשבים לאלמנטים לא יציבים.

חשוב לזכור ששינויי פריסה מתרחשים רק כאשר רכיבים קיימים משנים את מיקום ההתחלה שלהם. אם רכיב חדש נוסף ל-DOM או שרכיב קיים משנה את הגודל שלו, הוא לא נספר כשינוי בפריסה – כל עוד השינוי לא גורם לרכיבים גלויים אחרים לשנות את מיקום ההתחלה שלהם.

הציון של שינוי הפריסה

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

layout shift score = impact fraction * distance fraction

שבר ההשפעה

שבר ההשפעה מודד את ההשפעה של רכיבים לא יציבים על אזור התצוגה בין שתי פריימים.

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

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

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

שבר מרחק

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

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

בדוגמה הקודמת, המאפיין הגדול ביותר של אזור התצוגה הוא הגובה, והרכיב הלא יציב זז ב-25% מגובה אזור התצוגה, כך שחלק המרחק יהיה 0.25.

לכן, בדוגמה הזו שבר ההשפעה הוא 0.75 ושבר המרחק הוא 0.25, כך שציון שינוי הפריסה הוא 0.75 * 0.25 = 0.1875.

דוגמאות

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

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

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

הלחצן 'לחיצה עליי!' לא היה בעבר ב-DOM, ולכן גם מיקום ההתחלה שלו לא משתנה.

עם זאת, מיקום ההתחלה של התיבה הירוקה משתנה, אבל מכיוון שהוא הועבר חלקית מאזור התצוגה, האזור המוסתר לא נלקח בחשבון בחישוב שבר ההשפעה. איחוד האזורים הגלויים של התיבה הירוקה בשתי הפריימים (המאוייר באמצעות המלבן האדום המנוקד) זהה לשטח התיבה הירוקה במסגרת הראשונה - 50% מאזור התצוגה. שבר ההשפעה הוא 0.5.

שבר המרחק מתואר באמצעות החץ הסגול. התיבה הירוקה זזה למטה בכ-14% מאזור התצוגה כך ששבר המרחק הוא 0.14.

הציון של שינוי הפריסה הוא 0.5 x 0.14 = 0.07.

הדוגמה הבאה מראה איך מספר רכיבים לא יציבים משפיעים על הציון של שינוי הפריסה של דף:

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

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

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

שוב, המלבנים האדומים המנוקדים מייצגים את האיחוד של שלושת הרכיבים הלא יציבים האלה לפני ואחרי אזורים, שבמקרה זה הם כ-60% משטח אזור התצוגה (חלק ההשפעה של 0.60).

החיצים מייצגים את המרחקים שרכיבים לא יציבים זזו ממיקום ההתחלה שלהם. הרכיב "זברה", שמיוצג על ידי החץ הכחול, זז הכי הרבה, בכ-30% מגובה אזור התצוגה. כך שבר המרחק בדוגמה הזו הוא 0.3.

הציון של שינוי הפריסה הוא 0.60 x 0.3 = 0.18.

שינויים צפויים בפריסה לעומת שינויים לא צפויים בפריסה

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

שינויי פריסה ביוזמת המשתמש

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

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

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

אנימציות ומעברים

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

חשוב לכבד את הגדרות הדפדפן של prefers-reduced-motion, כי חלק מהמבקרים באתר עלולים לחוות השפעות לא טובות או בעיות תשומת לב שנובעות מהאנימציה.

מאפיין CSS transform מאפשר לכם להוסיף אנימציה לרכיבים בלי להפעיל שינויי פריסה:

  • במקום לשנות את המאפיינים height ו-width, צריך להשתמש ב-transform: scale().
  • כדי להזיז רכיבים, אין לשנות את המאפיינים top, right, bottom או left ולהשתמש ב-transform: translate() במקום זאת.

איך מודדים CLS

אפשר למדוד את ה-CLS בשיעור ה-Lab או בשדה. ה-CLS זמין בכלים הבאים:

כלי שדה

כלי מעבדה

מדידת שינויים בפריסה ב-JavaScript

כדי למדוד את התנודות בפריסה ב-JavaScript, משתמשים ב-Layout Instability API.

הדוגמה הבאה מציגה איך ליצור PerformanceObserver כדי לרשום רשומות layout-shift במסוף:

new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('Layout shift:', entry);
  }
}).observe({type: 'layout-shift', buffered: true});

מדידת CLS ב-JavaScript

כדי למדוד CLS ב-JavaScript, צריך לקבץ את רשומות layout-shift הבלתי צפויות האלה בסשנים ולחשב את ערך הסשן המקסימלי. אפשר לעיין בקוד המקור של ספריית JavaScript web vitals שמכיל הטמעה של הפניה לגבי אופן החישוב של CLS.

ברוב המקרים, ערך ה-CLS הנוכחי בזמן הסרת הדף הוא ערך ה-CLS הסופי של אותו דף, אבל יש כמה חריגים חשובים, כפי שמצוין בקטע הבא. ספריית ה-JavaScript של web vitals מתחשבת בהם ככל האפשר, במסגרת המגבלות של ממשקי ה-API באינטרנט.

הבדלים בין המדד לבין ה-API

  • אם דף נטען ברקע, או אם הוא מוצב ברקע לפני הצגת התוכן בדפדפן, לא יתבצע דיווח על ערך CLS.
  • אם דף שוחזר מהמטמון לדף הקודם/הבא, צריך לאפס את ערך ה-CLS שלו לאפס כי המשתמשים חווים זאת כביקור נפרד בדף.
  • ה-API לא מדווח על רשומות layout-shift לתנודות שמתרחשות בתוך מסגרות iframe, אבל המדד כן מדווח מפני שהן חלק מחוויית המשתמש בדף. המידע הזה יכול להבדיל בין CrUX ל-RUM. כדי למדוד כראוי את ה-CLS צריך לשקול אותן. אפשר להשתמש ב-API במסגרות משנה כדי לדווח על רשומות layout-shift שלהן למסגרת ההורה לצורך צבירה.

בנוסף למקרים חריגים אלה, ל-CLS יש מידת מורכבות נוספת בשל העובדה שהוא מודד את כל משך החיים של דף:

  • משתמשים עשויים להשאיר כרטיסייה פתוחה למשך הרבה מאוד זמן – ימים, שבועות, חודשים. למעשה, ייתכן שמשתמש לעולם לא יסגור כרטיסייה.
  • במערכות הפעלה לנייד, דפדפנים בדרך כלל לא מפעילים קריאות חוזרות (callback) של הסרת דפים שנטענו מכרטיסיות רקע, ולכן קשה לדווח על הערך ה"סופי".

כדי לטפל במקרים כאלה, צריך לדווח על CLS בכל פעם שדף נמצא ברקע – בנוסף לכל פעם שהוא לא נטען (האירוע visibilitychange מכסה את שני התרחישים האלה). ומערכות ניתוח הנתונים שמקבלות את הנתונים האלה יצטרכו לחשב את ערך ה-CLS הסופי בקצה העורפי.

במקום לזכור בעל פה ולהתמודד עם כל המקרים האלה בעצמכם, מפתחים יכולים להשתמש בספריית JavaScript של web-vitals כדי למדוד CLS, שמביא בחשבון את כל מה שצוין למעלה, מלבד מקרה ה-iframe:

import {onCLS} from 'web-vitals';

// Measure and log CLS in all situations
// where it needs to be reported.
onCLS(console.log);

איך לשפר את ה-CLS

במדריך לאופטימיזציה של CLS יש הנחיות נוספות לגבי זיהוי שינויים בפריסה בשטח ושימוש בנתוני Lab כדי לבצע אופטימיזציה שלהם.

מקורות מידע נוספים

יומן שינויים

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

כדי לעזור לכם לנהל זאת, כל השינויים בהטמעה או בהגדרות של המדדים האלה יופיעו ביומן השינויים הזה.

אם יש לכם משוב לגבי המדדים האלה, תוכלו לשלוח אותו בקבוצת Google בנושא Web-vitals-feedback.