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

בוריס סמוס
בוריס סמוס

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

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

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

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

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

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

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

לאילו מחלקות של מכשירים ברצונך לטרגט?

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

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

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

יש שני קצוות קיצוניים למגוון הגישות:

  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 מיושמת שיטה של הכפלת דחיסות פיקסלים (לדוגמה, iPhone 3GS לעומת 4, iPad 2 לעומת 3). ב-Retina Mobile Safari UAs עדיין מתבצע דיווח על אותו רוחב מכשיר, כדי למנוע גלישה באינטרנט. כמו מכשירים אחרים (למשל, ל-Android) לצפייה ברזולוציה גבוהה יותר, הם מבצעים את אותו הטריק של רוחב המכשיר.

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

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

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

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

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

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

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

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

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

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

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

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

MVC בכל המכשירים.
MVC במכשירים שונים.

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

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

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

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

סיכום

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

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