Zapytania o multimedia są świetne, ale…
Zapytania o media to świetne rozwiązanie, które jest prawdziwym błogosławieństwem dla twórców stron internetowych, którzy chcą wprowadzać niewielkie zmiany w arkuszach stylów, aby zapewnić użytkownikom lepsze wrażenia na urządzeniach o różnych rozmiarach. Zapytania o multimedia umożliwiają dostosowywanie CSS witryny w zależności od rozmiaru ekranu. Zanim zaczniesz czytać ten artykuł, dowiedz się więcej o projektowaniu responsywnym i sprawdź przykłady użycia zapytań o media: mediaqueri.es.
Jak zauważa Brad Frost w poprzednim artykule, zmiana wyglądu to tylko jedna z wielu kwestii, które należy wziąć pod uwagę podczas tworzenia stron internetowych na urządzenia mobilne. Jeśli jedyną czynnością, jaką wykonujesz podczas tworzenia witryny mobilnej, jest dostosowywanie układu za pomocą zapytań o media, sytuacja wygląda tak:
- Wszystkie urządzenia otrzymują te same pliki JavaScript, CSS i komponenty (obrazy, filmy), co powoduje dłuższy niż konieczny czas wczytywania.
- Wszystkie urządzenia otrzymują ten sam początkowy DOM, co może zmuszać programistów do pisania zbyt skomplikowanych arkuszy CSS.
- Niewielka elastyczność w określaniu niestandardowych interakcji dostosowanych do każdego urządzenia.
Aplikacje internetowe potrzebują czegoś więcej niż zapytań o media
Nie zrozum mnie źle. Nie mam nic przeciwko projektowaniu responsywnemu z użyciem zapytań o media i uważam, że ma ono swoje miejsce. Niektóre z wymienionych wyżej problemów można rozwiązać za pomocą takich metod jak obrazy responsywne czy dynamiczne wczytywanie skryptów. W pewnym momencie możesz jednak dojść do wniosku, że wprowadzasz zbyt wiele drobnych zmian, i że lepiej byłoby wyświetlać różne wersje.
W miarę jak interfejsy, które tworzysz, stają się coraz bardziej złożone i zaczynasz skłaniać się ku aplikacjom internetowym jednostronicowym, będziesz chcieć bardziej dostosowywać interfejsy do każdego typu urządzenia. Z tego artykułu dowiesz się, jak wprowadzać te zmiany przy minimalnym nakładzie pracy. Ogólne podejście polega na zaklasyfikowaniu urządzenia odwiedzającego do odpowiedniej klasy urządzeń i wyświetleniu na nim odpowiedniej wersji, przy jednoczesnym maksymalizowaniu ponownego wykorzystania kodu między wersjami.
Na jakie klasy urządzeń kierujesz reklamy?
Istnieje mnóstwo urządzeń połączonych z internetem, a niemal wszystkie mają przeglądarki. Problem polega na ich różnorodności: laptopy Mac, stacje robocze z systemem Windows, iPhone’y, iPady, telefony z Androidem z obsługą dotyku, kółka przewijania, klawiatury, wpisywanie głosowe, urządzenia z czujnikiem nacisku, smartwatche, tostery i lodówki oraz wiele innych. Niektóre z tych urządzeń są powszechnie używane, a inne bardzo rzadkie.
Aby zapewnić użytkownikom dobre wrażenia, musisz wiedzieć, kim są Twoi użytkownicy i jakich urządzeń używają. Jeśli stworzysz interfejs użytkownika dla użytkownika komputera stacjonarnego z myszą i klawiaturą i udostępnisz go użytkownikowi smartfona, będzie on frustrujący, ponieważ jest przeznaczony dla innego rozmiaru ekranu i innego sposobu wprowadzania danych.
Istnieją 2 skrajne podejścia:
Utwórz jedną wersję, która będzie działać na wszystkich urządzeniach. W rezultacie ucierpi UX, ponieważ różne urządzenia mają różne wymagania dotyczące projektu.
Utwórz wersję dla każdego urządzenia, które chcesz obsługiwać. Zajmie to bardzo dużo czasu, ponieważ będziesz tworzyć zbyt wiele wersji aplikacji. Poza tym, gdy pojawi się kolejny nowy smartfon (co zdarza się mniej więcej co tydzień), będziesz musiał(-a) utworzyć kolejną wersję.
Istnieje tu podstawowy kompromis: im więcej kategorii urządzeń, tym lepsze wrażenia użytkownika, ale tym więcej pracy wymaga projektowanie, wdrażanie i utrzymywanie.
Utworzenie osobnej wersji dla każdej wybranej klasy urządzeń może być dobrym pomysłem ze względu na wydajność lub jeśli wersje, które chcesz wyświetlać na różnych klasach urządzeń, znacznie się od siebie różnią. W przeciwnym razie elastyczne projektowanie witryn jest w pełni uzasadnionym podejściem.
Potencjalne rozwiązanie
Możesz pójść na kompromis: podziel urządzenia na kategorie i zaprojektuj jak najlepsze wrażenia w każdej z nich. Wybrane kategorie zależą od produktu i użytkownika docelowego. Oto przykładowa klasyfikacja, która obejmuje popularne urządzenia z dostępem do internetu.
- małe ekrany + dotyk (głównie telefony),
- duże ekrany + dotyk (głównie tablety),
- duże ekrany + klawiatura/mysz (głównie komputery stacjonarne i laptopy);
To tylko jeden z wielu możliwych podziałów, ale w momencie pisania tego artykułu ma on duży sens. Na powyższej liście nie ma urządzeń mobilnych bez ekranów dotykowych (np. telefonów z podstawową przeglądarką czy niektórych czytników e-booków). Większość z nich ma jednak zainstalowaną nawigację klawiaturową lub oprogramowanie czytnika ekranu, które będą działać prawidłowo, jeśli witryna zostanie utworzona z uwzględnieniem ułatwień dostępu.
Przykłady aplikacji internetowych dostosowanych do konkretnych formatów
Istnieje wiele przykładów usług internetowych, które wyświetlają zupełnie różne wersje na różnych urządzeniach. Robi to wyszukiwarka Google i Facebook. W tym celu należy wziąć pod uwagę zarówno wydajność (pobieranie zasobów, renderowanie stron), jak i ogólną wygodę użytkowników.
W przypadku aplikacji natywnych wielu deweloperów dostosowuje swoje aplikacje do klasy urządzenia. Na przykład Flipboard na iPada ma zupełnie inny interfejs niż Flipboard na iPhone’a. Wersja na tablet jest zoptymalizowana pod kątem obsługi dwoma rękami i przewracania w poziomie, a wersja na telefon – pod kątem obsługi jedną ręką i przewracania w pionie. Wiele innych aplikacji na iOS ma też znacznie różniące się wersje na telefony i tablety, np. Things (lista zadań) i Showyou (wideo społecznościowe), które przedstawiamy poniżej:
Technika 1. Wykrywanie po stronie serwera
Na serwerze mamy znacznie bardziej ograniczoną wiedzę o urządzeniu, z którym mamy do czynienia. Prawdopodobnie najbardziej przydatną wskazówką jest ciąg znaków klienta użytkownika, który jest dostarczany w nagłówku User-Agent w każdym żądaniu. Dlatego w tym przypadku sprawdzi się ta sama metoda wykrywania UA. W rzeczywistości projekty DeviceAtlas i WURFL już to robią (i podają wiele dodatkowych informacji o urządzeniu).
Niestety każda z nich wiąże się z określonymi wyzwaniami. WURFL jest bardzo duży i zawiera 20 MB kodu XML, co może powodować znaczne obciążenie serwera przy każdym żądaniu. Istnieją projekty, które dzielą plik XML ze względu na wydajność. DeviceAtlas nie jest oprogramowaniem open source i wymaga płatnej licencji.
Istnieją też prostsze, bezpłatne alternatywy, takie jak projekt Detect Mobile Browsers. Wadą jest oczywiście to, że wykrywanie urządzeń będzie nieuchronnie mniej kompleksowe. Rozróżnia też tylko urządzenia mobilne i inne, a obsługa tabletów jest ograniczona i działa tylko dzięki specjalnym poprawkom.
Technika 2. Wykrywanie po stronie klienta
Dzięki wykrywaniu funkcji możemy dowiedzieć się wiele o przeglądarce i urządzeniu użytkownika. Musimy przede wszystkim ustalić, czy urządzenie ma funkcję dotykową i czy ma duży czy mały ekran.
Musimy wyznaczyć granicę, aby odróżnić małe i duże urządzenia dotykowe. A co z przypadkami skrajnymi, takimi jak 5-calowy Galaxy Note? Poniższa grafika przedstawia popularne urządzenia z Androidem i iOS nałożone na siebie (z odpowiednimi rozdzielczościami ekranu). Gwiazdka oznacza, że urządzenie jest lub może być dostępne w wersji o podwójnej gęstości. Chociaż gęstość pikseli może być podwojona, CSS nadal zgłasza te same rozmiary.
Krótka uwaga na temat pikseli w CSS: piksele CSS w internecie mobilnym nie są takie same jak piksele ekranu. Urządzenia z iOS z wyświetlaczem Retina wprowadziły praktykę podwajania gęstości pikseli (np. iPhone 3GS w porównaniu z iPhone 4, iPad 2 w porównaniu z iPadem 3). Aby uniknąć problemów z wyświetlaniem stron internetowych, ciągle zgłaszają one tę samą szerokość urządzenia. jako inne urządzenia (np. Android) mają wyświetlacze o wyższej rozdzielczości, stosują tę samą sztuczkę z szerokością urządzenia.
Decyzję tę utrudnia jednak konieczność uwzględnienia zarówno trybu pionowego, jak i poziomego. Nie chcemy ponownie ładować strony ani wczytywać dodatkowych skryptów za każdym razem, gdy zmieniamy orientację urządzenia, ale możemy chcieć inaczej renderować stronę.
Na poniższym diagramie kwadraty przedstawiają maksymalne wymiary każdego urządzenia, które powstały w wyniku nałożenia na siebie konturów w orientacji pionowej i poziomej (i uzupełnienia kwadratu):
Ustawiając próg na 650px, klasyfikujemy iPhone’a i Galaxy Nexusa jako urządzenia „smalltouch”, a iPada i Galaxy Taba jako „tablety”. Androgeniczny Galaxy Note jest w tym przypadku klasyfikowany jako „telefon” i będzie miał układ telefonu.
Rozsądna strategia może wyglądać tak:
if (hasTouch) {
if (isSmall) {
device = PHONE;
} else {
device = TABLET;
}
} else {
device = DESKTOP;
}
Zobacz w działaniu minimalny przykład podejścia opartego na wykrywaniu cech.
Alternatywnym podejściem jest wykrywanie typu urządzenia za pomocą wykrywania klienta. W zasadzie tworzysz zestaw heurystyk i dopasowujesz je do navigator.userAgent użytkownika. Pseudokod wygląda mniej więcej tak:
var ua = navigator.userAgent;
for (var re in RULES) {
if (ua.match(re)) {
device = RULES[re];
return;
}
}
Zobacz, jak działa przykładowe podejście do wykrywania UA.
Uwaga dotycząca wczytywania po stronie klienta
Jeśli wykrywasz UA na serwerze, możesz zdecydować, jakie CSS, JavaScript i DOM mają być obsługiwane, gdy otrzymasz nowe żądanie. Jeśli jednak korzystasz z wykrywania po stronie klienta, sytuacja jest bardziej złożona. Masz kilka możliwości:
- Przekierowanie na adres URL dostosowany do typu urządzenia, który zawiera wersję dla tego typu urządzenia.
- dynamicznie wczytywać komponenty dostosowane do typu urządzenia;
Pierwsze podejście jest proste i wymaga przekierowania, np.
window.location.href = '/tablet'. Do lokalizacji zostaną jednak dołączone informacje o typie urządzenia, więc możesz użyć interfejsu History API, aby wyczyścić adres URL. Niestety to podejście wiąże się z przekierowaniem, które może być powolne, zwłaszcza na urządzeniach mobilnych.
Drugie podejście jest znacznie bardziej skomplikowane we wdrożeniu. Potrzebujesz mechanizmu dynamicznego wczytywania CSS i JS, a w zależności od przeglądarki możesz nie mieć możliwości dostosowania np. <meta viewport>. Ponadto, ponieważ nie ma przekierowania, musisz używać oryginalnego kodu HTML, który został wyświetlony. Oczywiście możesz manipulować tym elementem za pomocą JavaScriptu, ale w zależności od aplikacji może to być powolne lub nieeleganckie.
Decydujący klient lub serwer
Oto wady i zalety tych podejść:
Klient Pro:
- Bardziej przyszłościowe, ponieważ opierają się na rozmiarach i możliwościach ekranu, a nie na UA.
- Nie musisz stale aktualizować listy UA.
Serwer Pro:
- Pełna kontrola nad tym, która wersja ma być udostępniana na poszczególnych urządzeniach.
- Lepsza wydajność: nie ma potrzeby przekierowywania klientów ani dynamicznego wczytywania.
Osobiście wolę zacząć od device.js i wykrywania po stronie klienta. Jeśli w miarę rozwoju aplikacji stwierdzisz, że przekierowanie po stronie klienta znacznie obniża wydajność, możesz łatwo usunąć skrypt device.js i wdrożyć wykrywanie UA na serwerze.
Przedstawiamy device.js
Device.js to punkt wyjścia do semantycznego wykrywania urządzeń na podstawie zapytań o media bez konieczności specjalnej konfiguracji po stronie serwera, co pozwala zaoszczędzić czas i wysiłek potrzebny do analizowania ciągu klienta użytkownika.
Chodzi o to, aby u góry <head> umieścić znacznik przyjazny dla wyszukiwarek (link
rel=alternate), który wskazuje, które wersje witryny chcesz udostępniać.
<link rel="alternate" href="http://foo.com" id="desktop"
media="only screen and (touch-enabled: 0)">
Następnie możesz samodzielnie wykrywać klienta użytkownika po stronie serwera i obsługiwać przekierowanie wersji lub użyć skryptu device.js do przekierowania po stronie klienta na podstawie funkcji.
Więcej informacji znajdziesz na stronie projektu device.js oraz w fałszywej aplikacji, która używa device.js do przekierowania po stronie klienta.
Zalecenie: MVC z widokami dostosowanymi do formatu
Pewnie myślisz teraz, że namawiam Cię do stworzenia 3 zupełnie odrębnych aplikacji, po jednej na każdy typ urządzenia. Nie! Udostępnianie kodu jest kluczowe.
Prawdopodobnie używasz frameworka podobnego do MVC, np. Backbone, Ember itp. Jeśli tak jest, znasz zasadę rozdzielenia odpowiedzialności, a w szczególności wiesz, że interfejs użytkownika (warstwa widoku) powinien być oddzielony od logiki (warstwy modelu). Jeśli nie znasz tego pojęcia, zapoznaj się z tymi materiałami na temat MVC i MVC w JavaScript.
Historia na różnych urządzeniach dobrze pasuje do istniejącej struktury MVC. Możesz łatwo przenieść widoki do osobnych plików, tworząc niestandardowy widok dla każdego typu urządzenia. Następnie możesz wyświetlać ten sam kod na wszystkich urządzeniach z wyjątkiem warstwy widoku.
Twój projekt może mieć taką strukturę (oczywiście możesz wybrać strukturę, która najlepiej pasuje do Twojej aplikacji):
models/ (modele udostępnione) item.js item-collection.js
controllers/ (shared controllers) item-controller.js
wersje/ (elementy specyficzne dla urządzenia) tablet/ desktop/ phone/ (kod specyficzny dla telefonu) style.css index.html views/ item.js item-list.js
Taka struktura umożliwia pełną kontrolę nad tym, jakie komponenty wczytuje każda wersja, ponieważ w przypadku każdego urządzenia masz niestandardowy kod HTML, CSS i JavaScript. Jest to bardzo przydatne i może prowadzić do najbardziej wydajnego sposobu tworzenia aplikacji internetowych na różne urządzenia bez polegania na sztuczkach takich jak obrazy adaptacyjne.
Po uruchomieniu ulubionego narzędzia do kompilacji połączysz i zminimalizujesz wszystkie pliki JavaScript i CSS w jeden plik, aby przyspieszyć wczytywanie. Produkcyjny kod HTML będzie wyglądać mniej więcej tak (w przypadku telefonu, z użyciem 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>
Pamiętaj, że zapytanie o multimedia (touch-enabled: 0) jest niestandardowe (zostało zaimplementowane tylko w Firefoxie za pomocą prefiksu dostawcy moz), ale jest prawidłowo obsługiwane (dzięki Modernizr.touch) przez device.js.
Zastąpienie wersji
Wykrywanie urządzeń może czasami działać nieprawidłowo, a w niektórych przypadkach użytkownik może woleć wyświetlać układ tabletu na telefonie (np. jeśli używa Galaxy Note). Dlatego ważne jest, aby dać użytkownikom możliwość wyboru wersji witryny, której chcą używać, jeśli chcą ręcznie zastąpić ustawienia.
Zwykle w takiej sytuacji umieszcza się w wersji mobilnej link do wersji na komputery. Jest to dość łatwe do wdrożenia, ale device.js obsługuje tę funkcję za pomocą parametru GET device.
Podsumowanie
Podsumowując, podczas tworzenia interfejsów jednostronicowych na różne urządzenia, które nie pasują do koncepcji elastycznego projektowania, wykonaj te czynności:
- Wybierz zestaw klas urządzeń, które mają być obsługiwane, oraz kryteria klasyfikacji urządzeń.
- Twórz aplikacje MVC z wyraźnym podziałem na poszczególne elementy, oddzielając widoki od reszty kodu.
- Użyj device.js, aby wykrywać klasę urządzenia po stronie klienta.
- Gdy wszystko będzie gotowe, spakuj skrypt i arkusze stylów w jednym pliku dla każdej klasy urządzenia.
- Jeśli wydajność przekierowania po stronie klienta jest problemem, zrezygnuj z device.js i przejdź na wykrywanie UA po stronie serwera.