Cumulative Layout Shift (CLS)

Milica Mihajlija
Milica Mihajlija
Philip Walton
Philip Walton

תמיכה בדפדפנים

  • Chrome:‏ 77.
  • Edge:‏ 79.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

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

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

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

ההבדלים בין אופן התפקוד של אתר בפיתוח לבין האופן שבו המשתמשים בו חווים אותו, מחמירים את הבעיה הזו. לדוגמה:

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

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

מהו CLS?

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

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

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

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

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

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

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

ערכים טובים של 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, כי חלק מהמבקרים באתר עלולים להיתקל באפקטים לא טובים או בבעיות של תשומת הלב כתוצאה מאנימציה.

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

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

איך מודדים את ה-CLS

אפשר למדוד את CLS במעבדה או בשטח, והוא זמין בכלים הבאים:

כלים לשדה

כלי Labs

מדידת שינויי פריסה ב-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 שלו צריך לאפס את ערך ה-CLS, כי המשתמשים חווים זאת כביקור בדף נפרד.
  • ה-API לא מדווח על רשומות layout-shift לגבי שינויים שמתרחשים בתוך iframes, אבל המדד כן מדווח עליהם כי הם חלק מחוויית המשתמש בדף. ההבדל הזה עשוי להופיע כהבדל בין CrUX לבין RUM. כדי למדוד את CLS בצורה נכונה, כדאי להביא בחשבון את הגורמים האלה. מסגרות משנה יכולות להשתמש ב-API כדי לדווח על הרשומות שלהן ב-layout-shift למסגרת ההורה לצורך צבירה.

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

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

כדי לטפל במקרים כאלה, צריך לדווח על 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.

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

יומן שינויים

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

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

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