Cumulative Layout Shift (CLS)

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

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

שבר של מרחק

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

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

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

לכן, בדוגמה הזו, הערך של impact fraction הוא 0.75 והערך של distance fraction הוא 0.25, כך שlayout shift score הוא 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 לאפס כי המשתמשים חווים את זה כביקור נפרד בדף.
  • ה-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.