גלילה מהירה של הגלגלים כברירת מחדל

Sahel Sharify
Sahel Sharify

כדי לשפר את ביצועי הגלילה או שינוי מרחק התצוגה של wheel, מומלץ למפתחים לרשום את wheel ו-mousewheel מאזינים לאירועים בתור פסיביים על ידי העברת האפשרות {passive: true} ל-addEventListener(). רישום של פונקציות event listener פסיביות מורה לדפדפן שהמאזינים לגלגלים לא יתקשרו ל-preventDefault() ושהדפדפן יכול לבצע גלילה ושינוי מרחק תצוגה בלי לחסום את המאזינים.

הבעיה היא שברוב המקרים פונקציות ה-listener של אירועי הגלגל הם פסיביים קונספטואלית (לא קוראים ל-preventDefault()) אבל לא יצוינו במפורש כך, ולכן הדפדפן צריך להמתין לסיום הטיפול באירוע JS לפני הגלילה או שינוי מרחק התצוגה, למרות שאין צורך להמתין. בגרסה 56 של Chrome תיקנו את הבעיה ב-touchstart וב-touchmove, והשינוי הזה אימץ מאוחר יותר גם ב-Safari וגם ב-Firefox. כפי שאפשר לראות בסרטון ההדגמה שהכנו באותו זמן, וההתנהגות הזו גרמה לעיכוב משמעותי בתגובת הגלילה. ב-Chrome 73 יישמנו את אותה התערבות גם באירועים של wheel וב-mousewheel.

ההתערבות

המטרה שלנו במסגרת השינוי הזה היא לקצר את הזמן שנדרש לעדכון המסך אחרי שהמשתמש מתחיל לגלול באמצעות גלגלים או לוח מגע, בלי שהמפתחים יצטרכו לשנות את הקוד. לפי המדדים שלנו, 75% ממאזיני האירועים wheel ו-mousewheel שרשומים ביעדי בסיס (חלון, מסמך או גוף) לא מציינים ערכים לאפשרות הפסיבית, ויותר מ-98% מהמאזינים האלה לא קוראים ל-preventDefault(). ב-Chrome 73, אנחנו משנים את פונקציות ה-listener של wheel ו-mousewheel שרשומים ביעדי הבסיס (חלון, מסמך או גוף) כך שיהיו פסיביים כברירת מחדל. המשמעות היא ש-event listener כמו:

window.addEventListener("wheel", func);

הופך להיות שווה ערך ל:

window.addEventListener("wheel", func, {passive: true});

המערכת תתעלם מהפעלה של preventDefault() בתוך ה-listener עם האזהרה הבאה של כלי הפיתוח:

[Intervention] Unable to preventDefault inside passive event listener due
to target being treated as passive. See https://www.chromestatus.com/features/6662647093133312

עצירה והדרכה

ברוב המקרים לא תהיה שבירה. רק במקרים נדירים (פחות מ-0.3% מהדפים לפי המדדים שלנו), גלילה או שינוי מרחק התצוגה עשויים להתרחש אם המערכת מתעלמת מהקריאה ל-preventDefault() במאזינים שמתייחסים כברירת מחדל בתור פסיבי. האפליקציה יכולה לקבוע אם היא עלולה להגיע למצב הזה בטבע על ידי בדיקה אם לקריאה ל-preventDefault() הייתה השפעה כלשהי דרך הנכס defaultPrevented. פתרון הבעיות במקרים שהושפעו הוא קל יחסית: מעבירים את {passive: false} אל addEventListener() כדי לשנות את התנהגות ברירת המחדל ולשמר את האזנה לאירועים כחסימה.