שני APIs של HTML5 שיחסכו סוללה לגולשים שלכם

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

אחת הדאגות של בוני אתרים, לפחות ההגונים מביניהם, היא לבנות אתר שלא ישתה לגולשים את כל הסוללה. השיפור ביכולות ה Web יחד עם המעבר לגלישה מהמובייל הופכים אתגר זה למשמעותי הרבה יותר מבעבר. לשמחתנו, שני APIs של HTML5 נכתבו כדי לעזור לנו לבנות אתרים יעילים יותר: Battery API ו Page Visibility API.

1. איך להשתמש ב Battery API

נתחיל עם הפשוט ביניהם שנקרא Battery Status API או בקיצור Battery API. מטרתו של הממשק לאפשר לקוד JavaScript לדעת מה מצב הסוללה וכך לאפשר למתכנת להוסיף יכולות לאתר אם הגולש מגיע עם סוללה מלאה או מחובר למטען.

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

הפונקציות של Battery API מספקות מידע על הסוללה כדי שגם אתם תוכלו להוסיף מעין ״מצב חסכון בחשמל״ לגולשים באתר שלכם. בואו נראה את זה בפעולה:

הקוד עצמו משתמש בפונקציה navigator.getBattery כדי לקבל את מצב הסוללה הפונקציה מחזירה הבטחה שבעת מימושה מחזירה אוביקט מסוג Battery Manager. אוביקט זה כולל את המאפיינים הבאים:

interface BatteryManager : EventTarget {
    readonly attribute boolean             charging;
    readonly attribute unrestricted double chargingTime;
    readonly attribute unrestricted double dischargingTime;
    readonly attribute double              level;
             attribute EventHandler        onchargingchange;
             attribute EventHandler        onchargingtimechange;
             attribute EventHandler        ondischargingtimechange;
             attribute EventHandler        onlevelchange;
};

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

האירועים ש Battery Manager יודע לדווח עליהם כוללים עדכון על חיבור או ניתוק מהמטען (onchargingchange), עדכון בהערכות הזמנים ועדכון על רמת הסוללה הנוכחית.

קוד הטיפול באירועים יכול לגשת לאוביקט הסוללה באמצעות event.target או באמצעות Closures.

2. דברים שמבזבזים סוללה (או: על מה כדאי לוותר)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4. תבנית לדוגמא: מצב חסכון בחשמל

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

const PowerManager = {
    addEventListener(el, event, callback),
    setInterval(callback, interval),
    enterPowerSaveMode(),
    leavePowerSaveMode(),
};

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

PowerManager.setInterval(function() {
  document.querySelector('#counter2').textContent++;
}, 1500);

ואז כשמפעילים את PowerManager.enterPowerSaveMode האינטרוול יעצור עד שנחזור למצב הרגיל. את המימוש המלא בפעולה אפשר לראות בקטע הקוד הבא:

5. איך להשתמש ב Visibility API

היכולת המשלימה של Battery API נקראת Visibility API והרעיון שלה הוא לזהות מתי גולש לא מסתכל על האתר. תגובה למצב זה ושינוי התנהגות של האתר כך שלא יבוצעו פעולות שממילא אף אחד לא יהנה מהן יכול לחסוך בחיי סוללה ובמשאבי המחשב הגולש.

6. תוכנית דוגמא: עצירת סרט כשעוזבים את הטאב

נסו לנגן את הסרטון הבא ואז עברו לטאב אחר. רואים מה קרה? זה Visibility API:

מבחינת קוד המבנה מאוד פשוט ומבוסס על הרחבה של Document באופן הבא:

enum VisibilityState { "hidden", "visible", "prerender", "unloaded" };

partial interface Document {
  readonly attribute boolean hidden;
  readonly attribute VisibilityState visibilityState; 
};

המאפיין document.hidden הוא מאפיין בוליאני שמציין אם העמוד מוצג או נסתר, והמאפיין document.visibilityState מוסיף אינדיקציה למצבים של prerender ו unloaded. הראשון מתרחש כשדפדפן טוען את העמוד במצב preload ואז הטאב פעיל אבל המשתמש עדיין לא רואה את הדף, והשני כשהמשתמש בדיוק עוזב את הדף.

האירוע document.visibilitychange מאפשר קבלת עדכון כשמצב התצוגה שלנו משתנה.

בניגוד ל Battery API, ל Visibility API אין מתנגדים ולכן נתמך בכל הדפדפנים כולל מכשירי Mobile.

7. יצירת טיימרים באמצעות הספריה visibility.js

הבעיה היחידה שכן יש ל Visibility API היא שמדובר בממשק מאוד מינימליסטי. ספריה בשם visibility.js בונה על גבי ממשק זה שתי יכולות מאוד חשובות: אפשרות הגדרת שעונים שיתקדמו רק כאשר העמוד פעיל, ואפשרות הגדרת קוד אתחול שיופעל רק כשמשתמש אכן יסתכל על תוכן החלון.

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

Visibility.every(1000, function () {
    updateCountdownAnimation();
});

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

var minute = 60 * 1000;
Visibility.every(minute, 5 * minute, function () {
    checkForEmail();
});

הקוד בודק הודעות חדשות פעם בדקה בזמן שהמשתמש מסתכל או פעם בחמש דקות כשהמשתמש מסתכל על טאב אחר.

8. הגדרת קוד אתחול באמצעות visibility.js

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

Visibility.onVisible(function () {
    startIntroAnimation();
});

9. סיכום ורעיונות להמשך

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

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