כשאופטימיזציית ביצועים מסתירה תוכן

איך content-visibility ו-reveal-on-scroll באתר עסקי הובילו לתיקון שמזכיר: ביצועים טובים חייבים להיבדק מול חוויית המשתמש בפועל.

פורסם 2026-07-12. תגיות: Performance, Frontend, UX.

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

באתר של Nis Boutique Catering נתקלתי בדיוק בנקודה הזאת. רציתי לשמור על אתר מהיר ונקי, אבל שילוב בין `content-visibility`, אנימציות reveal וטעינת תוכן דינמית יצר מצב שבו אופטימיזציה שנשמעת נכונה על הנייר הייתה עלולה לפגוע בחוויית המשתמש.

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

הבעיה: ביצועים אינם קיימים לבד

`content-visibility: auto` הוא כלי מעניין. הוא מאפשר לדפדפן לדחות רינדור של אזורים שאינם קרובים למסך, וכך לצמצם עבודה מיותרת. עבור עמודים ארוכים, זו יכולה להיות אופטימיזציה אמיתית.

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

באתר עם sections שנחשפים בגלילה, הדפדפן, ה-CSS וה-JavaScript צריכים להסכים ביניהם על דבר פשוט:

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

האבחון: לא כל אלמנט קיים בזמן שה-hook רץ

ה-hook שאחראי על ה-reveal עבד בצורה פשוטה: למצוא את כל האלמנטים עם המחלקה `.reveal`, לחבר אותם ל-IntersectionObserver, ולהוסיף `is-visible` כשהם נכנסים למסך.

הבעיה היא שהמודל הזה מניח שכל האלמנטים כבר קיימים ברגע שה-hook רץ.

באתר אמיתי, במיוחד כזה שיש בו preview, תוכן מנוהל, hydrate, או sections שנוצרים אחרי טעינה ראשונית, זו לא תמיד הנחה בטוחה. חלק מהאלמנטים יכולים להיכנס ל-DOM רק אחרי שה-hook כבר סיים לאסוף את הרשימה הראשונית.

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

התיקון הראשון: להסיר אופטימיזציה שפגעה באמון

ב-commit `bdf2ba3` הסרתי את `content-visibility: auto` ואת `contain-intrinsic-size` מה-sections ומה-footer.

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

הקוד שהוסר היה קטן:

content-visibility: auto;
contain-intrinsic-size: auto 960px;

וב-footer:

content-visibility: auto;
contain-intrinsic-size: auto 280px;

הלקח כאן פשוט: לא כל API שמבטיח פחות עבודה לדפדפן מתאים לכל מסך. באתר עסקי קטן, שבו כל section נועד לבנות אמון ולהוביל לשיחה, הסיכון של תוכן שלא מופיע בזמן חמור יותר מהרווח האפשרי במדד performance.

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

ב-commit `2d6ed14` חיזקתי את `useRevealOnScroll`.

במקום לאסוף פעם אחת את כל האלמנטים, ה-hook קיבל פונקציה שמחפשת אלמנטים בכל פעם מחדש:

const revealSelector = '.reveal';
const revealElements = () =>
  Array.from(document.querySelectorAll<HTMLElement>(revealSelector));

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

if (element.dataset.revealObserved === 'true') {
  return;
}

element.dataset.revealObserved = 'true';
observer.observe(element);

והחלק החשוב: `MutationObserver` שמאזין לשינויים ב-DOM ומריץ שוב את החיבור ל-IntersectionObserver:

const mutationObserver = new MutationObserver(() => {
  observePendingRevealElements();
});

mutationObserver.observe(document.body, {
  childList: true,
  subtree: true,
});

כך גם אם section נכנס לעמוד אחרי ההרצה הראשונית, הוא לא נשאר מחוץ למערכת ה-reveal.

למה זה עניין מוצרי, לא רק טכני

באתר עסקי, content הוא לא קישוט. גלריה, שירותים, FAQ ו-footer הם חלק מהמסלול שמוביל משתמש להבין אם הוא סומך על העסק.

אם section לא מופיע, המשתמש לא יודע שהיה שם bug. הוא פשוט מקבל פחות מידע, פחות ביטחון ופחות סיבה לפנות.

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

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

איך הייתי בודק שינוי כזה בעתיד

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

הייתי בודק:

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

מה למדתי

ביצועים הם חלק מהמוצר, אבל הם לא עומדים מעליו.

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

הכלל שאני לוקח מזה הוא פשוט:

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