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

שאילתות מדיה הן כלי נהדר, אבל…

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

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

  • כל המכשירים מקבלים את אותם נכסי JavaScript,‏ CSS ונכסים (תמונות, סרטונים), וכתוצאה מכך זמני הטעינה ארוכים מהנדרש.
  • כל המכשירים מקבלים את אותו DOM ראשוני, ויכול להיות שהמפתחים יצטרכו לכתוב CSS מסובך מדי.
  • קשה לציין אינטראקציות בהתאמה אישית לכל מכשיר.

לאפליקציות אינטרנט נדרשים רכיבים נוספים מלבד שאילתות מדיה

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

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

לאילו סוגים של מכשירים אתם מטרגטים?

יש המון מכשירים שמחוברים לאינטרנט, ולכמעט כולם יש דפדפנים. המורכבות נובעת מהמגוון שלהם: מחשבים ניידים של Mac, תחנות עבודה של Windows, מכשירי iPhone, מכשירי iPad, טלפונים עם Android עם קלט מגע, גלילות, מקלדות, קלט קולי, מכשירים עם רגישות ללחץ, שעונים חכמים, טוסטרים ומקררים ועוד הרבה מכשירים. חלק מהמכשירים האלה נפוצים מאוד, וחלקם נדירים מאוד.

מגוון מכשירים.
מגוון מכשירים (source).

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

יש שני קצוות קיצוניים בספקטרום הגישות:

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

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

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

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

פתרון אפשרי

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

  1. מסכים קטנים + מגע (בעיקר טלפונים)
  2. מסכים גדולים + מגע (בעיקר טאבלטים)
  3. מסכים גדולים + מקלדת/עכבר (בעיקר מחשבים נייחים או מחשבים ניידים)

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

דוגמאות לאפליקציות אינטרנט ספציפיות לפורמטים שונים

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

בעולם האפליקציות הניידות, מפתחים רבים בוחרים להתאים את חוויית המשתמש שלהם לסוג המכשיר. לדוגמה, ממשק המשתמש של Flipboard ל-iPad שונה מאוד מזה של Flipboard ל-iPhone. הגרסה לנייד מותאמת לשימוש בשתי ידיים ולהעברה אופקית, והגרסה לנייד מותאמת לאינטראקציה עם יד אחת ולהעברה אנכית. אפליקציות רבות אחרות ל-iOS מספקות גם גרסאות שונות באופן משמעותי לטלפון ולטאבלט, כמו Things (רשימת משימות) ו-Showyou (סרטונים באינטרנט), שמוצגות בהמשך:

התאמה אישית משמעותית של ממשק המשתמש לטלפון ולטאבלט.
התאמה אישית משמעותית של ממשק המשתמש בטלפון ובטאבלט.

גישה מס' 1: זיהוי בצד השרת

בשרת, יש לנו הבנה מוגבלת הרבה יותר לגבי המכשיר שבו אנחנו מטפלים. כנראה שהרמז הכי שימושי שזמין הוא מחרוזת סוכן המשתמש, שסופקה דרך הכותרת User-Agent בכל בקשה. לכן, אפשר להשתמש באותה שיטה של ניחוש ב-UA. למעשה, הפרויקטים DeviceAtlas ו-WURFL כבר עושים זאת (ומספקים המון מידע נוסף על המכשיר).

לצערנו, לכל אחת מהאפשרויות האלה יש אתגרים משלה. קובץ WURFL גדול מאוד, והוא מכיל 20MB של XML, כך שיכול להיות שייגרם עומס משמעותי בצד השרת לכל בקשה. יש פרויקטים שמחלקים את ה-XML מסיבות שקשורות לביצועים. DeviceAtlas הוא לא קוד פתוח, ונדרש רישיון בתשלום כדי להשתמש בו.

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

גישה מס' 2: זיהוי בצד הלקוח

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

אנחנו צריכים להגדיר איפה עובר הקו כדי להבדיל בין מכשירי מגע קטנים לבין מכשירי מגע גדולים. מה קורה במקרים קיצוניים כמו Galaxy Note 5"? בתרשים הבא מוצגים כמה מכשירים פופולריים של Android ו-iOS (עם רזולוציות המסך התואמות). הכוכבית מציינת שהמכשיר מגיע או יכול להגיע בצפיפות כפולה. גם אם צפיפות הפיקסלים תושלם, עדיין ידווחו אותם גדלים ב-CSS.

