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 או בשדה, והוא זמין בכלים הבאים:

כלים לשטח

כלים לשיעור Lab

מדידת שינויי פריסה ב-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 לגבי שינויים שמתרחשים בתוך מסגרות iframe, אבל המדד כן מתייחס לחוויית המשתמש בדף. זה יכול להיות הבדל בין 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

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

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

יומן שינויים

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

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

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