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

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

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

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

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

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

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

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

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

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

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

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

יש שתי גישות קיצוניות:

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

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

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

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

פתרון אפשרי

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

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

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

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

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

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

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

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

בשרת, ההבנה שלנו לגבי המכשיר שאיתו אנחנו מתמודדים מוגבלת הרבה יותר. האות הכי שימושי שזמין הוא כנראה מחרוזת סוכן המשתמש, שמסופקת דרך כותרת סוכן המשתמש בכל בקשה. לכן, אותה שיטה לזיהוי 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). סוכני המשתמש של Safari לנייד עם מסך Retina עדיין מדווחים על אותו רוחב מכשיר כדי למנוע שיבוש של האינטרנט. במכשירים אחרים (למשל ‫Android) יש מסכים ברזולוציה גבוהה יותר, הם משתמשים באותו טריק של רוחב המכשיר.

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

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

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

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

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

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

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

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

הגישה החלופית כאן היא להשתמש ב-UA sniffing כדי לזהות את סוג המכשיר. בעצם, אתם יוצרים קבוצה של היוריסטיקות ומשווים אותן ל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 הוא נקודת התחלה לביצוע זיהוי סמנטי של מכשירים שמבוסס על שאילתות מדיה, בלי צורך בהגדרה מיוחדת בצד השרת. כך אפשר לחסוך את הזמן והמאמץ שנדרשים לניתוח של מחרוזת user agent.

הרעיון הוא לספק תגי עיצוב ידידותיים למנועי חיפוש (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 עם תצוגות ספציפיות לגורם צורה

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

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

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

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

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

models/ (shared models) item.js item-collection.js

controllers/ (shared controllers) item-controller.js

versions/ (device-specific stuff) tablet/ desktop/ phone/ (phone-specific code) 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 בצד השרת.