הערה קצרה על פיקסלים ב-CSS: פיקסלים ב-CSS באתרים לנייד לא זהים לפיקסלים במסך. במכשירי iOS עם מסכי Retina החלו להשתמש בשיטה של הכפלת צפיפות הפיקסלים (למשל, iPhone 3GS לעומת 4,‏ iPad 2 לעומת 3). מזהי UA של Retina Mobile Safari עדיין מדווחים על אותה רוחב מכשיר כדי למנוע שיבושים באתר. כמו מכשירים אחרים (למשל Android) מקבלים מסכים ברזולוציה גבוהה יותר, הם משתמשים באותה תכונה של התאמה לרוחב המכשיר.

רזולוציית המכשיר (בפיקסלים).
רזולוציית המכשיר (בפיקסלים).

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

בתרשים הבא, הריבועים מייצגים את המימדים המקסימליים של כל מכשיר, כתוצאה מהשכבת-העל של קווי המתאר האנכי והאופקי (והשלמת הריבוע):

רזולוציה לאורך ולרוחב (בפיקסלים)
רזולוציה לתמונות בפורמט לאורך ולתמונות בפורמט לרוחב (בפיקסלים)

כשמגדירים את הסף ל-650px, אנחנו מסווגים את iPhone ו-Galaxy Nexus כ'מכשיר מגע קטן', ואת iPad ו-Galaxy Tab כ'טאבלט'. במקרה כזה, מכשיר Galaxy Note האנדרוגיני יסווג כ'טלפון' ויקבל את הפריסה של הטלפון.

לכן, אסטרטגיה סבירה עשויה להיראות כך:

if (hasTouch) {
  if (isSmall) {
    device = PHONE;
  } else {
    device = TABLET;
  }
} else {
  device = DESKTOP;
}

דוגמה מינימלית לגישה לזיהוי תכונות בפעולה.

הגישה החלופית היא להשתמש ב-UA sniffing כדי לזהות את סוג המכשיר. בעיקרון, יוצרים קבוצה של שיטות ניתוח נתונים (heuristics) ומתאימים אותן ל-navigator.userAgent של המשתמש. קוד מדומה נראה בערך כך:

var ua = navigator.userAgent;
for (var re in RULES) {
  if (ua.match(re)) {
    device = RULES[re];
    return;
  }
}

דוגמה לגישה לזיהוי UA בפעולה.

הערה לגבי טעינת צד לקוח

אם אתם מבצעים זיהוי UA בשרת, אתם יכולים להחליט אילו קובצי CSS,‏ JavaScript ו-DOM להציג כשאתם מקבלים בקשה חדשה. עם זאת, אם אתם מבצעים זיהוי בצד הלקוח, המצב מורכב יותר. יש כמה אפשרויות:

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

הגישה הראשונה פשוטה, והיא דורשת הפניה לכתובת אחרת כמו window.location.href = '/tablet'. עם זאת, עכשיו יתווספו למיקום פרטי סוג המכשיר, לכן מומלץ להשתמש ב-History API כדי לנקות את כתובת ה-URL. לצערנו, הגישה הזו כוללת הפניה אוטומטית, שעשויה להיות איטית, במיוחד בניידים.

ההטמעה של הגישה השנייה מורכבת הרבה יותר. צריך מנגנון לטעינה דינמית של CSS ו-JS, וייתכן (בהתאם לדפדפן) שלא תוכלו לבצע פעולות כמו התאמה אישית של <meta viewport>. בנוסף, מכיוון שאין הפניה אוטומטית, אתם תקועים עם קובץ ה-HTML המקורי שסופק. כמובן, אפשר לבצע בו שינויים באמצעות JavaScript, אבל הפעולה הזו עשויה להיות איטית ו/או לא אלגנטית, בהתאם לאפליקציה.

החלטה אם להשתמש בלקוח או בשרת

אלה ההבדלים בין הגישות:

לקוח Pro:

  • מתאים יותר לעתיד כי הוא מבוסס על גדלים/יכולות של מסכים ולא על UA.
  • אין צורך לעדכן כל הזמן את הרשימה ב-UA.

שרת Pro:

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

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

חדש: device.js

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

הרעיון הוא לספק קוד תגים ידידותי למנועי חיפוש (link rel=alternate) בחלק העליון של <head>, שמציין אילו גרסאות של האתר אתם רוצים לספק.

<link rel="alternate" href="http://foo.com" id="desktop"
    media="only screen and (touch-enabled: 0)">

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

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

המלצה: MVC עם תצוגות ספציפיות לגורם צורה

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

אני מקווה שהשתמשתם במסגרת שדומה ל-MVC, כמו Backbone,‏ Ember וכו'. אם כן, אתם מכירים את העיקרון של הפרדת תחומי אחריות, ובמיוחד את העובדה שצריך לנתק את ממשק המשתמש (שכבת התצוגה) מהלוגיקה (שכבת המודל). אם אתם לא מכירים את הנושא, תוכלו להיעזר במקורות המידע האלה בנושא MVC ובמקורות המידע האלה בנושא MVC ב-JavaScript.

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

MVC במכשירים שונים.
MVC חוצה-מכשירים

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

models/‏ (מודלים משותפים) item.js item-collection.js

controllers/‏ (בקרים משותפים) item-controller.js

versions/ (תוכן ספציפי למכשיר) tablet/ desktop/ phone/ (קוד ספציפי לטלפון) style.css index.html views/ item.js item-list.js

מבנה כזה מאפשר לכם לשלוט באופן מלא בנכסים שיועלו בכל גרסה, כי יש לכם קוד HTML, ‏ CSS ו-JavaScript בהתאמה אישית לכל מכשיר. זו תכונה חזקה מאוד, שיכולה להוביל לדרך הפשוטה והיעילה ביותר לפיתוח לאינטרנט במכשירים שונים, בלי להסתמך על טריקים כמו תמונות מותאמות.

אחרי שתפעילו את כלי ה-build המועדף עליכם, תוכלו לשרשר ולצמצם את כל קובצי ה-JavaScript וה-CSS לקובץ יחיד כדי לזרז את הטעינה. קוד ה-HTML בסביבת הייצור ייראה בערך כך (לטלפון, באמצעות device.js):

<!doctype html>
<head>
  <title>Mobile Web Rocks! (Phone Edition)</title>

  <!-- Every version of your webapp should include a list of all
        versions. -->
  <link rel="alternate" href="http://foo.com" id="desktop"
      media="only screen and (touch-enabled: 0)">
  <link rel="alternate" href="http://m.foo.com" id="phone"
      media="only screen and (max-device-width: 650px)">
  <link rel="alternate" href="http://tablet.foo.com" id="tablet"
      media="only screen and (min-device-width: 650px)">

  <!-- Viewport is very important, since it affects results of media
        query matching. -->
  <meta name="viewport" content="width=device-width">

  <!-- Include device.js in each version for redirection. -->
  <script src="device.js"></script>

  <link rel="style" href="phone.min.css">
</head>
<body>
  <script src="phone.min.js"></script>
</body>

חשוב לזכור ששאילתת המדיה (touch-enabled: 0) היא לא סטנדרטית (היא מיושמת רק ב-Firefox מאחורי קידומת של ספק moz), אבל היא מטופלת בצורה נכונה (בזכות Modernizr.touch) על ידי device.js.

שינוי הגרסה

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

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

סיכום

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

  1. בוחרים קבוצה של סוגים של מכשירים שרוצים לתמוך בהם, ואת הקריטריונים לסיווג המכשירים לקבוצות.
  2. פיתוח אפליקציית MVC עם הפרדה חזקה בין תחומי העניין, ופיצול התצוגות משאר קוד הבסיס.
  3. משתמשים ב-device.js כדי לזהות את סוג המכשיר בצד הלקוח.
  4. כשהכול מוכן, צריך לארוז את הסקריפט ואת גיליונות הסגנון בחבילה אחת לכל סוג מכשיר.
  5. אם יש בעיה בביצועים של ההפניה האוטומטית בצד הלקוח, כדאי להפסיק להשתמש ב-device.js ולהחליף לזיהוי UA בצד השרת.