Professional Documents
Culture Documents
CH - Hudson, T.leadbetter - HTML5. Podręcznik Programisty (Ocr Z Reflow)
CH - Hudson, T.leadbetter - HTML5. Podręcznik Programisty (Ocr Z Reflow)
ISBN: 978-83-246-4966-2
Authorized translation from the English language edition, entitled: HTML5 DEVELOPER'S
COOKBOOK; ISBN 0 321769 384; by Chuck Hudson and Tom Leadbetter; published by
Pearson Education, Inc, publishing as Addison Wesley.
Copyright © 20 12 Pearson Education, Inc.
Ali rights reserved. No part of this book may by reproduced or transmitted in any form
or by any means, electronic or mechanical, including photocopying, recording or by any
information storage retrieval system, without permission from Pearson Education, Inc.
Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce
informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich
wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich.
Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne
szkody wynikłe z wykorzystania informacji zawartych w książce.
Wydawnictwo HELION
ul. Kościuszki 1c,
44- 100 GLIWICE
3223 1 22 19, 32230 98 6 3
tel.
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl!user/opinie/htm15p
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Printed in Poland.
Wstęp . ......................................................................................................13
Podziękowania . .........................................................................................21
O autorach . ...............................................................................................23
H IM L
(ang. Hypertext Markup Language) j est podstawowym językiem służącym do tworzenia
i budowania witryn internetowych. Używany przez programistów od ponad 20 lat, przez
pierwszych kilka był poddawany radykalnym zmianom, ale w końcówce minionego stulecia jego rozwój
nieco się spowolnił. Aż do teraz.
XHTML
Po publikacji HTML 4 organizacja World Wide Web Consortium (W3C) zdecydowała się na wstrzymanie
rozwoju HTML-a i zajęła się pracą nad opartym o XML językiem XHTML (ang. Extensible Hypertext
Markup Language), który był uważany za przyszłość internetu. W XHTML-u nie było nowych elementów
- specyfIkacja była dokładnie taka sama jak w przypadku HTML 4, ale programiści mieli stosować się do
nowych reguł składniowych: zamykania znaczników i umieszczania wartości atrybutów w cudzysłowach.
Oznaczało to bardziej ścisłe, sztywne standardy kodowania i wymuszało na programistach stosowanie tego
samego stylu pisania kodu. Mniej więcej w tym samym czasie, w 2001 roku, coraz ważniejsze i popularniejsze
zaczęły się stawać kaskadowe arkusze stylów (CSS), a wraz z upowszechnieniem się blogowania wzrosło
znaczenie standardów sieciowych.
Począwszy od 2002 roku, W3C publikowało szkice XHTML 1 . 1 . Podczas gdy XHTML 1 był w gruncie
rzeczy HTML-em z odrobiną XML-a, XHTML 1 . 1 był właściwie XML-em. Chociaż miał być standardem
14 Wstę p
przyszłościowym, nie był kompatybilny wstecz - jeżeli był używany do tworzenia witryny, nie działała
ona w starszych przeglądarkach.
Wywołało to niepokój w społeczności sieciowej, u producentów przeglądarek, a nawet w samym W3e.
Nie każdy był przekonany, że XML jest przyszłością języka znaczników w sieci.
Podstawowa specyfIkacja HTMLS jest dokładnie taka - podstawowa. Nastąpiła istotna zmiana mająca
na celu dopasowanie się do szybkości, z jaką rozwija się technologia. Nowe, dodatkowe specyfikacje są
przez cały czas opracowywane - zarówno przez zespoły w WHATWG, j ak i w W3C - i dotyczą różnych
kwestii, takich jak informacj e o połączeniach sieciowych czy urządzeniach rejestrujących obraz. Producenci
przeglądarek współpracują, by pomóc zdefIniować te funkcjonalności i przyspieszyć ich włączenie do swoich
przeglądarek. Następne lata będą na pewno niezwykle ekscytujące.
Założenia HTML5
HTMLS utworzono tak, aby obsługiwał istniej ącą treść, czyli innymi słowy, jest on kompatybilny wstecz.
Główną przeszkodą w pracach nad specyfIkacją XHTML 2 była obawa przed nieprawidłowym wyświetlaniem
większości witryn. HTMLS został zbudowany na fundamentach HTML 4, tak aby przeglądarki mogły nadal
obsługiwać HTML - nie tylko nowe elementy HTMLS, ale wszystko to, co znajduje się w HTML 4. Witryny,
które działają teraz w HTML 4 lub XHTML, powinny prawidłowo działać w HTMLS.
Używanie HTMLS oznacza, że możesz nadal kodować w stylu, z którego korzystałeś wcześniej . Przy
projektowaniu HTMLS wzięto pod uwagę głosy programistów, więc nadal możesz używać składni HTML
lub XHTML, a przeglądarki będą wiedziały, co robić. Opiszemy to dokładniej w rozdziale 1 .
HTMLS wskazuje również przeglądarkom, jak powinny reagować na błędy spowodowane przez
nieprawidłową implementację znaczników. Wcześniej przeglądarki interpretowały błędy samodzielnie,
a więc każda przeglądarka miała własne standardy zachowania. HTMLS utworzono dla programistów
takich jak my oraz dla producentów przeglądarek, tak aby w niezbyt odległej przyszłości wszyscy pracowali
według tych samych standardów.
Być może prawdziwą siłą HTMLS j est to, j ak odpowiada na potrzeby programistów aplikacji
internetowych. Ze względu na coraz większe możliwości przeglądarek możemy tworzyć przypominaj ące
aplikacje witryny, które umożliwiają współdzielenie zdjęć, rysowanie, edycję plików oraz wykonywanie
innych zadań. Wcześniej funkcje te wymagały warstw J avaScriptu oraz wtyczek, takich jak J ava lub Flash
- oznaczało to problemy z dostępnością oraz zależność od stabilności zewnętrznego oprogramowania.
HTMLS daje nam nowe standardy dotyczące tworzenia aplikacji internetowych z posiadającymi ogromne
możliwości API dla takich dziedzin j ak obszary, na których można rysować, przeciąganie i upuszczanie,
lokalne przechowywanie danych oraz bezpośrednia obsługa wideo w przeglądarce. Wraz z wyspecyfIkowaniem
standardów przeglądarki za j akiś czas będą obsługiwać te funkcj e prawidłowo oraz w stabilny sposób .
Społeczność sieciowa będzie pomagać w rozwijaniu standardów, stale je weryfIkując i ulepszając, a programiści
nie będą musieli używać sztuczek umożliwiających działanie tych funkcji.
• rysowanie na płótnie,
• funkcja "przeciągnij i upuść",
• lokalne przechowywanie danych,
• przesyłanie wiadomości między stronami,
• powiadomienia na pulpicie,
• audio i wideo,
• gniazda sieciowe,
• geolokalizacja,
• historia,
• mikro dane.
Chociaż, ściśle rzecz biorąc, geolokalizacj a nie j est częścią specyfikacji HTMLS, stanowi przydatną,
nową technologię rozwijaną równolegle, w związku z czym mówi się o niej w kontekście HTMLS. A zatem
tak, my również będziemy mówić o geolokalizacji w tej książce.
Nie wszystko, co w programowaniu internetowym nowe, j est związane z HTMLS. CSS3 nie j est tym
samym co HTMLS, ale ponieważ jest nowy i niezwykle popularny, j est zaliczany do tej samej kategorii. CSS
jest zupełnie innym językiem i technologią niż HTML. CSS dotyczy prezentacji; HTML - struktury. Możesz
zrobić kilka całkiem fantastycznych rzeczy przy użyciu CSS3, ale programiści i społeczność powinni być
świadomi tego, że istnieje między tymi językami różnica.
A chociaż nie chcemy włączać się do debaty na temat tego, czy HTMLS jest zabój cą Flasha (nie jest),
istotą HTMLS jest to, że istnieje teraz ogromna ilość funkcji bezpośrednio wbudowanych w przeglądarkę
i wszystkie one należą do standardu.
HTML
Rysunek W. l. Logo HTML5 (logo HTML5 jest autorstwa organizacji W3C, www.w3. org)
o c
Rysunek W. 2 . Mniejsze ikony reprezentujące różne technologie (od lewej do prawej):
dostęp do urządzeń, 30, grafika i efekty, CSS3, semantyka, multimedia, łączność,
wydajność i integracja, lokalne przechowywanie danych
(te ikony również są autorstwa organizacji W3C, www.w3. org)
Uwaga
Niektóre kom ponenty HTML5, takie jak j avascri ptowe API , są wciąż do pracowywane w specyfikacjach
i i m plementowane w różnych przeglądarkach . W tej ks i ążce s k u p i l iśmy się na elementach , które
są dobrze wys pecyfi kowane oraz obsługiwane przez co naj m n iej jedną spośród naj popu larn iejszych
przeglądare k . Na leży j e d n a k pam iętać o tym , że H T M L5 jest stale rozbudowywany o nowe dod atki
oraz fun kcj e . W rozdziale 15. omówil iśmy niektóre spośród możl iwości i nowych fun kcj i dotyczących
integracj i urządze ń .
18 Wstę p
Zaprezentowane w książce przepisy zostały podzielone na trzy kategorie: dla początkujących, średnio
zaawansowanych oraz zaawansowanych. Kategorie te służą zobrazowaniu trudności omawianego tematu
i jednocześnie stanowią podstawę oszacowania wysiłku i czasu, który musisz przeznaczyć na ukończenie
przepisu. To oczywiście kategorie subiektywne - wiemy, że czas i wysiłek dla różnych czytelników mogą
być zupełnie różne.
Zewnętrzne b iblioteki
Jeśli kiedykolwiek byłeś zaangażowany w tworzenie stron internetowych, jedną z pierwszych rzeczy, którą
zauważysz w przepisach z niniej szej książki, j est znikoma liczba odwołań do j avascriptowych bibliotek
zewnętrznych, takich jak choćby j Query. Jest kilka drobnych wyjątków od tej reguły, kiedy staramy się
przezwyciężyć określone przeszkody przy użyciu zewnętrznych bibliotek. Jednak ogólnie rzecz biorąc,
próbowaliśmy się skupić na rdzeniu technologii HTMLS, gdyż każdy czytelnik będzie miał swój własny
zbiór ulubionych bibliotek, a każda biblioteka będzie miała swoje własne metody obsługi funkcji HTMLS
i integracji z nimi.
Mocno wierzymy, że biblioteki odgrywają istotną rolę w codziennym projektowaniu stron internetowych
oraz rozwijaniu witryn i aplikacji. Sami również mamy ulubione biblioteki. W wielu przypadkach wykorzystanie
funkcji HTMLS w bibliotekach, które j uż takie funkcj e udostępniaj ą, przypomina ich użycie w czystym
HTMLS. A zatem zrozumienie tego, j ak używać komponentu w zwykłym JavaScripcie, pozwoli Ci łatwiej
wykorzystać komponent w bibliotece, którą wybrałeś.
Wspólne podziękowania
Dziękujemy Trinie MacDonald i wydawnictwu Pearson za postawienie na nas i nieustającą cierpliwość
związaną z naszymi niekończącymi się próbami ulepszania tekstu. Dziękujemy redaktorom, Michaelowi
Thurstonowi, Evanowi Burchardowi, Timowi Wrightowi, Siddharthowi Ramowi i Kimowi Wimpsettowi,
za rady i szczegółowe oraz wnikliwe uwagi, a także za dostrzeganie rzeczy, których sami nigdy byśmy nie
zauważyli. Wielokrotnie pozwoliliście nam dostrzec zza drzew las, a efekt końcowy jest dzięki temu znacznie
lepszy. Dziękuj emy za cały poświęcony czas i wysiłek. Wiemy, że było to spore wyzwanie, które łączyło się
z poświęceniem.
Na końcu chcielibyśmy podziękować całej społeczności HTMLS za dzielenie się swoją wiedzą oraz Tobie,
czytelniku, za bycie pionierem w prawdziwie ekscytuj ących czasach. Mamy nadzieję, że będziesz czerpał tyle
radości z czytania tej książki, ile my mieliśmy przy jej tworzeniu.
o autorach
Chuck Hudson od lat dziewięćdziesiątych zajmuje się pisaniem oprogramowania w branżach związanych
z siecią i urządzeniami przenośnymi. Jest odnoszącym sukcesy przedsiębiorcą. Dzięki swoj ej pasji do
rozwiązywania problemów biznesowych za pomocą technologii przeprowadzał na rzecz firm konsultacje
związane z różnymi technologiami sieciowymi oraz wygłaszał prezentacje na rozmaitych konferencjach.
Będąc stale szkolącym się maniakiem komputerowym, Hudson jest również certyfikowanym programistą
PHP, deweloperem PayPal oraz nauczycielem programowania internetowego, technologii mobilnych
i przedsiębiorczości w okolicach Bostonu i Atlanty. W 2008 roku otrzymał nagrodę eBay Star Developer
za pierwsze internetowe i natywne aplikacje przeznaczone na platformę iOS.
Tom LeadbeUer j est proj ektantem stron internetowych i programistą z Liverpoolu (Wielka Brytania).
Od 2005 roku działa w branży internetowej na rzecz różnych organizacji i klientów, a od początku 2009
roku eksperymentuj e z HTML5, na którego temat bloguje na witrynie HTML5Doctor.com.
24 O autorach
1
Nowe elementy strukturalne
w HTML5
W
HTMLS nie chodzi tylko o interaktywną magię związaną z j avascriptowymi API i wyświetlaniem
filmów. Przy tworzeniu stron internetowych możesz używać ponad 20 nowych elementów, które
wzbogacają semantykę i pozwalają tworzyć bardziej dostępne i łatwiejsze do ponownego wykorzystania treści.
W kolejnych rozdziałach poznasz nowe kontrolki formularzy oraz elementy HTMLS związane
z multimediami. W tym rozdziale dowiesz się o nowych elementach strukturalnych: header, hgroup, nav,
footer, art; c l e, sect ; on oraz as; de
- skupimy się na tym, jak, kiedy i dlaczego używać tych elementów,
czy to osobno, czy łącznie. Ś ciśle rzecz biorąc, zbudujesz prosty szablon strony internetowej zawierającej
nowe elementy, taki jak pokazany na rysunku 1 . 1 .
<heade r >
<nav>
< s ec t i on>
< s ec t i on>
< f o o t er >
d octype
Czy to wygląda znajomo?
< ! OOCTYPE html PUBLIC " -jjW3CjjOTO XHTML 1 . 0 Stri ctjjEN " '' http : //www . w3 . org/TR/xhtml 1/OTO/xhtml 1 -stri c t . dtd''>
Na samym początku, w pierwszym wierszu dokumentu HTML, powinna się znaleźć wymagana przez
standardy sieciowe definicja typu dokumentu (ang. Document Type Definition, DTD), czyli element
doctype. Informuje on przeglądarkę, jak przetwarzać dokument, i właśnie dlatego powinien być pierwszym
fragmentem pliku HTML. Gdybyś nie użył definicji doctype lub umieścił przed nią jakikolwiek inny kod,
przeglądarka zadziałałaby w tzw. trybie osobliwości i być może w niektórych przeglądarkach napisany przez
Ciebie kod nie zostałby prawidłowo zinterpretowany.
Nie sądzę, abyś chciał zapamiętywać podany doctype, bo niby z jakiej racji? Jest okropny i niezgrabny.
W HTMLS masz teraz ładny i łatwy do zapamiętania doctype:
< ! OOCTYPE html >
I
Uwaga
Pod adresem http:;jinfomesh.netjhtm/jhistoryjearlyj możesz odnaleźć najwcześniejszy dokument
HTML z 13 l istopada 1990 roku . Użyty kod znaczn i ków jest n aprawdę prosty, co przypo m i n a nam
doctype z HTML5. Prawdę mówiąc, jeśli dodałbyś do tej strony nowy doctype, przeszłaby ona wa lidację .
To łatwe! Pamiętaj, że potrzebuj esz definicji doctype oraz powyższej deklaracji na swojej stronie.
Budowa startowego dokumentu HTML5 27
Czy zastanawiasz się, dlaczego możesz tak zrobić? Cóż, jedną z intencji autorów HTMLS było umożliwienie
kodowania w sposób bardziej zdroworozsądkowy. Jeśli więc umieszczasz łącze do skryptu, przeglądarka
zakłada, że jest to plik javascriptowy, a jeśli używasz re l = "styl esheet", oznacza to, że podłączasz plik CSS-owy.
I nie martw się, pominięcie atrybutu type nie powoduje żadnych problemów w starszych przeglądarkach.
Wszystkie te sposoby są dozwolone, jednak stanowczo zaleca się, aby wybrać jakiś styl i konsekwentnie
się go trzymać. Będzie to użyteczne nie tylko dla Ciebie, ale także dla innych programistów, którzy mogą
być kiedyś zmuszeni do używania Twojego kodu. Styl składni powinien być spójny. Mamy doświadczenie
związane z XHTML-em, więc w tej książce zamykamy wszystkie tagi i używamy małych liter oraz znaków
cudzysłowu w atrybutach.
Zbierając razem to wszystko, o czym powyżej napisaliśmy, utwórzmy stronę startową w HTMLS
- taką jak pokazana na listingu 1 . 1 .
T o wszystko! Zapisz stronę jako plik .htm (lub .html) - od teraz możesz j uż rozpocząć wypełnianie
jej wspaniałą treścią.
28 Rozdział 1. Nowe e lementy stru kturalne w HTML5
Wskazówka
Wa l id acja jest bardzo użytecznym n arzędziem sprawdza n i a , d l aczego coś może n ie działać,
a wykorzystywanie jej w procesie rozwoju oprogramowa n i a jest n iezwykle korzystne . Pon ieważ jed nak
HTML5 jest nadal rozwij any, nie udostępnia żadnych oficj a l nych usług walidacyj nych . Walidator W3C ,
http://va/idator. w3.org/, s prawdza zgodność kodu z HTML5, ale ostrzega przy tym , że jest to fun kcj a
wyłącznie eksperymenta l n a . In nym wa lidatore m , którego możesz używać do testowan ia swoich stron ,
jest http://htm/5. va/idator.nuj. Warto testować swoje strony w o b u wa lidatorach .
Chociaż kilka z tych nazw dotyczy prezentacji (na przykład wh ; te, s tyl el, mson orma l ), inne stanowią
elementy zawarte w specyfIkacji HTML5 (footer, nav, header).
Po co zatem używać tych nowych elementów? Cóż, HTML5 pozwala Ci nadać tworzonej treści znaczenie
(semantykę), więc jeśli na przykład Twoj a strona zawiera elementy nawigacji, możesz użyć elementu nav,
ponieważ ten element nadaje znaczenie swojej treści.
Wykorzystanie e lementu header do utworzen i a nagłówka witryny 29
Jak mówi specyfikacja HTMLS, header może zawierać elementy pomagaj ące w nawigacji, więc zawartość
rysunku 1.2 mogła zostać określona przy użyciu elementu header, który obejmuje logo, główne łącza nawigacji
oraz formularz wyszukiwania. Mogłoby się także okazać, że musiałeś umieścić element nav poza elementem
header, co również jest dopuszczalne.
Rysunek 1 . 2 . Typowy element header zawierający tytuł witryny. logo oraz obszary
związane z wyszukiwaniem i nawigacją
Poniższa lista zawiera treści, które mogą się znajdować w elemencie header. Niektóre z nich są pokazane
na rysunku 1 .2:
• logo,
• nazwa/tytuł witryny,
• podtytuł witryny,
• formularz wyszukiwania,
• główne elementy nawigacji.
Nie istnieje ograniczenie do j ednego elementu header na stronie i nie musi się on znajdować na górze
strony. Jak wyjaśnimy szczegółowo później, jeśli Twoja strona zawiera kilka nagłówków, możesz rozważyć
ich umieszczenie w elemencie header. Możesz również użyć więcej niż jednego znacznika h l na stronie
i w rezultacie otrzymać stronę podobną do tej z listingu 1 .2 (o elemencie art; c l e przeczytasz w dalszej części
tego rozdziału) .
30 Rozdział 1. Nowe e lementy stru kturalne w HTML5
<art i e l e>
<header>
<h l><a href= " # " >Rozdz i a ł 1 . <ja><jhl>
<p> 1 1 . 1 1 . 2 0 1 1<jp>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas .
�Ves t i bul um tortor quam, feugi at v i tae, ul tr i e i es ege t , tempor s i t amet , ante . . . . <jp>
<jart i e l e>
<art i e l e>
<header>
<h l><a href= " # " >Rozdz i a ł 2 . <ja><jhl>
<p> 12 . 1 1 . 2 0 1 1<jp>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas .
�Ves t i bul um tortor quam, feugi at v i tae, ul tr i e i es ege t , tempor s i t amet , ant e . <jp>
<jart i e l e>
<art i e l e>
<header>
<h l><a href= " # " >Rozdz i a ł 3 . <ja><jhl>
<p> 13 . 1 1 . 2 0 1 1<jp>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas .
�Ves t i bul um tortor quam, feugi at v i tae, ul tr i e i es ege t , tempor s i t amet , ant e . <jp>
<jart i e l e>
Rozdział 1 .
1 1 . 1 1 .2 0 1 1
Pellentesque habitant morhi tristique senectus et netus e t ma1esuada fames a c turpis egestas . Vestibulum tortor
quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean
ultric ies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet es! et sapien ullamcorper pharetra. Vesribulum
erat \visi, condimenrum sed: conunodo vitae, omare sit amer: \:v1.si. Aenean fennentum, elit eget tinciclunt
condimentum, eros ipsum rutrom orei, sagittis tempus lacus enim ac duI. Danec non enim in turpis pulvinar facilisis.
Ut felis. Praesent dapibus, neque id cursus faucibus� tortor neque egestas augue. eu vulputate magna eros eu erat.
Aliquam erat volutpat. Nam clui mi, tinciclunt quis, accumsan pomitor, facilisis luctus, metus
Rozdział 2 .
12. 1 1 .20 1 1
Pellentesque habitant morbi tristique. senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor
quam. feugiat vitae., ultricies eget, tempor sit amet, ante .
Rozdział 3.
1 3 . 1 1 .2 0 1 1
Pellentesque habitant morbi tristique. senectus et netus e t malesuada fames a c turpis ege stas . Vestibulum tortor
quam, feugiat vitae., ultricies eget, tempor sit amet, ante.
Rysunek 1.3. Wiele elementów header na jednej stronie (nie nadawano stylów)
Tworzenie n awigacj i przy użyciu e lementu n av 31
W obrębie elementu header mógłbyś umieścić również autora i datę, jednak specyfikacja HTMLS sugeruje,
że informację o autorze lepiej zamieścić w elemencie footer.
Jeśli w elemencie header znajduje się tylko jeden nagłówek (h l - h6), nie ma potrzeby, by używać tego
elementu; wystarczy samodzielny znacznik h l - h6.
<header>
<hgroup>
<h l><a href= " # " >HTML5 . Podręczn i k program i s ty<ja><j hl>
<h2>Pyszne przepi sy HTML5<jh2>
<jhgroup>
<jheader>
Na listingu 1 .3 treść elementu h2 nawiązuje do elementu h l, więc w tym przypadku możesz użyć hgroup.
Jeśli miałbyś pojedynczy nagłówek hl - h6, nie musiałbyś używać hgroup.
Gdy używamy elementów n a v w HTML5, k o d niewiele się zmienia. Kod dający efekt widoczny
na rysunku 1.5 jest mniej więcej taki jak pokazany na listingu 1 .5.
<nav>
<ul >
<l i ><a href= " # " >Strona gfówna<ja><jl i >
<l i ><a href= " # " >O w i t ryni e<ja><jl i >
<l i ><a href= " #" > Poznaj zespóf<ja><jl i >
<l i ><a href= " # " >Wi adomości <ja><jl i >
< l i ><a href= " #" > Konta kt<ja><jl i >
<jul >
</nav>
._----------------------------------------------------------------------------------------------------------------------
J ak pokazano na listingu 1 .6, nav może się również znaj dować w elemencie h eader, ponieważ header
pozwala na umieszczanie w nim treści wprowadzającej i nawigacyjnej. Jednak treść ta nie musi się znajdować
w tym elemencie, a jej położenie może czasem zależeć od kwestii związanych ze stylami. Dość typową
praktyką jest również umieszczanie menu nawigacyjnego w stopce strony i duplikowanie w ten sposób
głównego mechanizmu nawigacji po witrynie.
<header>
<h l>Moja superwi tryna HTML5<jh l>
<nav>
<ul >
<l i ><a href= " # " >Strona gfówna<ja><jl i >
<l i ><a href= " # " >O w i t ryni e<ja><jl i >
<l i ><a href= " # " >Wi adomości <ja><jl i >
< l i ><a href= " #" > Konta kt<ja><jl i >
<jul >
<jnav>
<jheader>
Tworzenie n awigacj i przy użyciu e lementu n av 33
Umieszczanie wszystkich łączy ze strony w elemencie nav nie jest konieczne. Specyfikacja HTMLS ostrzega,
że za właściwe uważane jest tylko umieszczanie w elemencie nav bloków "głównej nawigacji".
W witrynach z wiadomościami lub blogami powszechnym widokiem jest boczny pasek zawierający łącza
do artykułów i innych stron. Znaczniki z listingu 1 .7 wykorzystano do uzyskania elementu z rysunku 1 .6.
<nav>
<h2>Udostępni one<jh2>
<ul >
<l i ><a href= " # " >Pel l entesque hab i tant<ja><jl i >
<l i ><a href= " # " >Morbi tri s t i que senectus<ja><j l i >
<l i ><a href= " # " >Aenean u l t ri c i es mi v i tae est<ja><jl i >
<jul >
<h2>Przeczytane<jh2>
<ul >
<l i ><a href= " # " >Pel l entesque hab i tant<ja><jl i >
<l i ><a href= " # " >Morbi tri s t i que senectus<ja><j l i >
<l i ><a href= " # " >Aenean u l t ri c i es mi v i tae est<ja><jl i >
<jul >
<h2>ObejrzanejWystuchane<jh2>
<ul >
<l i ><a href= " # " >Pel l entesque hab i tant<ja><jl i >
<l i ><a href= " # " >Morbi tri s t i que senectus<ja><j l i >
<l i ><a href= " # " >Aenean u l t ri c i es mi v i tae est<ja><jl i >
<jul >
<jnav>
Zwróć uwagę na elementy h2, służące do oddzielenia grup łączy w elemencie nav. N a rysunku 1 .6
znaczniki h2 pełnią funkcję tytułów zakładek - po wybraniu tytułu przez użytkownika treść ulega zmianie
(ten efekt może być uzyskany za pomocą JavaScriptu). Nagłówek nie zawsze jest konieczny, ale jeśli tylko
to możliwe, powinien być używany do rozdzielenia i ustrukturyzowania grup nawigacyjnych. Z powodów
związanych ze stylizacją możesz być zmuszony do rozdzielenia poprzedniego przykładu na dwie struktury
nav, co również j est dopuszczalne.
Używanie elementu nav daje olbrzymie korzyści związane z dostępnością. Technologie ułatwień dostępu,
takie jak czytniki ekranu, będą w stanie wyszukać i natychmiast wykorzystać grupy nawigacyjne, zamiast
oczekiwać, aż pojawią się one na ekranie. Programiści umieszczali dotychczas na samym początku dokumentu
HTML łącza "skaczące" lub "pomijaj ące", które odwoływały się zwykle do głównego mechanizmu nawigacji
lub głównej treści. Jednak przy użyciu elementu nav będziesz mógł wkrótce opuszczać takie "pomijaj ące"
34 Rozdział 1. Nowe e lementy stru kturalne w HTML5
menu. Jedyny problem stanowi w tej chwili ograniczona obsługa elementów HTMLS w technologiach
wspomagających, powinno się to j ednak niebawem zmienić.
<arti c I e>
<header>
<h l>HTML5 oszczędza mi l i ony ! <jhl>
<jheader>
<p><strong>Pel l entesque hab i t ant morbi t r i s t i que<jstrong> senectus et netus et mal esuada fames ac turp i s
�egestas . Vest i bu l um tortor quam, feugi at vi tae , u l t ri c i es eget ...<jp>
<h2>! nny tytuf<jh2>
<ol >
<l i >Lorem i psum dol or s i t ame t , consectetuer adi p i s c i ng el i t . <jl i >
<l i >Al i quam t i nc i dunt maur i s e u r i s us . <jl i >
<jol >
<bl oc kquote><p>Lorem i psum dol or s i t amet , consectetur ad i p i sc i ng el i t . V i vamus magna. Cras i n m i a t fel i s
�al i quet congu e . Ut a est eget l i gul a mol est i e gra v i da . ...<p><jbl oc kquote>
<h3>! j eszcze i nny tytuf<jh3>
<ul >
<l i >Lorem i psum dol or s i t ame t , consectetuer adi p i s c i ng el i t . <jl i >
<l i >Al i quam t i nc i dunt maur i s e u r i s us . <jl i >
<jul >
<p>Ten artykuf j est autorstwa Toma Leadbettera zostaf opubl i kowany w " Czas i e HTML5 " w n i edz i e l ę ,
�32 l i stopada 2 0 1 0 . <jp>
</art i c l e>
Powyższa notka została umieszczona w elemencie a rt i e l e, ponieważ stanowi zamkniętą treść. Czy
pojawiłaby się w syndykacji (czyli kanale RSS)? Tak! Czy samodzielnie ma sens? Tak! A zatem jest to art i e l e.
Jak odkryjesz w późniejszych rozdziałach, możesz zagnieździć element seet i on w elemencie art i e l e,
a element art i el e wewnątrz elementu seet i on .
Specyfikacj a HTMLS mówi, że art i e l e jest niezależnym fragmentem treści i nawet komentarze na blogu
mogą być elementami art i e l e.
Gru powanie treści przy użyciu e lementu section 35
Inny tytuł
Lorem ipsum dolor sit arnet, consectetur adipiscmg elit. Vivamus magna. Cras in mi at felis aliquet congue.
Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifeud. libero at sagitis molis, tellus est
malesuada tellus, at luctus turpis elit sit arnet quarn. Vivarnus pretium ornare est.
Ten artykuł jest autorstwa Toma Leadbettera i został opublikowany \V "C zasie HTML5" w niedzielę. 32 listopada 20 10.
Rysunek 1 . 7 . Prosty element article z treścią (bez nadawania stylów)
Listing 1 . 9 . Utworzenie prostej strony z różnymi rodzajami wiadomości umieszczonymi w elementach section
Mnóstwo wiadomości
Wiadomości sportowe
Tutaj umieścimy \"iadomości sporto\�:e_
Wiadomości rozrywkowe
\Viadomości rozrywkowe zostaną u
mieszczone tutaj.
I
Wiadomości dla maniaków komputerowych
\Viadomości dla maniaków komputerowych zostaną umieszczone w tej części strony.
Na rysunku 1 .8 każdy fragment objęty elementem sect ; on ma swój własny nagłówek i każdy z nich jest
całkowicie oddzielony od pozostałych. Gdyby na stronie pojawiła się inna treść, mógłbyś opakować całość
w jeden element sect ; on i nadać mu tytuł "Rodzaje wiadomości, które pokazujemy":
<section>
<h l>Rodzaj e wi adomośc i , które pokazuj emy<jh2>
<section>
<h l>W i adomośc i rozrywkowe<jhl>
<p>W i adomości rozrywkowe zostaną umi eszczone tutaj . <jp>
</section>
</section>
Dodatkowo mógłbyś podzielić punkt "Wiadomości dla maniaków komputerowych" na kolejne podpunkty:
<section>
<h l>Rodzaj e wi adomośc i , które pokazuj emy<jh2>
<section>
<h l>W i adomośc i d l a man i a ków komputerowyc h<jhl>
<p>W i adomości dl a man i a ków komputerowych zostaną umi eszczone w tej częśc i strony. <jp>
<section>
<h2>W i adomośc i o grach<jh2>
</section>
<section>
<h2>W i adomośc i o gadżetach<jh2>
</section>
</section>
</section>
Mógłbyś zamieścić nagłówek "Wiadomości", a pod nim, niczym w gazecie, rozmaite rodzaje wiadomości.
W HTML 4 opakowałbyś całość elementem d i v, ale teraz możesz wykorzystać element sect i on. Każdy rodzaj
wiadomości byłby wtedy w swoim własnym elemencie sect i on i miałby swój własny tytuł.
Jeśli uważasz, że dana treść stanowi zamkniętą całość, powinieneś użyć elementu art i c l e.
Wskazówka
J a k przekonasz się w d a lszej części książki, kiedy będziemy mówić o tworzen i u konspektu w HTML5,
ważne jest sprawdzenie, czy użyłeś prawidłowego znaczn ika.
Często popełn i a n ą n a wczesnym eta pie n a u k i HTML5 pomyłką jest używanie elementu s ect i on do
opakowywania całych stron, na przykład tak: <section c l ass= "conta i ner"> albo <sect i on cl ass= "wrap">.
Nie jest to prawidłowy sposób użycia e lementu sect i on.
J a k mówi s pecyfi kacj a HTML5:
" Zaleca się stanowczo, aby autorzy postrzeg a l i użycie elementu d i v j a ko rozwiązanie ostateczne,
m ające zastosowan ie w sytu acj i , gdy żaden inny element nie jest właściwy. Używanie elementu d i v
zam i ast elementów bardziej o d powied n ich s kutkuje utru d n i e n i a m i w przegląd a n i u strony przez
użytkown i ków oraz dokonywa n i u zmian przez autorów " .
Element d i v " nie ma żadnego s pecj a lnego znacze n i a " , więc jest wykorzystywany do grupowa n i a treści,
która n ie pasuje do żadnego z nowych elementów HTML5. Często potrzebny jest do styl izacj i , w sytuacj i
kiedy C S S n i e d aje możl iwości wyod ręb n i e n i a danej treści w żaden i n n y s posób . J eżel i używasz
elementu sect i on, zostanie on uznany za zn aczący d l a dokumentu i z tego powodu dodany do jego
kon s pe ktu , n atom i ast e lement d i v n i e jest tam dod awany. J e ś l i zatem używasz elem entu sect i on
ze względu na style, zam iast n iego powin ieneś używać elementu d i v.
Listing 1 . 10 . Użycie elementu aside d o utworzenia paska bocznego "Powiązane łącza "
<styl e>
art i c l e , as i d e , nav { d i s p l ay: bl oc k ; }
art i c l e , as i de { fl oa t : l eft ; }
art i c l e { w i d t h : 500px ; }
nav { w i d t h : 250px ; }
</styl e>
</head>
<body>
<art i c l e>
<header>
<hl>10 i nformacj i na temat HTML5</hl>
</header>
<p><strong>Pel l entesque hab i t ant morbi t r i s t i que</strong> _</p>
</art i c l e>
<asi de>
<h2>Powi ązane tącza</h2>
<nav>
<ul >
<l i ><a href= " #" > lO i nformacj i n a temat HTML 4</a></l i >
< l i ><a href= " #" > lO i nformacj i n a temat CSS3</a></1 i >
<l i ><a href= " #" > lO i nformacj i n a temat JavaScri ptu</a></l i >
<fu l >
</nav>
</asi de>
</body>
</html >
tempor sit amet� ante. Donec eu libero sit amet quam egestas sempet". Aenean
Ja" aScripto
ulrricies mi vitae est. Mauris placemt e1eifend leo. Quisque sit amet est et
sapien ullarncorper pharetra. Ve'tibulum erat wisi, condimentum ,ed,
eomm.odo vi t. a � . omare sit amet, wisi_ Aenean f�enrum. elit egeI tincidunt
condime.nrum, eros ipsum rutrom ocei, saginis tempus lacus enim ac dui.
Donec non enim in torpis puh'inar faeilisis. Ut felis.
Inny tytuł
Możesz również zagnieździć element a s ; de wewnątrz innych elementów, włącznie z elementem art ; e l e.
Rozszerzając poprzedni przykład, mógłbyś udostępnić użytkownikowi słowniczek zawierający wyjaśnienie
użytych w zasadniczej treści słów lub zwrotów, które mogą nie być mu znane:
<art i c l e>
<header>
<hl>10 i nformacj i na temat HTML5<jh l>
<p>Pel l entesque hab i t ant morbi tri s t i que senectus et netus et mal esuada fames ac turp i s egestas .
�Ves t i bul um tortor quam, feugi at v i tae , ul tr i c i es eget , tempor s i t amet , ant e . Donec eu l i bero s i t
�amet quam egestas semper. Aenean ul tri c i es m i vi tae e s t . Maur i s pl acerat el ei fend l eo . <jp>
<jheader>
<asi de>
<h2>Stowni czek<jh2>
<p>Prawdopodobn i e zam i eśc i l i śmy na tej stron i e mnóstwo a kroni mów s krótów, warto w i ęc dodać
�stown i cz e k . <jp>
</asi de>
<jart i c l e>
Powyższy przykład znajdowałby się prawdopodobnie zaraz przed zamykaj ącym tagiem </body>.
(Zwróć również uwagę na to, że informacj a dotycząca praw autorskich znaj duje się w tagu sma 1 1 .
Powrócimy do tego w następnym rozdziale) .
Tak j ak w przypadku elementu header, możesz użyć elementu footer więcej niż raz na stronie. Możesz
również umieścić go na przykład wewnątrz artykułu. Na listingu 1 . 1 1 w ramach elementu art ; el e zostało
zagnieżdżonych kilka elementów footer, co ilustruje rysunek 1 . 1 0 .
<art i c l e>
<footer>
<p>Ta wi adomość zostafa opubl i kowana <t i me>l kwi et n i a 2 0 1 1<jt i me> przez <a href= " # " >Toma
�Leadbettera<ja><jp>
</ footer>
<p><strong>Pel l entesque hab i t ant morbi t r i s t i que<jstrong> _<jp>
< 1-- treść strony _o>
<footer>
<p>Ta wi adomość zostafa opubl i kowana < t i me> l kwi etn i a 201 1<jt i me> przez <a href= " # " >Toma
�Leadbettera<ja><jp>
<a href= " # " >Przeczytaj następny artykuf Toma<ja>
</ footer>
<jart i c l e>
< footer>
<smal l >© Copyri ght Podręczn i k HTML5 201 1<jsmal l >
</footer>
Pellente.sque babitant morbi tristique senectus et netus et ma1esuada farnes ae turpis egestas. Vestibulum tortor quam. feugiat ,:iłae,
ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. A�nMn ultricies mi virae esr. Mauris placer"" el�ifend
l�. Quisque sit aroet est et sapien ullamco�r phatetra.. Vestibulum erat wisi, condimentum std, cc:n.-nodo ·�lu�, ornate sit amet. wisi.
Aenean fennentum, elit eget tincidunt condimentum. eros ipsum rutrom orci. saginis tempus lacus enim ae dui. Dente non enun in rurpis
puh-inar facilisis. Ut felis.
Inny tytuł
l. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
2. Aliquam tincidunt mauris eu risus.
Lorem ipsum dolor sit amet, consectetur adipiscmg elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula
molestie gravida. Curabirur massa. Denec eleifend, Libero at sagiris molis, teHus est malesuada lelIas, at luctus rurpis elit sit
amet quam. Vi\"amus pretium omare est.
Ten artykuł jest autorstwa Toma Leadberttra i został opublikowany w "Czasie H1ML5" w niedzielę, 32 listopada 2010.
W tym przykładzie w elemencie art i e l e zostały umieszczone dwa elementy footer. Wyświetlanie autora
lub daty na górze i dole pojedynczej wiadomości lub wpisu na blogu to typowa praktyka, a elementu footer
możesz używać tyle razy, ile chcesz.
W poprzednim przykładzie wprowadziliśmy element t i me, o którym opowiemy w następnym rozdziale.
Możesz umieścić w elemencie footer inne treści, dotyczące przykładowo nawigacji (oczywiście
z wykorzystaniem elementu nav ) , logo partnerów i umów licencyjnych. Możesz także często zobaczyć
tekst w rodzaju: "Ta witryna jest obsługiwana przez <nazwa systemu zarzqdzania treściq>".
Specyfikacja HTMLS mówi, że element footer może zawierać łącza do powiązanych dokumentów,
i chociaż wcześniej używałeś w tym celu kombinacji elementów as i de i nav, możesz również użyć do takiej
treści elementu footer, jeśli tylko znajduje się on wewnątrz elementu art i e l e. Element footer może zawierać
na przykład łącza do poprzedniego i następnego artykułu, co wyglądałoby mniej więcej tak:
Wykorzystanie narzędzia HTML5 Outliner 41
<art i c l e>
. . . cata treść tego artykutu . . .
<footer>
<a href= " # " >Poprzedni artykut<ja> I <a href= " # " >Następny artykut<ja>
<jfooter>
<jart i c l e>
Kiedy wybierzesz to narzędzie, zostaną wyświetlone dane przypominające wyglądem spis treści, zwykle
z wcięciami.
Jeśli właściwie zorganizowałeś treść strony, spis treści powinien być ustrukturyzowany i logiczny.
Komunikat "Untitled section/article" j est niepożądany - j eśli został wyświetlony, być może użyłeś
nieprawidłowych znaczników, musisz zatem ponownie się im przyjrzeć. Zauważ jednak, że w przypadku
nav i a s ; de komunikat " Untitled section" j est jak najbardziej dozwolony.
Poprawny konspekt mógłby wyglądać następująco:
1. Nazwa witryny
a. Blog
i. Tytuł artykułu
ii. Tytuł artykułu
b. O mnie
i. Moje imię i nazwisko
ii. Rzeczy, które lubię
iii. Rzeczy, których nie lubię
c. Kontakt
42 Rozdział 1. Nowe e lementy stru kturalne w HTML5
Cl Poprowny konspekt
1 . Mnóst'....o WiadomoścI
1. UnHtled NAV
Mnóstwo " 2. :lli!!!
3. Rozrywka
I 4. Ko moutery
Dostarczamy er O"J ' "LiO .....J '>L� "" o ' T" U " U I " "",, ' .
� ----------'
-
• Strona główna
. �
• Kontakt
• StronągłÓ\\Jlą
Sport
Rozrywka
Komputery
Rysunek 1. 12. Konspekt prostego dokumentu HTML5 w Google Chrome
<header>
<hgroup>
<h l><a href= " # " >Mn6stwo wi adomości </a></h l>
<h2>Dostarczamy Ci najr6żn i ej szych wi adomośc i ! </h2>
</hgroup>
</header>
<nav>
<ul >
<l i ><a href= " # " >Strona gł6wna</a></l i >
<l i ><a href= " # " >O w i t ryni e</a></l i >
< l i ><a href= " #" > Konta kt</a></l i >
<l i ><a href= " # " >Strona gł6wna</a></l i >
<fu l >
</nav>
Tworzenie strony z wiadomościami 43
<sect i on>
<h 1>Sport<j h1>
<jsec t i on>
<sect i on>
<h1>Rozrywka<jh1>
<jsec t i on>
<sect i on>
<h1>Komputery<jh1>
<jsec t i on>
Strona zawiera na początku element h eader, użyty jako pierwszy węzeł konspektu (nie tytuł strony),
a następnie elementy seet ; on, które zawierają nagłówki. W elemencie header znajduje się element hgroup
z elementem h2 zawierającym tekst: "Dostarczamy Ci najróżniej szych wiadomościl" , ale nie widzisz go
w konspekcie, ponieważ konspekt czyta wyłącznie pierwszy nagłówek (hl, h2, h3, h4, h5 lub h6) w danym
elemencie.
Elementy seet ; on, art ; el e, nav i a s ; de rozpoczynają kolejne poziomy wcięć (podpunkty) w konspekcie.
Elementy seet; on zawieraj ą nagłówki h l , których treść j est wyświetlana w konspekcie. Mógłbyś tu użyć
elementów h2 albo h3, gdybyś chciał; to nie ma znaczenia. Jeśli element seet ; on zawierałby treść, ale nie
nagłówek, w konspekcie byłby wyświetlany tekst "Untitled section", czego powinieneś unikać.
<header>
<hgroup>
<h l>Mnóstwo wi adomośc i <jhl>
<h2>Dostarczamy Ci najróżn i ej szych wi adomośc i ! <jh2>
<jhgroup>
<jheader>
<nav>
<ul >
<l i ><a href= " # " >Strona główna<ja><jl i >
<l i ><a href= " # " >Sport<ja><j l i >
<l i ><a href= " #" > Rozrywka<ja><jl i >
<l i ><a href= " #" > Komputery<ja><jl i >
<l i ><a href= " # " >O w i t ryni e<ja><jl i >
< l i ><a href= " #" > Konta kt<ja><jl i >
<jul >
<jnav>
<sect i on i d= " headl i ne " >
<hl>Główny artykuł<jhl>
<art i c l e>
<header>
<h2><a href= " # " >To j est nasz naj ważni ejszy artykuł<ja><jh2>
<p>lO l i stopada 20 10<jp>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que senectus et netus et mal esuada fames ac turp i s egestas . <jp>
<jart i cl e>
<jsec t i on>
<sect i on i d= " sports " >
<h l>Sport<j hl>
<art i c l e> < 1-- (x3) _ o >
<header>
<h2><a href= " # " >Sport - tytuł 1<ja><jh2>
<p>lO l i stopada 20 10<jp>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que senectus et netus et mal esuada fames ac turp i s egestas . <jp>
<jart i cl e>
<jsec t i on>
<sect i on i d= " entertai nment" >
<hl>Rozrywka<jhl>
<art i c l e> < 1-- (x3) _ o >
<header>
<h2><a href= " #" > Rozrywka - tytuł 1<ja><jh2>
<p>lO l i stopada 20 10<jp>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que senectus et netus et mal esuada fames ac turp i s egestas . <jp>
<jart i cl e>
<jsec t i on>
<sect i on i d= " nerdy" >
<hl>Komputery<jhl>
<art i c l e> < 1-- (x3) _ o >
<header>
<h2><a href= " #" > Komputery - tytuł 1<ja><jh2>
<p>lO l i stopada 20 10<jp>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que senectus et netus et mal esuada fames ac turp i s egestas . <jp>
<jart i cl e>
<jsec t i on>
<as i de>
<a href= " # " >< i mg al t = " Wytworna rekl ama" src = " i magesjsnazzy-advert . g i f" hei ght= " 128"
�wi dth= " 128" j><ja>< I -- (x4) _ o >
Tworzenie strony z wiadomościami 45
<jasi de>
<footer>
<p>W i t ryna używa <a href= " # ">systemu zarządzan i a treśc i ą bez nazwy<ja><jp>
<p>W i t ryna znajduj e s i ę na <a href= " # " >serwerze bez nazwy<ja><jp>
<p>Wszys t k i e zdj ęc i a w tej wi tryni e są wtasnoś c i ą fotografa<jp>
<smal l >© Copyri ght N i kt 201 1<jsmal l >
<jfooter>
<jbody>
<jhtml >
'.
J(llln,�lQ]O
Pt.ll� lubltMł iBOfto:ł tf� hll� ilubllili!!ł � lłfńl.qęt Pelltetoetq'Dt lu.I:hlld �bI�.ąue
�nLIi -et l\d'Iri; e1 fnt!ie-.wb: 6.mn �rw d � C'1 n.l!kalUd..l
i l t'un...:. 1inlIt:Cru.t tt ",IUI et mJi)uu.łd..i: tłdn
Pcll� lb.t:łnDl!t: morb:a � �1I� iblbIlAll!l I!lilOCbi Iłfn.ł� PeIl� łWtttllfll łlllOtbJ �tqoe:
�hlIi -ci: ftdV6 et mde.� llmri �fl.ljd l5d"llło � f� tliBt:łi �W:i d �Ul d ll1*1a --uad;t {ła:k:łi
IIC tulptJ tf61a. "' ....... -- 1IC 1UfPH *ł1ti..
Rysunek 1 . 13. Układ strony z wiadomościami utworzonej z wykorzystaniem nowych elementów HTML5
Teraz, kiedy uporałeś się z podstawowym układem i kodem, powinieneś sprawdzić konspekt dokumentu.
Na podstawie powyższego kodu zostanie utworzony następujący konspekt:
46 Rozdział 1. Nowe e lementy stru kturalne w HTML5
1 . Mnóstwo wiadomości
a. Untitled NAV
2. Główny artykuł
a. To jest nasz najważniejszy artykuł
3. Sport
a. Sport - tytuł 1
b. Sport - tytuł 2
c. Sport - tytuł 3
4. Rozrywka
a. Rozrywka - tytuł 1
b. Rozrywka - tytuł 2
c. Rozrywka - tytuł 3
5. Komputery
a. Komputery - tytuł 1
b. Komputery - tytuł 2
c. Komputery - tytuł 3
6. Untitled ASIDE
Wygląda to wspaniale! W przeciwieństwie do elementów sect ; on, art ; c l e, nav i as ; de, element footer
nie dzieli treści na fragmenty, więc nie pojawia się w konspekcie. Elementy nav i a s ; de nie są zatytułowane,
ale to nie problem. Gdybyś chciał, mógłbyś nadać elementowi a s ; de tytuł, jednak czy reklama produktu
zewnętrznego zasługuj e na tytuł?
<header>
<h 1>Szukan i e nazwy przedsi ębi orstwa<jh 1>
<jheader>
<form>
<fi el dset>
<l egend>Szukaj<jl egend>
<l abe l for= " search i nput " >Szukaj<jl abe l >
<i nput type=" searc h " i d = " searc h i nput" name= " searc hi nput" j>
<i nput type=" submi t " j>
<a href= " # " >Szukan i e zaawansowane<ja>
<jf i e l dset>
<jform>
<nav>
<h2>Doktadne szukani e<jh2>
<ul >
<l i ><a href= " # " >Wszyst ko<ja><jl i >
<l i ><a href= " # " >Wi adomości <ja><jl i >
<l i ><a href= " # " >Wi ęcej<ja><jl i >
<jul >
<h3>S i eć<jh3>
<ul >
<l i ><a href= " # " >Strony z Pol s ki <ja><jl i >
<l i ><a href= " # " >Strony z Twoj ego regi onu<ja><jl i >
<jul >
<h3>Czas<jh3>
<ul >
<l i ><a href= " # " >Naj nowsze<ja><jl i >
<l i ><a href= " # " >Ostatni e 2 dni <ja><jl i >
<l i ><a href= " # " >Wi ęcej narzędzi wysz u k i wani a<ja><jl i >
<jul >
<jnav>
<sect i on>
<header>
<h1>Wyn i ki dl a &quo t ; test&quo t ; <jh1>
<p>Okoto 1 410 000 000 wyn i ków ( 0 , 2 1 sekundy)<jp>
<jheader>
<art i c l e>
<header>
<h 1><a href= " #">Pi erwszy wyn i k<ja><jh1>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que senectus et netus et mal esuada fames ac turp i s egestas . <jp>
<footer>
<p>www. udawaj wi tryne . com - <a href= " # " >z pami ęci podręcznej <ja><jp>
<ul >
<l i ><a href= " # " >W i adomośc i <ja><jl i >
<l i ><a href= " # " >Za kupy<ja><jl i >
<l i ><a href= " # " >Zdj ę c i a<ja><jl i >
<l i ><a href= " # " >Sport<ja><jl i >
<l i ><a href= " #" > B i znes<ja><jl i >
48 Rozdział 1. Nowe e lementy stru kturalne w HTML5
<art i e l e>
<header>
<h l><a href= " # " >Drugi wyni k<ja><jhl>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas . <jp>
<footer>
<p>www. udawaj w i t ryne2 . p l - <a href= " # " >z parni ęe i podręeznej <ja><jp>
<jfooter>
<jart i el e>
<art i e l e>
<header>
<h l><a href= " # " >Trzee i wyn i k<ja><j h l>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas . <jp>
<footer>
<p>www. udawaj w i t ryne3 . p l - <a href= " # " >z parni ęe i podręeznej <ja><jp>
<jfooter>
<jart i e l e> < 1-- i tak dale]. .. _o>
<as i de>
<nav>
<h2>Wys z u k i wan i a powi ązane z " ; Test" ; <jh2>
<ul >
<l i ><a href= " #" > I nna <strong>w i t ryna<jstrong><ja><jl i >
<l i ><a href= " #" > I nna <strong>w i t ryna<jstrong><ja><jl i >
<l i ><a href= " #" > I nna <strong>w i t ryna<jstrong><ja><jl i >
<l i ><a href= " #" > I nna <strong>w i t ryna<jstrong><ja><jl i >
< 1-- etc. _o>
<jul >
<jnav>
<jasi de>
<jsee t i on>
<nav>
<ul >
<l i e l as s = " eurrentpage" > l<jl i >
<l i ><a href= " # " >2<ja><jl i >
<l i ><a href= " # " >3<ja><jl i >
<l i ><a href= " # " >4<ja><jl i >
<l i ><a href= " # " >5<ja><jl i >
<l i ><a href= " # ">6<ja><jl i >
< 1-- i tak dale]. . . _o>
<jul >
<jnav>
<form>
<fi el dset>
<l egend>Szukaj<jl egend>
<l abe l f or= " seareh i nput2 ">Szu kaj <jl abe l >
<i nput type=" seare h " i d = " seare h i nput 2 " name= " seare h i nput 2 " j>
<i nput type=" submi t " j>
<a href= " # " >Szukan i e zaawansowane<ja>
Tworzenie strony z wyn i kam i wyszu kiwania 49
<jf i e l dset>
<jform>
<footer>
<ul >
<l i ><a href= " #" > Regul ami n<ja><jl i >
<l i ><a href= " # " >Pol i tyka prywatnoś c i <ja><jl i >
< 1-- etc. -- >
<jul >
<jfooter>
<jbody>
<jhtml >
Wy n i ki dla "test"
Dokładne OkOl<> l 4 1 0 000 000 wynik6w (0.21 sekundy)
szukanie
Wszystko WiadomoSa
� P i erwszy wyn i k
Sieć Pellenlesque habitani morbi tristique senedus et nelus e t malesuada fames ac turpis egestas.
\V\Wł udawajwitJyne ,com - zpamieąpodrecznej
Stronyz Polski Stronyz Wl8dOmOŚCI Zakupy Zd"ącl8 §QQ!! BIznes
Twojeoo regionu RozMvka Wiece!'łNmkÓW z udawajwlłrvne.ol
Czas
Nalnowsze Ostatnie 2 dni
Wlacej narzędzi D rugi wynik
wyszukiwama
Pellentesque haMant morbi trisłlQue senecłus et nelus et malęsui;Jda fames ec turpis egeslas
www.udawajwiuyne2. pl - zpamieclpodręcznei
Trzeci wyn i k
Pellentesque habltant morbi triSłlque senedus e t nelus et malesuada fames ae turpis egestas
www udaW8jWrlryne3. pl - zpamięcipodreClnel
Być może masz inne pomysły dotyczące tego, jakich znaczników użyć. To świetnie. Powinieneś myśleć
o tworzeniu pięknego kodu HTML.
Wszystkie rezultaty znajdują się we fragmencie (oznaczonym elementem sect i on) strony, który ma
nagłówek <h 1>Wyn i ki d l a " test" ; </h 1>. Każdy kolejny rezultat j est umieszczony w osobnym
artykule, a wszystkie one maj ą nagłówek i stopkę. Rezultaty wyszukiwania mogłyby być rozdzielone na
kolejne podpunkty lub artykuły, w zależności od treści.
W dolnej części kodu znajdują się znaczniki umożliwiające przechodzenie na inne strony, co można uznać
za "główną nawigację" (przypomnij sobie, co mówi na ten temat specyfIkacja HTMLS), gdyż funkcjonalność
stronicowania ma kluczowe znaczenie dla sposobu, w jaki użytkownik nawiguj e po rezultatach swoich
wyszukiwań.
50 Rozdział 1. Nowe e lementy stru kturalne w HTML5
P od su mowanie
w tym rozdziale dowiedziałeś się o nowych elementach dostępnych w HTMLS, których możesz używać,
tworząc główną strukturę strony internetowej . Rozpocząłeś od nowego doctype i zmian związanych
z dołączaniem plików J avaScript i CSS. Następnie wykorzystałeś elementy h eader, hgroup, nav, footer,
art ; c l e, sect ; on i as ; de do utworzenia układu strony. Wreszcie, dzięki weryfikacji konspektu dokumentu,
sprawdziłeś, czy struktura Twojego dokumentu j est poprawna i czy użyłeś właściwych elementów.
2
Grupowanie, poziom tekstu
i zmiany semantyczne
W
poprzednim rozdziale poznałeś kilka nowych elementów HTMLS, które umożliwiają tworzenie
głównej struktury strony. W tym rozdziale przeczytasz o kolejnych nowych elementach (mianowicie
fi gure, t i me, deta i l s i mark) oraz o kilku elementach, które zostały przedefiniowane (address, s, c i te, o l ,
d l , smal l , b, strong, i , em, abbr oraz hr). Przyjrzymy się również nowym łączom blokowym oraz specyfikacji
WAl-ARIA. Elementy te są nazywane elementami grupującymi albo elementami poziomu tekstu i dotyczą
treści strony.
<fi gure>
< i mg a l t = " Wykres s tup kowy" src = " analyt i c s . g i f" j>
<fi gcapt i on>
Anal i za danych dotyczących wi tryny za paźdz i erni k 2010
<jf i gcapti on>
<jfi gure>
52 Rozdział 2. Gru powanie, poziom tekstu i zm iany semantyczne
I listopada 20 I O
Pellentesque habitant morbi tristique. senectus et netus et malesuada fames ac turpis egestas . Vestibulum
tortor quam, feugiat vitae, ultricies eget, te,mpor sit amet� ante. Donec fU libero sit amet quam egestas semper.
Aenean ultricies mi \"itae es!. Mauris placerat eleifend leo. Ut felis. Praesent dapibus, neque id cursus
faucibus, tortO! neque egestas augue� eu vulputate magna eros eu erat. Aliquam erat volurpat. Nam dui mi�
tincidunt quis, accumsan porttitor, facilisis luctus: metus
11
semper. Aenean ultricies mi
vitae est. Mauris placerat
eleifend le,o. Quisque sit amet
II
est et sapien ullamcorper
•••
pharetra Vestibulum erat wisi,
c.ondimentum sed� c.ommodo
vitae, ornare sit amet, \\risi.
Aenean fermentum, elit eget
tincidunt condimentum, eros
ipsum rutrom orc.i� sagirtis Analiza danych dotyczqcych witryny za październik 20 l O
tempus lacus enim ac. dUl. Donec
non enim in turpis pulvinar
facilisis . Ut felis. Praesent dapibus, neque id cursus fauc.ibus, tortor neque ege.stas augue, eu 'V-ulputate magna
eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, fac.ilisis luctus, metus
są pewne niejasności dotyczące tego, czy tekst umieszczany w atrybucie a l t (tekst alternatywny dla
przeglądarek, które nie obsługują obrazków) jest nadal potrzebny w elemencie fi g ure. Umieszczony poza
elementem fi g ure znacznik i mg zawsze wymaga atrybutu al t. Jeśli obrazek jest czysto prezentacyjny i nie
musi być identyfIkowany przez technologie ułatwień dostępu, można zastosować pusty atrybut a l t. W sytuacji
gdy użyto elementu figure, jeśli podpis może być jednocześnie odpowiednim opisem, a l t nie jest potrzebny.
Obecnie jednak, w związku z niedostateczną obsługą technologii ułatwień dostępu w przeglądarkach, takie
rozwiązanie nie jest optymalne.
Sugeruj emy, aby wykazać się raczej nadmierną ostrożnością i mimo wszystko nadawać wartość
atrybutowi a l t. Na listingu 2.1 podpis jest wystarczaj ąco jasny, lecz dla kogoś używającego czytnika ekranu
nie jest wiadome, jak dane są reprezentowane, a wtedy tekst w atrybucie a l t dostarczyłby tej informacji.
Zwróćmy dodatkowo uwagę, że pomimo iż użyliśmy w przykładzie obrazu z wykresem, nie ma powodu,
dla którego nie mógłbyś użyć wykresu utworzonego za pomocą Canvas albo SVG.
I
Uwaga
Początkowo specyfikacja zalecała użycie Uuż istniejącego) elementu l egend zam iast nowego elementu
(fi gcapt i on), lecz ze względu na problemy z n ad awan iem stylów w przeglądarkach l egend zarzucono
na rzecz fi gcapt i on .
J ak pokazuje rysunek 2.2, nie trzeba się ograniczać tylko d o jednego obrazka w elemencie fi gure;
możesz używać elementu f i gure do wyświetlania wielu obrazków. Kod dotyczący rysunku 2.2 znajduje
się na listingu 2.2.
Oznaczanie d aty i czasu za pomocą elementu time 53
<fi gure>
< i mg a l t = " Wykres s tup kowy danymi paźdz i ern i ka 2 0 1 0 " src = " anal yt i cs-october . png" j>
< i mg a l t = " Wykres s tup kowy z danymi z l i stopada 2 0 1 0 " src= " anal yt i cs-november. png" j>
< i mg a l t = " Wykres s tup kowy z danymi z grudni a 2 0 1 0 " src = " analyt i c s -december . png" j>
<fi gcapt i on>
Anal i za porównawcza danych wi tryny za j es i eń 2010
(paźdz i ern i k, l i stopad, grudz i eń)
<jf i gcapti on>
<jfi gure>
l stycznia 201 1
Pellentesque habitant mochi tristique. senectus et netus et malesuada farnes ac turpis egestas. Vestibulum
tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas
semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.
.1.1
Analiza porównawcza danych witryny zajesień 20 l O (październik, listopad, grudzie/z)
Pe IJentesque habitant mocbi tristique senectus et netus et malesuada farnes ac turpis egestas. Vestibulum
tonor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas
semper. Aenean ultricies mi vitae es" Mauris placerat eleifend leo. Quisque sit amet es! et sapien
ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi.
Rysunek 2 . 2 . Użycie elementu figure do wyświetlenia trzech obrazków współdzielących jeden podpis
Czy trzeba zawsze używać fi gcapt i on, żeby wyświetlić taką treść? Jeśli obrazek (albo wykres, tabela itp.)
służy celom czysto prezentacyjnym, użyj zwykłego tagu i mg. Jeśli jednak zawiera on dodatkowe informacje
i jest istotny dla treści strony, prawdopodobnie będzie wymagał opisu - w takim przypadku użyj elementów
fi gure i fi gcapt i on. Na koniec dodajmy, że fi gure może mieć tylko jeden fi gcapt i on.
• datet i me - użytkownik końcowy zobaczy treść znajdującą się wewnątrz znacznika t i me, lecz
komputer będzie w stanie odczytać wartość atrybutu datet i me: datet ime= "2011 -04-01 T16 : OOZ".
Część dotycząca czasu (T1 6 : 00) jest opcjonalna. Możesz również dodać informację o poprawce
związanej ze strefą czasową: T16: 00+04 : 00. Znak Z reprezentuje uniwersalny czas koordynowany
(UTC), a j ego użycie jest równoznaczne z dodaniem poprawki o wartości +00 : 00 .
• pubdate - pubdate jest atrybutem logicznym. Wskazuje, że data oraz (być może) czas dotyczą
publikacji najbliższego rodzicielskiego elementu art i c l e. Jeśli nie ma rodzicielskiego elementu
art i ci e, wtedy pubdate odnosi się do całego dokumentu. Każdy element art i ci e powinien mieć
tylko jeden element t ime z atrybutem pubdate.
Element t i me jest przeznaczony do pokazywania dokładnych dat, takich j ak ,,22 stycznia 2 0 1 1 ", a nie
nieprecyzyjnych, takich jak "w jakimś momencie roku 20 1 1 " . Wartość atrybutu dat et i me musi być
podana w formacie stosowanym w kalendarzu gregoriańskim: RRRR-MM-DD, z czasem zakodowanym
w postaci TOO : 00.
Oto kilka przykładów:
<art i c l e> . . . .
<footer>
<p>Ta wi adomość zostata opubl i kowana dni a <time pubdate dateti me="2011-04-01T16 :00">1 kwi et n i a 2 0 1 1
-" 0 godz i n i e 1 6 : 00</time> przez <a href= " # " >Jana Kowa l s k i ego<ja><jp>
<jfooter>
<jart i c l e>
<art i c l e>
<h 1>Rodz i nne zdj ęc i e bożonarodzeni owe<jh1>
<p>Byto wspan i al e być tutaj z rodz i ną w czas i e <time pubdate datetime= "2010-12- 25">Bożego Narodze n i a
-"2 0 1 0</time> <jp>
<fi gure>
< i mg al t = " " src = " OOOOO l . j p g " j>
<fi gcap t i on>Rodz i na Kowa l s k i c h w czas i e bożonarodzeni owego poranka<jfi gcapt i on>
<jfi gure>
<jart i c l e>
<p>Publ i kacja HTML6 j est ocze k i wana w dn i u <time datetime="2040-01-04">1 kwi etn i a 2 0 4 0</time> <jp>
1 Zgodnie z informacjami podanymi w witrynie http://caniuse.com/, o której wspomniano w rozdziale 3., w kwietniu 2012 roku
obsługiwała go także przeglądarka systemu Android -przyp. tłum.
Tworzenie widżetu przełączn i ka za pomocą elementu details 55
zawartość elementu s unrnary. Użytkownik może kliknąć treść z elementu s ummary, co spowoduje otwarcie
bądź zamknięcie szczegółów.
Rysunek 2.3 pokazuje, j ak wygląda krótka informacj a o autorach z górną częścią domyślnie otwartą.
Listing 2.3 prezentuje kod.
<deta i l s ap en>
<summary>Tam Leadbetter<jsummary>
<fi gure>
< i mg al t= " " sre = " i magesjtam-and - l uee . j pg" j>
<fi geapti an>Tam i Luey Leadbetterawi e<jfi geapti an>
<jfi gure>
<p>Pel l entesque habi tant marbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas . <jp>
<p>Pel l entesque habi tant marbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas . <jp>
<p>Pel l entesque habi tant marbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas . <jp>
<p>Pel l entesque habi tant marbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas . <jp>
<jdet a i l s>
<deta i l s>
<summary>Chuek Hudsan<jsummary>
<fi gure>
< i mg al t=" " src=" c huc k . j pg" />
<fi geapti an>Chuek Hudsan<jfi geapt i an>
<jfi gure>
<p>Pel l entesque habi tant marbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas . <jp>
<jdet a i l s>
� Chuek Hudson
Innym przykładem może być wykorzystanie elementu deta; l s do pokazywania lub ukrywania spisu
treści. W zależności od stylów wykorzystanych na stronie i ilości treści użyteczne może być umieszczenie
spisu treści tak, by był zawsze widoczny w górnym rogu strony. Użytkownik mógłby go kliknąć w celu
rozwinięcia go i przejścia do innej części strony. Listing 2.4 pokazuj e kod takiego rozwiązania, z elementem
deta; l s domyślnie zamkniętym.
56 Rozdział 2 . Gru powanie, poziom tekstu i zm iany semantyczne
<art i c l e>
<h l>Ol brzymi dokument z mnóstwem soczystej treśc i </ h l>
<deta i l s>
<summary>Sp i s treś c i </summary>
<nav>
<ul >
<l i ><a href=#chapterl>Rozdz i at l . </a></l i >
<l i ><a href=#chapter2>Rozd z i a t 2 . </a></l i >
<ful >
</nav>
</deta i l s>
<sect i on>
<hl i d= " chapter l " > Rozdz i at l . </ h l>
</sect i on>
<sect i on>
<hl i d= " chapter2 ">Rozdz i at 2 . </ h l>
</sect i on>
<fart i c l e>
HTMLS próbuje wyj aśnić nieporozumienie poprzez zalecanie, by element address był używany
do przechowywania danych kontaktowych dotyczących najbliższego elementu art; e l e albo body.
Co to w takim razie oznacza? Oznacza to, że powinieneś używać elementu address do danych
kontaktowych autora bieżącego artykułu (a rt ; e l e) albo całej strony. Ponieważ możesz użyć elementu
address wewnątrz elementu art ; el e, addres s potencjalnie może być wykorzystany w witrynie kilka razy.
Treścią elementu address może być adres e-mail, adres strony internetowej, numer telefonu, adres pocztowy
albo j akiś inny rodzaj danych kontaktowych.
Ponieważ jest on używany do danych kontaktowych, zwykle umieszcza się element addres s wewnątrz
elementu footer. Na listingu 2.5 użyliśmy elementu address dwukrotnie: raz do umieszczenia informacji
o autorze treści strony głównej, a drugi raz do podania kontaktu do autorów całej witryny.
Podświetlanie tekstu za pomocą e lementu mark 57
<art i e l e>
<header>
<h l><a href= " # " >Mój n i ezwykl'y wp i s na bl ogu<ja><jhl>
<p> 12 . 12 . 20 1 1<jp>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas . . . <jp>
<footer>
Ten wpi s zostal' nap i s any przez
<address><a href="me . htm">Jana Kowal ski ego</a></address>
<jfooter>
<jart i e l e>
<footer>
Wl'aśe i e i el ami tej w i t ryny są
<address>
<a href= "me . htm">Jan Kowal ski</a> i
<a href=mai l to : pi otr@examp l e . com>Pi otr Nowak</a> .
</address>
<jfooter>
<styl e>
mark {background- co l o r : #OFO; font-wei ght :bol d ; }
<jstyl e>
<art i e l e>
<header>
<h l>Coś nap i s anego po l'ae i n i e<jh l>
<jheader>
<p>Pel l entesque hab i t ant morbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas .
�<mark>Vest i bul um tortor quam</mark> , feu g i at v i tae, ul tr i e i es ege t , tempor s i t amet , ante . Donee eu
�l i bero s i t amet quam egestas semper. Aenean ul tri e i es mi vi tae e s t . Mauri s pl aeerat el e i fend l eo . <jp>
<jart i e l e>
Pellentesque habitant morbi tristique seneetus et netus et malesuada fames ae turpis egestas. iStIb
........
. ..".
., .., feugiat vitae, ultricies eget, tempor sit amet, ante. Donee eu libero sit amet quam egestas
semper. Aenean ultricies mi vitae e st. Mauris placerat eleifend leo.
Listing 2 . 7 . Element s
Zaleeanaeeaaeietaliezaa: 45,99zł
W HTML 4 element s definiował przekreślony tekst, więc domyślnie przeglądarki będą nadawać temu
elementowi styl przekreślenia.
Jeśli fragment dokumentu został przeredagowany lub usunięty, nie używaj elementu s; zamiast tego
skorzystaj z elementu del .
Element cite
Element c ; te w HTMLS został poprawiony. W HTML 4 c ; te pozwalał twórcom treści oznaczać nazwisko
autora cytatu:
<ci te>Jul i usz Cezar<j c i te> pow i edzi af ki edyś <q>przybyfem , zobaczyfem, zwyc i ężyfem<jq> .
Był on również używany wewnątrz elementu b l ockquote, co w zasadzie w HTML 4 było niepoprawne,
niemniej często stosowane:
Zm iany dotyczące istniej ących elementów 59
<bl oc kquote>
<p>Pel l entesque hab i t ant morbi t r i s t i que senectus et netus et mal esuada fames ac turp i s egestas . Ves t i bul um
�tortor quam, feu g i at v i tae, ul tr i c i es ege t , tempor s i t amet , ante . <jp>
<ci te>Osoba , która mów i t a po tac i n i e<jci te>
<jb l oc kquote>
Natomiast w HTMLS c; te reprezentuje tytuł dzieła, jak książka albo piosenka. Specyfikacja HTMLS
określa precyzyjnie, że nazwisko osoby nie j est tytułem dzieła. Mógłbyś zatem użyć czegoś takiego j ak
w następującym przykładzie:
<p>Jedną z mo i ch ul ubi onych ks i ążek j est <c i te>Dz i eń Szakal a<j c i te> <b>Frederi c ka Forsytha<jb><jp>
I
Uwaga
W l i pcu 2011 roku odbyła się dyskusja, której skutkiem może być umożliwienie użycia e lementu
footer w elemencie b l ockq uote, co stworzyłoby możl iwość dodawan ia inform acj i o cytacie,
n a przykład dotyczącej autora. Miej o ko na tę s prawę.
Element ol
Element o l , używany do tworzenia listy uporządkowanej, został przedefiniowany i teraz akceptuje trzy
atrybuty:
• start,
• reversed,
• type.
Użyty na listingu 2.8 atrybut reversed jest nowością w HTMLS i - w założeniu - umożliwia utworzenie
odwróconej listy, odliczającej w dół do jednego.
Atrybut start w HTML 4 był uznawany za przestarzały i w związku z tym, gdy został użyty na jakiejś
stronie, to strona ta nie przechodziła walidacji, co było dość irytujące. Szczęśliwie atrybut ten powrócił
i w HTMLS jest zupełnie prawidłowy. Jeśli więc musisz rozpocząć uporządkowaną listę od drugiej pozycji,
użyj następującego kodu:
<ol start = " 2 " > <l i >tutaj
zaczynamy</l i >
</0 1 >
Również atrybut type powrócił z zaświatów. Wcześniej, jeśli chciałeś zmienić sposób wyświetlania listy,
aby pokazywać kolejne punkty, powiedzmy, jako liczby rzymskie (na przykład I, IV, X), musiałeś użyć CSS
a. W HTMLS możesz to zrobić jak na listingu 2.9.
<ol >
<l i > Lorem i psum dol ar s i t amet , consectetuer ad i p i s c i ng el i t .
<ol>
<l i >Lorem i psum dol ar s i t ame t , consectetuer adi p i s c i ng el i t . </l i >
<l i >Al i quam t i nc i dunt maur i s e u r i s us . </l i >
<l i >Lorem i psum dol ar s i t ame t , consectetuer ad i p i s c i ng el i t .
<ol >
<l i >Al i quam t i nc i dunt mauri s eu r i sus . </l i >
<l i >Vest i bul um auctor dap i bus nequ e . </l i >
</0 1 >
c/l i >
<l i >Ves t i bul um auctor dapi bus neque . </l i >
</01 >
c/l i >
<l i >Al i quam t i nc i dunt mauri s e u r i s us . </l i >
<l i >Vest i bul um auctor dap i bus neque . </l i >
</0 1 >
Używając atrybutu type, możesz zmienić rodzaj numeracji elementów listy bez potrzeby używania CSS-a.
Możesz wybierać spośród pięciu rodzajów:
type= " 1 " - 1, 2, 3, 4, 5
type= " a " - a, b, c, d, e
type = " A " - A, B, C, D, E
type= " i " - i, ii, iii, iv, v
type= " I " - I, II, III, IV, V
Jeśli zmienisz typ elementu o l na jeden z powyższych, elementy listy zostaną pokazane następująco :
1 . Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
a. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
b. Aliquam tincidunt mauris eu risus.
c. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
i. Aliquam tincidunt mauris eu risus.
ii. Vestibulum auctor dapibus neque.
d. Vestibulum auctor dapibus neque.
2. Aliquam tincidunt mauris eu risus.
3. Vestibulum auctor dapibus neque.
Dzięki użyciu różnych wartości atrybutu type w treści możesz się odwolywać do pozycji 1 .b.ii zamiast 1 .3.2.
Element dl
W HTML 4 element d l oznaczał listę definicji, która powinna zawierać pojęcie, a następnie definicję - lecz
jego własna definicja i sposób użycia nigdy nie były jasne i w związku z tym był on używany niewłaściwie lub
zastępowany innymi elementami.
W HTMLS określono, że ma on być listą opisów lub listą asocjacyjną. Łatwiej jest zrozumieć ten element
na przykładach. Na listingu 2 . 1 0 użyliśmy elementu dl do utworzenia słownika. Umieściliśmy ten słownik
w elemencie as i de, ponieważ zakładamy, że znajduje się on wewnątrz elementu art i cl e z zawartością na
temat tworzenia stron internetowych.
<as i de>
<h2>Stowni k<jh2>
<dl >
<dt>HTML<jdt>
<dd>HTML, będący akronimem pochodzącym od ang i e l s ki ego HyperText Markup Language, j est domi nuj ącym j ęzyki em
�znaczn i ków stużącym do tworzen i a stron i nternetowyc h . <jdd>
<dt>PHP<jdt>
<dd>PHP (Hypertext Preprocessor) j est szeroko używanym j ęzyk i em s kryptowym ogól nego przeznaczen i a , który byt
�zaproj ektowany do tworzen i a dynam i c znych stron i nternetowyc h . <jdd>
<jd l >
<jasi de>
62 Rozdział 2 . Gru powanie, poziom tekstu i zm iany semantyczne
Na listingu 2 . 1 1 użyliśmy elementu dl do zapisania listy osób biorących udział w produkcji filmu.
<jd l >
W powyższym kodzie użyliśmy wielu wartości (dd) przypisanych d o j ednego klucza (dt). Można
argumentować, że każda pozycja na liście osób mających udział w wyprodukowaniu filmu (reżyser, autorzy
scenariusza i tak dalej) mogłaby się znajdować we własnym elemencie sect i on, jak pokazano tutaj :
<art i c l e>
<header>
<hl>S kazani na Shawshank<jhl>
< t i me>1994<jt i me>
<jheader>
<sect i on>
<hl>Reżyser<jh l>
<h2>Fran k Darabont<jh2>
<p> (bi o) <jp>
<jsec t i on>
<sect i on>
<hl>Scenari usz<j h l>
<h2>Stephen Ki ng<jp>
<p> (bi o) <jp>
<h2>Fran k Darabont<jh2>
<p> (bi o) <jp>
<jsec t i on>
<jart i c l e>
Tak naprawdę zależy to od treści strony i od tego, jak chcesz tę treść ustrukturyzować.
Wskazówka
Element dl był dawn iej używany do oznaczania d i alogów, lecz teraz s pecyfi kacj a mówi n a m , że takie
wykorzystanie elementu dl jest niewłaściwe. Początkowo w HTML5 istn iał e lement d i a l og, lecz został
on usun ięty pod kon iec 2009 roku . Zam iast n iego powin ieneś używać e lementów p, a jeś l i chciałbyś
nadać styl im ionom rozmawiaj ących, powinieneś zastosować albo e lement span, albo b. Oto przykład :
Element smali
W HTML 4 element sma 1 1 był używany do zmniejszania rozmiaru tekstu. Jednak w sprawach związanych
z prezentacją powinniśmy używać CSS-a. Teraz, w HTMLS, element sma 1 1 jest używany do wyświetlania
"drobnego druku", takiego j ak informacj a o prawach autorskich, regulamin albo informacj e licencyjne
bądź prawne:
<p><sma 1 1 > Ta w i t ryna j es t dostępna na l i cencj i <a href= ''http : //crea t i vecommons . org/
�l i censesjby-nc-saj3 . 0jpl j ">Creat i ve Commons Uznan i e autorstwa-Użyc i e n i ekomercyj ne-Na tych samych
�warunkach 3 . 0<ja> . Możesz ją zmi eni a ć , używać ponown i e i rozszerzać . <jsmal l ><jp>
Ponieważ sma 1 1 jest treścią wewnętrzną, możesz umieścić go w razie potrzeby w innym elemencie,
takim jak st rong, co nadałoby wagi tym podanym drobnym drukiem informacjom:
<p><strong><smal l >Ta treść nal eży do mni e ! N i e krad n i j j ej , bo w przec i wnym wypadku będz i esz mi at bardzo
�poważne ktopoty. <jsmal l ><jstrong><jp>
Nie powinieneś używać w takiej sytuacji elementu strong, ponieważ nie chcesz dodawać ważności
pierwszemu akapitowi; po prostu nadajesz mu inny styl. Zamiast używać elementu b, możesz skorzystać
z CSS-a (p : fi rst-of-type albo h2+p). Na listingu 2 . 1 2 zastosowaliśmy element b do wyróżnienia fragmentów
tekstu kolorem.
Listing 2 . 1 2 . Element b
<styl e>
b . red { c o l or: red ; }
b . green { co l or : green ; }
b . b l ue { col or: bl ue ; }
<jstyl e>
<hl>Moje ul ubi one kol ory<jhl>
<ol reversed>
<l i ><b cl ass="red">czerwony</b><jl i >
<l i ><b cl ass="green">z i el ony</b><j l i >
<l i ><b cl ass="bl ue">ni ebi es k i </b><jl i >
<jo l >
64 Rozdział 2. Gru powanie, poziom tekstu i zm iany semantyczne
Element strong pokazuje tekst mający duże znaczenie, więc teraz powinieneś używać go do uzyskania
efektu pogrubienia. Możesz zagnieżdżać elementy st rong, by zwiększyć powagę treści:
<p><strong>Ni e j edz moi ch ci astek</strong> ani <strong><strong>n i e p i j mojego ml e ka</strong></strong><jp>
Elementy i oraz em
Element ; służył w HTML 4 do wyróżniania tekstu kursywą. Teraz jednak oznacza się nim tekst, który jest
wyrażony innym głosem lub w jakiś sposób odbiegający od głównego tekstu. Specyfikacja HTMLS podaje
kilka przykładów jego użycia; korzystamy z niego, by zapisać jakąś myśl, pojęcie techniczne czy idiom
z języka obcego:
<p>Zjem dz i ś w i eczorem rybę < i > (a potem może z j em c i astec z k a ; od dawna n i e j adfem c i astec z e k ) <j i > . <jp>
Dla odmiany element em oznacza podkreślenie, które zmienia sens zdania. Słowa, które powinny być
zaakcentowane, opakuj elementem em, pamiętając, że zmiana położenia elementu em spowodowałaby zmianę
sensu zdania:
<p>Ja myś l afem , że mam s i ę spot kać z przyj a c i ófmi o 20 : 00 , a l e moja żona mówi , że o <em>2 1 : 00<jem><jp>
<p><em>Ja<jem> myśl afem, że mam s i ę spotkać z przyj aci ófmi o 20 : 00 , a l e moja <em>żona<jem> mówi , że o 2 1 : 00<jp>
Element abbr
Element abbr nie jest nowy w HTMLS i nie został przedefiniowany. Po c o więc zawracać sobie nim głowę?
Cóż, abbr został połączony z elementem acronym. Teraz element abbr reprezentuje wszystkie skróty. Atrybutu
t ; t l e możesz użyć do rozwinięcia skrótu, aby dać użytkownikowi podpowiedź:
<p><abbr t i tl e=" HyperText Markup Language">HTML<jabbr> j est standardem używanym przez twórców stron
�i nternetowych<jp>
W HTML 4 były dostępne oba znaczniki: abbr na oznaczenie typowych skrótów, j ak na przykład "dr",
i acronym dla akronimów, jak choćby "NATO". Ponieważ jednak często autorzy mieli wątpliwości co do tego,
którego z nich użyć, acronym został odrzucony i pozostał tylko abbr na oznaczenie ich obu.
Element h r
Element h r był używany do tworzenia poziomej kreski w dokumencie. Jego definicja została nieznacznie
poprawiona i teraz oznacza on przerwę po akapicie, taką jak zmiana miej sca akcji w książce. Zwykle jest
wyświetlany jako kreska albo wymyślny element graficzny pomiędzy fragmentami tekstu. Nie j est on obecnie
używany zbyt często, ponieważ w celu wstawienia dodatkowego miejsca, obrazka, kreski czy ozdoby po
odpowiednich fragmentach tekstu albo przed nimi, reprezentowanych przez elementy takie jak p, d ; v,
art ; c l e albo sect ; on, można użyć CSS-a.
To tak naprawdę wszystko, co jest do powiedzenia. Nie będziemy ogłaszać żałoby po tych elementach,
a jeśli obecnie ich używasz, natychmiast przestań!
<art i e l e>
<a href="arti c l e . htm">
<h2>< img al t="mi ni atura artykutu" sre = " t humb . j pg " hei ght = " lOO" w i dt h = " lQO" j>Tytut mojego artykutu<jh2>
<p>Pel l entesque hab i t ant morbi tri s t i que seneetus et netus et mal esuada fames ae turp i s egestas . <jp>
< l a>
<jart i e l e>
Elementem a możesz również opakować art i c l e; wiadomo jednak, że przeglądarki mają z tym pewne
problemy, więc proponujemy, by unikać tego rozwiązania.
66 Rozdział 2. Gru powanie, poziom tekstu i zm iany semantyczne
Listing 2.15 pokazuje opis osoby, która nazywa się Jan Kowalski, mieszka w Polsce i pracuje na stanowisku
kosmicznego kowala dla firmy Kosmiczni Kowale sp. z 0 . 0 .
Kiedy w jakimś elemencie jest użyty atrybut i temscope, element ten staje się j ednostką mikro danych.
Atrybut i temprop j est właściwością tej jednostki mikro danych i opisuje, co j est jej treścią. W przykładzie
mamy dwa atrybuty i temscope: jeden dotyczy całego fragmentu (Person), a drugi znajdującego się w nim
adresowego atrybutu i temtype.
Wartości i temprop w przykładzie pochodzą ze strony http://schema.org/, na której podane są nazwy
różnych typów danych (zdarzeń, produktów i tak dalej). Strona została uruchomiona w czerwcu 20 1 1 roku
jako wynik uwspólnienia schematów z Bing, Google i Yahoo!. Zawiera kilka przykładów i olbrzymi wybór
przykładowych danych. Możesz użyć swoich własnych wartości, lecz dla uzyskania spójnych wyników
powinieneś korzystać ze standardowych, uznanych nazw. Google również podaje przykłady i sugestie nazw
pod adresem http://support.google.com/webmasters/bin/answer.py?hl=pl&answer=99170. Używając narzędzia
do testowania opisów rozszerzonych dostępnego wśród narzędzi Google dla webrnasterów, możesz sprawdzić,
jak Google pokazałby Twoje mikrodane na stronie z wynikami wyszukiwania, co pokazano na rysunku 2.6.
Jan Kowalski
Rysunek 2.7 pokazuje inne informacje na temat treści zgromadzone przez Google.
Stosowan ie WAl-ARIA z HTML5 67
Ilem
url: example.com
Ilem l
To był tylko drobny przykład użycia mikro danych. Istnieje mnóstwo innych użytecznych możliwości,
których przykłady znajdziesz na wspomnianych wcześniej stronach Google. Oprócz narzędzi Google
istnieje kilka innych, które mogą pomóc przy tworzeniu mikrodanych: http://foolip.org/microdatajs/live/
i http://microdatajreebaseapps.com/.
I
Uwaga
M icroformats ( m i kroform aty) i R DFa to dwa inne sposoby rozszerzan i a HTML-a o określone inform acje.
Oba mają swoje zalety i wady. W rzeczywistości możesz łączyć m i krod ane z m i kroformata m i , a chociaż
m i krodane powo l i stają się standarde m , m i kroformaty są obecnie bardziej popularne.
opowiemy o fragmencie specyfikacji ARIA zatytułowanym "Landmark Roles"4 i o tym, jak możesz dodać te
nowe role do swojego dokumentu HTML5. Role są obszarami strony używanymi jako nawigacyjne punkty
orientacyjne. Specyfikacja wymienia ich ponad 50 (http://www.w3.orglTR/wai-aria/roles#landmark_roles),
a tutaj wskazujmy te bardziej popularne:
• ro l e= "art i c l e " ,
• ro l e= " banner",
• roi e= " compl ementary " ,
• ro l e= " conten t i n fo " ,
• ro l e = " form",
• ro l e = " head i ng",
• ro l e = "ma i n " ,
• ro l e= "navi gat i o n " ,
• ro l e = " searc h " .
</form>
Oznacza to, że ten konkretny formularz (na stronie może być kilka formularzy) jest używany do
wyszukiwania.
Przeglądając powyższą listę, możesz zauważyć, że niektóre role w oczywisty sposób nawiązują do nowych
elementów HTML5, a kiedy dodasz je do struktury głównej strony z poprzedniego rozdziału, otrzymasz
układ podobny do tego z rysunku 2.8.
Ponieważ używasz logicznej struktury znaczników wraz z rolami ARIA, pewnego dnia technologia
ułatwień dostępu będzie w stanie łatwo przemieszczać się do określonych obszarów treści strony. Jednak
obecnie czytniki ekranu w ograniczony sposób obsługują nie tylko HTML5, ale również elementy ARIA.
Nadal zaleca się używanie łączy pomijających na początku dokumentu, bardzo często ukrytych za pomocą
CSS-a, które pozwalają osobom nawigującym za pomocą czytnika ekranu, klawiatury albo innej technologii
ułatwień dostępu szybko "przeskoczyć" do ważnych obszarów, zwykle obszaru głównej nawigacji lub głównej
treści. Kod wyglądałby podobnie do tego:
<ul >
<l i ><a href= " #menu " >Przejdi do naw i gacj i </a></l i >
<l i ><a href= " #content " >Przejdi d o treśc i </a></l i >
<fu l >
Jednak przy użyciu ARIA punkty orientacyjne są oznaczone, dzięki czemu użytkownik będzie mógł
krążyć pomiędzy opcjami.
Walidacja HTML5 akceptuje role ARIA. Możesz używać ról ARIA także w HTML 4, ale spowodują one
błąd walidacji.
<secti on>
<secti on>
Role mogą być również używane w selektorach CSS-owych. Możesz mieć kilka nagłówków albo stopek
na stronie, ale jeśli chcesz nadać odmienny styl nagłówkowi i stopce głównej strony, to możesz się do nich
odwołać w CSS-ie w następujący sposób:
/* nadanie stylów wszystkim nagłówkom */
header { bac kground: red; border: 5px dotted bl ac k ; }
/* nadanie stylu głównemu nagłówkowi. który prawdopodobnie zawiera logo witryny */
header [rol e=banner] { bac kground: bl ac k ; border: 5px sol i d red ; }
/* nadanie stylu wszystkim stopkom */
footer { bac kground: bl ue ; border: 5px dotted green ; }
/* nadanie stylu stopce naszej witryny. która prawdopodobnie zawiera informację dotyczącą praw autorskich */
footer [rol e=content i nfo] { bac kground: green ; border: 5px sol i d bl ue ; }
Nie musisz koniecznie używać CSS-a w ten sposób, ale jest to użyteczna opcja do rozważenia.
Tematów związanych ze specyfikacj ą WAl-ARIA oraz dostępnością HTMLS jest na tyle dużo,
że zachęcamy Cię do dalszej lektury na następujących witrynach:
• http://www.w3.org/TR/wai-aria/,
• http://htmI5accessibility.com/,
• http://www.paciellogroup.com/blog!.
Listing 2 . 1 6 wykorzystuj e kilka ról stanowiących punkty orientacyjne ARIA użyliśmy ich do nadania
-
stylu znacznikom h l . Korzystamy również z elementu b do wyróżnienia pierwszej litery artykułu, dzięki
czemu j est trochę bardziej wyszukana. Kiedy nadajesz style elementom h l oraz b, nie musisz koniecznie
używać powyższego CSS-a, ponieważ istniej ą inne sposoby odwołania się do tych elementów; dobrze jednak
jest znać różne możliwości.
Podsumowanie 71
Blog Tomasza
!.Y.�.�.�..�.?J�g?...�.�.��.���.....................................................................................................................
Niedziela, 12 grudnia 20 l O
ellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas . Vestibulum tortO! quam,
P feugiat vitae, ultricies eget. tempor sit amet, ante. Donec fU libero sit amet quam egestas semper. Aenean ultricies
mi vitae est. Mauris placerat eleifend leo.
Pellentesque habitant morbi ttistique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam,
feugiat vitae, ultricies eget, tempor sit amet. ante. Danec eu libero sit amet quam egeslas semper. Aenean ultricies mi
vitae es't. Mauris placerat e leifend leo.
Komentarze
l. 2010-12-13 1 1 : 15
Komentarz
Tomasza Kowalskiego
Co za wspaniały artykuł!
2. 2010-12-16 1 1 : 15
Anonimowy komentarz
To jakieś bzdury.
Nowy element t i me został użyty kilkakrotnie, raz w przypadku głównego elementu art i el e z atrybutem
pubdate, a następnie w ramach każdego komentarza. W poprzednim rozdziale przeczytałeś, że komentarz
użytkownika może być umieszczony w elemencie a rt i e l e, więc użyliśmy go tutaj jako tytuł wykorzystując
datę i czas. Moglibyśmy umieścić w tytule autora komentarza, ale nie chcemy mieć w konspekcie powtórzeń,
a używanie daty i czasu gwarantuje unikalność. Wybór należy do Ciebie, za tytuł może posłużyć również
autor komentarza.
Zastosowana została również lista uporządkowana, tak aby każdy komentarz miał numer, co nie tylko
porządkuj e komentarze, ale daje nam również możliwość nadania stylu. Użyliśmy atrybutu reversed
w elemencie o l , ponieważ w tym przypadku chcemy, by ostatni komentarz był umieszczony na początku.
Moglibyśmy potencjalnie dodać przełącznik "posortuj po dacie" oraz za pomocą J avaScriptu dodawać lub
usuwać atrybut reversed. Oczywiście nie musisz robić tego w taki sam sposób, istnieje mnóstwo alternatyw,
a układ komentarzy może sprawić, że będziesz musiał rozważyć inne rozwiązania.
Podsumowanie
W tym rozdziale poznałeś wiele nowych elementów oraz takich, które zostały nieznacznie poprawione
w HTMLS. Nowe elementy, takie jak fi gure i deta i l s, na pewno ułatwią w przyszłości życie programistów,
a dzięki nowym rolom ARIA możesz strukturyzować swoj e dokumenty, nadając im lepszą semantykę
i sprawiając, że są bardziej dostępne. Możesz nawet zamieszczać dodatkowe informacje w swoim HTML-u
przy użyciu mikro danych, tak aby wyszukiwarki miały możliwość udostępniania bogatszych i dokładniejszych
informacji.
72 Rozdział 2 . Gru powanie, poziom tekstu i zm iany semantyczne
3
Obsłu g a prze glądarek w HTML5
C
hociaż specyfikacj e HTMLS i związanych z nim technologii nie są ukończone i caly czas się zmieniają,
wielu spośród dostępnych funkcji możesz już używać. HTMLS jest rozwij ającym się językiem i zawiera
coraz więcej javascriptowych API . Producenci przeglądarek ciężko pracują, by nadążać za rozwojem nowych
specyfikacji. Ze względu na te "ruchome cele" - nowe funkcjonalności i nowe wersje przeglądarek - my,
programiści, możemy się często natknąć na problemy z obsługiwaniem przez przeglądarki pewnych funkcji.
W tym rozdziale dowiesz się, jak radzić sobie z różnicami pomiędzy przeglądarkami i jak wypełniać luki
przy użyciu dostępnych sztuczek, wskazówek i skryptów.
Oczywiście pisanie tego dla każdego nowego elementu HTMLS byłoby nieco nużące i istnieje ryzyko,
że coś byś pominął. Na szczęście możesz używać HTMLSShiv (http://code.google.com/p/htmI5shiv), napisanego
przez Remy'ego Sharpa, zawieraj ącego wszystkie nowe elementy oraz dodatkowo kawałek JavaScriptu,
zwany lE Print Protector, który pomaga lE poprawnie wyświetlać elementy HTMLS. HTMLSShiv j est tak
zaprojektowany, by umożliwić wersjom przed lE9 rozpoznawanie elementów HTMLS i pozwolić na nadawanie
im stylów przy użyciu CSS-a.
Chociaż skrypt zawiera kod warunkowy, który może być uruchamiany tylko w lE, możesz również
opakować go we własny warunkowy komentarz, tak by tylko lE8 i starsze wersje przeglądarki pobierały
i uruchamiały kod. Skrypt musi być umieszczony w znaczniku head oraz przed odwołaniami do arkuszy
stylów, tak jak to pokazano na listingu 3 . 1 . Od Ciebie zależy, czy wolisz serwować plik html5shiv lokalnie,
czy linkować bezpośrednio do pliku dostępnego ze strony http://code.google.com. Udostępnianie pliku lokalnie
zapobiegnie zarówno błędom nieznalezienia zasobu ("resource not found"), spowodowanym j akimiś
problemami ze zdalnym dostępem, jak i błędom wynikającym z przyszłych modyfIkacji javascriptowego pliku.
Jeśli stosujesz resetowanie CSS-a, możesz włączyć kod z listingu 3.2 albo jeden z dostępnych obecnie
resetów CSS-a, które zawierają poprawki HTMLS:
• http://meyerweb.com/eric/thoughts/20 1 1/01/03/reset-revisited,
• Modernizr (który omówimy dalej w tym rozdziale),
• http://htmI5doctor.com/html-5-reset-stylesheet.
Testowanie działan i a nowych możl iwości HTML5 75
Szablony ( boilerplates)
Czy jesteś nowicjuszem, czy weteranem w branży tworzenia stron i aplikacji internetowych, nauczyłeś się
pewnych praktyk programistycznych oraz uzbierałeś różne wycinki kodu, które ponownie wykorzystujesz
w prawie każdym projekcie. Jeśli na zorganizowanie tych komponentów poświęciłeś dużo czasu, możesz
mieć nawet swój własny szablon, na który składa się szereg folderów i plików, pozwalaj ących Ci na szybkie
rozpoczęcie projektu. Istnieje kilka popularnych szablonów, które są chętnie używane przez społeczność
sieciową: http://htmI5boilerplate.com i http://htmI5reset.org. Dodatkowo szablony te zostały już rozszerzone:
możesz wygenerować wzorce dla Twojej konkretnej strony internetowej albo aplikacji poprzez witryny takie
jak http://www.initializr.com. które wykorzystują HTMLS Boilerplate.
Strony te, a HTMLS Boilerplate w szczególności, zawieraj ą mnóstwo dokumentacji, instrukcji oraz
wskazówek i sztuczek odnośnie do Twojego projektu internetowego, w tym kod HTML, skrypty, CSS
i techniki optymalizacji.
Aby używać tych szablonów sensownie, nie wystarczy tylko je ściągnąć i stosować, ale trzeba zrozumieć,
co w nich j est, i dodawać bądź usuwać rzeczy, których nie potrzebujesz albo z którymi się nie zgadzasz,
do czego masz zresztą prawo . Dzięki szablonom możesz po prostu rozpocząć proj ekt i skorzystać z wysiłku
wielu umysłów pracujących nad tym, by dostarczyć strukturę działania i poprawną obsługę HTMLS w różnych
przeglądarkach. Możesz nawet ostatecznie utworzyć swój własny, odpowiednio przystosowany szablon
w oparciu o właściwości uznane przez Ciebie za ważne.
Wskazówka
H T M L5 Bo ilerplate ma długą l istę możl iwości do wykorzystan i a na początku Twojej pracy nad
projektem . Szablony zawierają modele zgodności przeglądarek, u możl iwiają obsługę HTML5
w starszych przeglądarkach , ułatwiają projektowan ie dla u rządzeń m o b i l nych , a nawet oferują
s krypt optymalizacj i budowan i a . Zalecamy, abyś wypróbował pełen zbiór możl iwości pod adresem
http://htm/5boi/erp/ate.com. A jeś l i potrzebujesz na począte k gotowego szablonu, wypróbuj
http://www.initia/izr.com. który za pomocą HTML5 Boilerplate wygeneruje d l a C iebie wzorce stro n .
<jscri pt>
<jhead>
<body>
<jbody>
<jhtml >
J ak pokażemy w tej książce, za pomocą javascriptowych API można dość łatwo sprawdzić obecność
danego API. Jeśli j ednak próbujesz sprawdzić w przeglądarce dostępność elementu HTMLS, takiego j ak
element canvas, proces jest trochę bardziej złożony. Najczęściej tworzy się taki element, a następnie próbuje
pobrać j ego domyślną metodę albo atrybut. Jeśli zwróconą wartością j est nul l albo undefi ned, oznacza to,
że element nie jest obsługiwany przez przeglądarkę. Listing 3.4 pokazuje przykład sprawdzenia, czy element
canvas jest obsługiwany.
<jscri pt>
<jhead>
<body>
<jbody>
<jhtml >
Najpierw utworzyłeś nowy element typu canvas, po czym sprawdziłeś, czy udostępnia on metodę
getContext. Metody elementu canvas poznasz w dalszej części książki, ale w tym punkcie wystarczy wiedzieć,
że metoda getContext j est nieodzowną i domyślną metodą każdego obiektu canvas. Jeśli zatem metoda
getContext nie istniej e w tym elemencie, to sam element nie istniej e i canvas nie j est obsługiwane przez
tę przeglądarkę·
Wykorzystanie jQuery do zastąpienia kalendarza 77
0 Paź ·· G � �
Pn WI Śr Cz PI Sa N
1 2 3 4 5 6 7
8 9 10 1 1 12 13 14
15 16 17 18 19 20 2 1
2 2 23 2 4 25 26 27 2 8
29 30 3 1 2 3 4
5 6 7 9 10 1 1
Dzisiaj I
Rysunek 3 . 1 . Element <input type= "date "j> wyświetlony w przeglądarce Opera 1 1 . 61
Przy użyciu JavaScriptu należy utworzyć element w DOM, dodać do niego atrybut typu date, a następnie
przetestować, czy przeglądarka go obsługuje. Jeśli tak, nie musisz nic robić. Jeśli jednak przeglądarka go nie
obsługuje, musisz dołożyć widżet kalendarza. Rysunek 3.2 pokazuje kalendarz w Internet Explorerze, a na
listingu 3.5 widać tworzący go kod. W tym przykładzie użyliśmy j Query, by dostarczyć funkcjonalności
kalendarza, więc dla ułatwienia podlinkowaliśmy bezpośrednio potrzebne pliki j Query.
i l
Pn Wt Śr Cz Pt 50 N
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
$ (funct i on ( ) {
function i nputSupport () {
var i nput = document . createEl ement ("i nput") ;
i nput . setAttri bute ( "type " , "date") ;
var val = (i nput . type ! == "text") ;
del ete i nput;
return val ;
}
i f ( ! i nputSupport O I I ($. browser.webkit» {
Ilalert("typ wejściowy date niejest wspierany");
$ ( ' i nput [type=date] ' ) . datepi cker({
dateFormat : ' yy-mm-dd ' II ten samformat co w specyfikacji HTML5
}) ;
}
});
<jscri pt>
<jhead>
<body>
<form>
<l abel for= " date" >Którego dni a wyj eżdżasz?<jl abel > < i nput requ i red type= " date" i d = " date" name = " date" j>
< i nput type= " subm i t " val ue= "Wyśl i j " j>
<jform>
<jbody>
<jhtml >
Ci Z Was, którzy mają sokole oko, na pewno zauważyli następujący kawałek JavaScriptu: ($ . browser. webki t) .
Uwzględniliśmy go na listingu 3.5, ponieważ - co jest bardzo irytujące - przeglądarki Web Kit wykrywajq
<i nput type= "date ">, ale obecnie nic z nim nie robią. To samo dzieje się z typami wejściowymi ema i l , number,
tel oraz url . A zatem ten mały kawałek JavaScriptu pozwala się upewnić, że widżet kalendarza j Query działa
w Safari i Chrome. Mamy nadzieję, że wkrótce przeglądarki oparte na silniku Web Kit będą domyślnie robiły
coś fajnego z tymi typami wejściowymi, tak byśmy mogli pozbyć się kodu wykrywania przeglądarki.
Kod javascriptowy z listingu 3.5 służy do wykrycia obsługi tylko typu danych date. Jeśli chcesz wykrywać
inne typy, musisz wprowadzić drobne poprawki do poprzedniego kodu, tak abyś mógł testować kilka typów
elementu i nput. Spójrz na przykład na listing 3.6.
Wykorzystan ie b i b l ioteki Modernizr do wykrywan ia możl iwości 79
$ (funct i on ( ) {
funct i on i nputSupport ( i nputType) {
var i nput = document . createEl ement ( " i nput " ) ;
i nput . setAttri bute ( " type " , i nputType) ;
var val = ( i nput . type ! == " text " ) ;
del ete i nput ;
return va l ;
Jeśli masz niewielką witrynę, zawierającą tylko kilka nowych elementów HTMLS, którą chcesz udostępnić
dla wszystkich przeglądarek, opisane przed chwilą podejście może być odpowiednie, ponieważ jest ono
dość poręczną poprawką. Jeśli jednak musisz wykrywać, czy przeglądarka obsługuje jakieś funkcje, w wielu
miejscach kodu, stanie się to nużące, a w związku z tym możesz rozważyć używanie javascriptowej biblioteki,
takiej j ak Modernizr.
• sessionStorage,
• Drag and Drop,
• History Management,
• applicationCache,
• Canvas,
• Web Sockets,
• Web Workers,
• Web SQL Database,
• Input Types,
• Input Attributes.
Biblioteka Modernizr (albo mikrobiblioteka, jak chcą jej autorzy) jest prosta w użyciu i została wbudowana
w wiele większych i mniejszych stron na całym świecie. Dopóki wszystkie przeglądarki nie będą jednolicie
obsługiwać wszystkich możliwości HTMLS, CSS3 i javascriptowych API, będziemy potrzebować takich
narzędzi jak Modernizr.
J ak się jej w takim razie używa? By wygenerować swój plik, musisz wybrać spośród różnych cech HTMLS
te, których obecność chcesz wykryć. Wybranie tylko kilku z nich jest korzystne, ponieważ pozwala utrzymać
mały rozmiar pliku i zmusza Cię do myślenia bardziej konkretnie o funkcjach, jakie będzie posiadać Twoja
strona internetowa. Najpierw więc generujesz plik ze strony oraz, jak pokazano na listingu 3.7, dołączasz link
wewnątrz elementu head i dodajesz klasę no-j s do znacznika html .
Skrypt Modernizr po dołączeniu uruchomi się automatycznie i zastąpi atrybut klasy no-j s dodany do
elementu html . Skrypt dynamicznie dołączy klasy CSS dotyczące tych funkcjonalności, które przeglądarka
obsługuje, a także tych funkcjonalności, których przeglądarka nie obsługuje. Poniższy kod pokazuje znacznik
html po tym, jak skrypt Modernizr został uruchomiony i zastąpił atrybut c l ass w Chrome w wersji 8. Jak widać,
przeglądarka Chrome 8 obsługuj e wiele atrakcyjnych funkcji CSS3 i HTMLS, ale nie wspiera na przykład
WebGL, w związku z czym została dodana klasa no-webgl .
<html l ang= " p l " c l as s = " j s fl exbox canvas canvastext no -webgl no-touch geol ocat i on postmessage websql database
�no - i ndexeddb hashchange h i s tory draganddrop websockets rgba hs l a mul t i pl ebgs bac kgrounds i ze borderimage
�borderra d i us boxshadow textshadow opac i ty cssan imat i ons csscol umns cssgradi ents cssrefl ect i ons csstransforms
�no-csstransforms3d csstrans i t i ons fontface v i deo aud i o l ocal storage sess i onstorage webworkers
�appl i cat i oncache svg i nl i nesvg smi l svgcl i ppat h s " >
Wykorzystan ie b i b l ioteki Modernizr do wykrywan ia możl iwości 81
Na listingu 3.8 pokazaliśmy, j ak sprawdzić, czy obsługiwane jest Session Storage API2, ilustrując, w jaki
sposób Modernizr ujawnia w tagach CSS-a informacj e o działaniu różnych funkcji. Jeśli przechowywanie
danych w pamięci sesji jest dostępne, zostanie użyta klasa stylów . ses s ; onstorage, a jeśli nie jest obsługiwane,
używana będzie klasa stylów . no-ses s ; onstorage.
w powyższym przykładzie odpowiedni blok d; v zostanie wyświetlony w zależności od tego, czy wspomniane
API jest obsługiwane. Możesz używać tego sposobu przy dowolnym spośród j avascriptowych API. Spis
wszystkich właściwości udostępnionych w obiekcie Modernizr jest dostępny na stronie http://modernizr.com,
w części zawierającej dokumentację.
Choć pokazany sposób jest użyteczny przy programowaniu w języku CSS, Modernizr zawiera mnóstwo
właściwości javascriptowych, które mogą być używane do wykrywania możliwości HTMLS. Na listingu 3.9
informujesz użytkownika o tym, czy przechowywanie danych w pamięci sesji jest dostępne, w zależności od
wartości jednej z właściwości obiektu Modern ; zr.
Listing 3.9. Wykrywanie możliwości HTML5 przy użyciu obiektu Modernizr oraz JavaScriptu
<jscri pt>
<jhead>
<body>
</body>
</html >
Modernizr możesz również używać łącznie z j Query i innymi bibliotekami. Na listingu 3.5 zastosowaliśmy
javascriptowe rozwiązanie do udostępnienia widżetu kalendarza przeglądarkom, które nie obsługuj ą typu
date elementu ; nput z HTMLS. Na listingu 3 . 1 0 odtworzyliśmy ten przykład, używając biblioteki Modernizr
jako narzędzia wykrywającego.
Polyfilling ( wielowypełnianie)
Możliwość wykrywania, jakie cechy HTMLS są obsługiwane - albo za pomocą Twojego własnego kodu,
albo przy użyciu biblioteki Modernizr - to tylko połowa sukcesu. Koniec końców celem jest obsługiwanie
funkcjonalności HTMLS w różnych przeglądarkach i wersjach przeglądarek, które użytkownicy wykorzystuj ą
Polyfi l l ing (wielowypełn ianie) 83
Wskazówka
Metoda Modern i z r . l oad jest w rzeczywistości a l i asem polece n i a dołączonej zewnętrznej b i b l ioteki
javascri ptowej o n azwie yepnope.js, którą możesz znaleźć pod adresem http://yepnopejs.com.
B i b l ioteka jest przeznaczona do tego , by być szyb k i m , warun kowym i m aj ącym m ały rozm iar
programem ładującym zasoby. Może być używana oddzie lnie albo poprzez al ias metody ładuj ącej
b i b l ioteki Modern izr. Użycie javascri ptowej b i b l ioteki yepnope jest bardzo korzystne, gdyż pozwala
warun kowo ładować CSS albo J avaScri pt dzięki testowan i u różnych rodzajów o b i e któw d anych
- od napisów do tablic.
Dostępnych j est wiele już napisanych wielowypełniaczy dla mnóstwa komponentów w różnych
przeglądarkach; ich długą listę możesz znaleźć pod adresem https://github.com/Modernizr/Modernizr/
wiki/HTML5-Cross-browser-Polyfills. Ta pożyteczna lista jest prowadzona przez Paula Irisha, głównego
programistę biblioteki Modernizr, i zawiera rezerwowe implementacje dla różnego rodzaju możliwości,
w tym canvas, v i deo, geo l ocat i on, forms i wielu innych.
Uwaga
Zastosowan i e różnych wielowypełn i aczy, s h i mów i b i b l i otek umożliwiaj ących obsługę możl iwości
HTML5, które obecnie mogą nie być dostępne w n i e których przeglądarkach , jest świetnym sposobem
dostarczan i a jednol itego zachowan i a użytkown i kom wykorzystującym różne przeglądarki . Jeś l i jed nak
mus isz ładować d użo wielowypełn iaczy, może to m ieć negatywny wpływ n a wydajność. Dłuższy czas
ładowania powinien być zrównoważony uzys kaną funkcjonalnością i poziomem obsługi poszczególnych
funkcj i .
Podsumowanie
W tym rozdziale poznałeś niektóre metody radzenia sobie ze starymi wersjami przeglądarek i brakami
w przeglądarkach w odniesieniu do HTMLS. Wprawdzie istnieje już szeroki zakres wsparcia różnych
CSS-owych, HTML-owych i javascriptowych dodatków tworzących HTMLS, ale jest to standard, który
jest nadal rozwijany. Dowiedziałeś się również o resetach CSS-a i szablonach, które zawierają nowoczesne
i aktualne narzędzia, wskazówki oraz techniki tworzenia stron internetowych.
Poznałeś możliwości użycia biblioteki Modernizr, potężnego skryptu wykrywającego cechy HTMLS,
umożliwiającego Ci oferowanie rezerwowych rozwiązań dla przeglądarek, które j eszcze nie obsługują
niektórych j ego nowych cech. Nie będziemy pokazywać w książce, j ak używać biblioteki Modernizr,
ponieważ Ty sam musisz zdecydować, czy w ogóle ją stosować na swoich własnych stronach, i ponieważ
próbuj emy pokazać tu podstawowe sposoby rozwiązywania problemów. Zachęcamy jednak do korzystania
z tej biblioteki albo jednego z innych narzędzi wspomnianych w tym rozdziale, gdyż przyniesie Ci to wiele
korzyści podczas pisania kodu HTMLS.
4
Nowe techniki dotyczące układu
i stylizacji w CSS3
CSS
poziomu 3 (CSS3) jest najnowszym wydaniem specyfikacji CSS. Zostało ono oparte na poprzednich
wersjach, zawiera j ednak nowe możliwości, które możesz zaimplementować w najnowszych
przeglądarkach, by poprawić wygląd, łatwość użycia, dostępność i wydajność swoich stron internetowych.
CSS to nie HTMLS - wiemy o tym i Ty także powinieneś to wiedzieć, ale dołączyliśmy ten rozdział, ponieważ
często współdziałaj ą one ręka w rękę, a my chcemy pokazać Ci tylko niektóre z możliwości dostępnych
w CSS3, abyś mógł nadać Twoim witrynom HTMLS fantastyczny wygląd. W tym rozdziale powiemy sobie
o witrynach reaguj ących na zmiany, proj ektowanych z użyciem Media Queries, zewnętrznych czcionek,
gradientów, przejść i transformacji oraz, na końcu, animacji. Nie opisujemy wszystkich nowych możliwości
CSS3; odkrywamy j edynie rąbek tego, co jest dostępne.
Android 2 . 3+
Chrome 13.0+
Fi refox 4.0+
Internet Explorer 9 . 0+
iOS Safari 4.0+
Opera 11.0+
Safari 5 . 0+
<l i nk rel = " styl esheet ll href= " s creen . cs s " med i a = " screen" />
<l i nk rel = " styl es heet " href= " pri nt . cs s " med i a= " pr i n t " />
Teraz jednak możesz być bardziej wyrafinowany dzięki wykorzystaniu zapytań o media w CSS3 (listing 4 . 1 ) .
<l i nk rel = " styl es heet " med i a= " screen and
(max-devi ce-wi dth : 480px) " href= " smartphane . cs s " />
<l i nk rel = " styl es heet " med i a= " screen and
(mi n-width: 480px) " href= " screen . cs s " />
Listing 4.1 odpytuj e urządzenie, czy j ego rozdzielczość pozioma to 480 pikseli albo mniej . Jeśli tak j est,
możesz przypuszczać, że jest to smartfon, i ładujesz arkusz stylów smartphone.css. Następnie sprawdzasz,
czy rozdzielczość to przynajmniej 480 pikseli; jeśli tak jest, możesz użyć innego arkusza stylów. Potencjalnie
możesz mieć kilka różnych zapytań o media, jeśli chcesz obsługiwać rozmaite rodzaje urządzeń, rozdzielczości
i orientacji ekranu. Możesz mieć arkusze stylów dla smartfonów, smartfonów z orientacją poziomą, ekranów
androidowych, iPada, iPada z pionową orientacją, przeglądarek z rozdzielczością poziomą mniejszą od 800
pikseli albo przeglądarek z rozdzielczościami szerokoekranowymi. Tak naprawdę możesz być tak dokładny,
jak chcesz.
Będziesz potrzebował dobrego sposobu organizacji wszystkich oddzielnych zapytań. Pamiętaj, że będziesz
także miał wszystkie swoje główne arkusze stylów. Używanie podejścia z listingu 4.1 oznaczałoby dużą liczbę
żądań HTTP w znaczniku head.
Zamiast tego możesz umieścić zapytania o media wewnątrz pliku CSS przy użyciu @med ; a, jak pokazano
na listingu 4.2.
bady { ba c kgraund : b l ac k ; cal ar: #fff; fan t : narmal 62 . 5%/ 1 . 5 tahama , verdan a , sans-seri f ; }
h l { fant - s i z e : 2em; }
p { fant- s i z e : 1 . 4em ; }
/* style dla smartfonów i bardzo małych rozdzielczości ekranu */
@medi a anly screen and (mi n -wi dt h : 320px) and (max-wi dt h : 400px)
{
bady { ba c kgraund : b l ue ; }
}
/* style dla rozdzielczości ekranu większej od smartfonów. ale mniejszej lub równej J 024px */
@medi a anly screen and (mi n -wi dt h : 401px) and (max-wi dt h : 1024px)
Tworzen ie dostosowującego się projektu za pomocą CSS3 Med ia Queries 87
W kodzie z listingu 4.2 zmienialiśmy kolor tła w zależności od rozdzielczości ekranu. Wszystkie zapytania
o media są częścią tego samego dokumentu CSS, więc ważne jest, by był on odpowiednio zorganizowany
- prawdopodobnie będziesz miał setki wierszy kodu Twojego projektu i dodatkowo CSS z zapytaniami
o media. W zależności od konfiguracji Twojej witryny prostszym sposobem może być edytowanie zapytań
o media, na przykład poprawienie m i n-wi dth : 2000px na mi n-wi dth : 2500px, wewnątrz pliku CSS zamiast
na wszystkich stronach HTML.
Używając składni and, możesz łączyć różne zapytania. Możesz używać kilku właściwości, takich jak wi dth,
hei ght, dev i ce-wi dth, dev i ce-hei ght, o r i entat i on, aspect- rat i o, dev i ce-aspect-rat i o, co1 or, co1 or- i ndex,
monoch rome, reso 1 ut i on, scan i gri d, wraz z rozpoznawanymi rodzajami mediów: a 1 1 , bra i 1 1 e, embos sed,
handhe 1 d, pri nt, project i on, screen i speech. Dzięki połączeniu ich ze sobą będziesz jednocześnie obsługiwać
kilka różnych typów urządzeń i rozmiarów. Warto zauważyć, że max-dev i ce-w i dth j est rozmiarem ekranu
urządzenia (takiego jak iPhone), podczas gdy max-wi dth jest tylko szerokością wyświetlającego obszaru (takiego
jak okno przeglądarki); spójrz na różnice w poniższym kodzie:
/* urządzenia mniejsze od 480px; urządzenia przenośne. iPhone i zwykłe ekrany */
@medi a handhel d and (max-w i dt h : 480px) , screen and (max-dev i ce-w i dt h : 480px) , screen and (max-wi dt h : 480px) {
/* tutaj umieszczasz style */
}
/* układ dla iPada w trybie poziomym */
@medi a onl y screen and (mi n-devi ce-w i d t h : 768px) and (max-devi ce-w i d t h : l024px) and (ori entat i on : l andscape) {
/* tutaj umieszczasz style */
}
Wskazówka
Na kolejnych l istingach użyliśmy k i l ku różnych rodzajów selektorów CSS . Pierwszym z n ich jest selektor
atrybutu użyty w przypadku header ero 1 e=banner] , a d rugim - selektor pseudo klasy, nth-xx O , taki
jak s ect i on : nth -of-type ( 1 ) , wykorzystany w k i l ku przypad kach . Selektory atrybutu nie są nowością
w CSS3, ale w powiązan i u z nowym i ro l a m i ARIA d aj ą więcej możl iwości używan i a selektorów wraz
z CSS-em . Istnieje wiele nowych selektorów CSS3, włącznie z nth-ch i 1 dO i nth-of-type O . Korzystanie
z tych nowych selektorów oznacza, że możesz zrezygnować z używan i a klas przy wielu e lementac h .
Na przykład przy użyciu nth-of-type O możesz wybrać c o drugi wiersz tabeli a l b o element listy. Więcej
inform acj i n a ten temat uzyskasz, czytając http://quirksmode. orgjcssjnthchi/d.htm/.
Wiele s pośród tych nowych selektorów n ie dzi ała w starszych wersj ach I nternet Explorera, więc
polecamy Selectivizr ( http://se/ectivizr. com/) - łatwe w użyciu n arzędzie j avascri ptowe , które
powoduje, że I nternet Explorer rozu mie nowe selektory CSS.
<body>
<nav><jnav>
<seet i on> < 1-- ten element sectionjest powtórzony jeszcze trzykrotnie _o>
<art i e l e>
<header></header>
< i mg al t = " t humbnai l " sre = " i mages/box-lOOx lOO . g i f " />
<p></p>
<fart i el e>
</see t i on>
</body>
<styl e>
* {marg i n : O; padd i ng : O ; }
body { ba e kground : #fff; eol or: #000 ; font : normal 62 . 5%/1 . 5 " Pa l at i no Li notype " , " Book Ant i qua" , Pal at i no ,
�ser i f ; marg i n : O aut o ; w i dt h : l260px}
header, nav , see t i on , art i e l e , footer, as i de { d i spl ay: bl oe k ; }
header [rol e=banner] {marg i n : 10px O 20px ; text -al i gn : center ; }
header [rol e=banner] h l { bae kground: url ( i mages/l ogo . g i f) top center no-repeat ; font-s i z e : 5em ;
�paddi ng: 100px O O; text-trans form: upperease ; }
header [rol e=banner] h 2 { font-styl e : i tal i e ; }
header, nav { e l ear: bot h ; w i d t h : 100% ; }
nav { border-bottom: lpx dotted #eee ; paddi ng-bottom : 20px ; text-al i gn : center; }
nav l i { d i spl ay: i n l i ne ; }
nav l i a { font-s i ze : 1 . 4em; padd i ng : . 5em; }
see t i on { fl oat : l eft ; marg i n : O O O 10px ; padd i ng : 10px ; w i d t h : 345px ; }
see t i on h l {marg i n : O O 10px ; }
see t i on art i e l e {marg i n : O O 10px ; }
see t i on art i e l e header p { font-s i ze : lem ; font -we i gh t : bol d ; marg i n : O O lOpx ; }
see t i on art i e l e i mg { fl oa t : l eft ; marg i n : O 5px 5px O ; }
see t i on : nth-of-type ( l ) { e l ear: bot h ; marg i n : O O 10px; mi n-hei ght : 200px; padd i ng : 1% 1% 1% 30% ; pos i t i on :
�rel at i ve ; w i d t h : 69%; }
see t i on : nth-of-type ( l ) art i el e {marg i n : O ; }
see t i on : nth-of-type ( l ) art i el e i mg { fl oa t : none; h e i g h t : 200px ; l eft : O ; pos i t i on : absol ute ; top : 10px ;
�wi dt h : 360px}
see t i on : nth-of-type (2) {margi n-l eft : O ; }
as i de {fl oat : r i g ht ; marg i n - l eft : 10px ; w i d t h : l30px ; }
as i de img { border: lpx sol i d #ee e ; di spl ay: bl o e k ; marg i n : O auto 10px ; }
footer { e l ear: bot h ; }
h l { font - s i z e : 2em; }
p { font- s i z e : 1 . 4em ; }
</styl e>
Tworzen ie dostosowującego się projektu za pomocą CSS3 Med ia Queries 89
MNÓSTWO WIADOMOŚCI
Rysunek 4.2 pokazuje układ na mniejszym ekranie z wykorzystaniem zapytań o media z listingu 4.5.
/* style dla rozdzielczości ekranu większych od smartfonów. ale mniejszych lub równych 1280px */
@medi a only sereen and (mi n -wi dt h : 48 1px) and (max-wi dt h : 1259px)
{
body { w i d t h : 800px ; }
see t i on {marg i n : O O O lOpx ; }
see t i on : nth-of-type ( l ) , see t i on { e l ear: non e ; fl oat : l eft ; padd i ng : lOpx; w i dt h : 375px ; }
see t i on : nth-of-type ( l ) art i el e i mg {fl oat : l eft ; he i ght : auto ; pos i t i o n : rel at i ve ; w i d t h : auto ; }
see t i on : nth-of-type (2) {margi n-l eft : lOpx ; }
see t i on : nth-of-type (3) { e l ear: bot h ; marg i n - l eft : O ; }
as i de { e l ear: bot h ; fl oat : l eft ; w i d t h : lOO% ; }
as i de img { fl oat : l eft ; marg i n : O lOpx O O ; }
Wreszcie, listing 4.6 zawiera zapytania o media w CSS-ie przeznaczone dla smartfonów albo znacznie
zmniejszonego okna przeglądarki (rysunek 4.3). Zasadniczo tych kilka dodatkowych wierszy CSS-a sprawia,
że w porównaniu z ekranem o mniejszych rozmiarach niektóre elementy są ukrywane lub zmniejszane.
90 Rozdział 4. Nowe techn i ki dotyczące u kład u i styl izacj i w CSS3
MNÓSTWO WIADOMOŚCI
Sport
:o.�ptKer.�dhlfl'dlolo.�lil_tilftur-en
uJ�płUtftI•. \'� ...I1 ww. �
�"'łIM�mort-.. lnJtICiIII' _ �IMt1łtt\
I:tHWlDduNOlIIda-.um. _1ptWn
Nd. -,,* '"'IIA. II'IJw."''''''' '''''' MnIi
..
m.&lHwd.I l_ M NlJ'd epIUL
MNtI'Iora.�"wm;wLAaol_łoCoiIui.o.-cMn
rH1lWftlD'l. ....
KOlllpuh!l')'
"O"'ł'II!"'J,..;:txI\llj
.---
wytworna wytworna
reklama reklama
MNÓSTWO WIADOMOŚCI
Rozrywka
Rozrywka - tyłuł 1
Rozrywka - tytuł 2
Rozrywka - tytuł 3
Komputery
Komputery - tytuł 1
Komputery - tytuł 2
Komputery - tytuł 3
Rysunek 4.3. Witryna z wiadomościami na smartfonie
Korzystanie z zapytań o media oznacza na ogół pokazywanie lub ukrywanie treści w zależności od
rozmiaru ekranu. Wiele witryn, takich jak http://youtube.com/, http://facebook.com/, http://cnn.com/ oraz
http://nfl.com/, wykrywa, czy użytkownik używa urządzenia przenośnego, i przekierowuj e go na wersj e
przeznaczone dla takich urządzeń. Witryny te zawieraj ą dużo treści z mnóstwem danych, zdjęć, filmów
wideo, reklam, Flasha i innych rzeczy - gdyby korzystać tylko z zapytań o media, smartfon nadal musiałby
pobierać wszystkie te dane, mimo że użytkownik nie mógłby ich zobaczyć. A zatem to, czy potrzebuj esz
jedynie nowych stylów, czy zupełnie oddzielnej witryny dla urządzeń przenośnych, zależy od treści witryny,
ale jeśli masz zmienić j edynie układ i dodać kilka szczegółów, wtedy powinieneś prawdopodobnie użyć
zapytań o media. Kilka znakomitych sposobów ich użycia znajdziesz na http://mediaqueri.es/.
Android 2 . 3+
Chrome 13.0+
Fi refox 4.0+
Internet Explorer 6 . 0+
iOS Safari 4.0+
Opera 11.0+
Safari 5 . 0+
I
Uwaga
Deklaracja @font- face została włączon a do s pecyfikacj i CSS2 w 1998 roku , j e d n a k była w zasadzie
n ieużywan a z powodu słabej im plementacj i w przeglądarkach, zamieszania z rodzaj a m i p l i ków czcionek
i obaw dotyczących problemów prawnych i licencyjnych związanych z wykorzystywan iem czcionek. Internet
Explorer w wersj i 4 obsługiwał nawet dodatkowe , zewnętrzne czcion ki, choć tyl ko w formacie EOT.
Używając deklaracji @font-face, możesz osadzić na stronie swoje własne czcionki za pomocą j edynie
kilku wierszy CSS-a. Na rysunku 4.4 pokazaliśmy czcionkę Anagram (dalej podamy informacje o tym, skąd
zdobyć czcionki) w elemencie h l, chociaż może być ona użyta w dowolnym elemencie.
, ,
JANOSTWO WI�DOA\OSCI
Rysunek 4.4. Własna czcionka wyświetlona przy użyciu @font-face
Listing 4.7 pokazuje kod potrzebny do uzyskania efektu z rysunku 4.4 i prezentuje przykład najprostszego
użycia @ font-face. Podajemy nazwę czcionki Anagram jako wartość właściwości font- fami l y (można
nazwać ją dowolnie), a później będziemy się ponownie do niej odwoływać; spójrz na wartości czcionek h l .
W deklaracji @font-face właściwość s rc wykorzystuje ten sam katalog, w którym znajduje się strona HTML;
jeśli chcesz, możesz umieścić czcionkę w innym katalogu.
<styl e>
@ font - face {
font- fami l y : Anagram;
Korzystanie z własnych czcionek przy użyciu @font-face 93
<jstyl e>
<header rol e= " banner">
<hgroup>
<hl>Mnóstwo wi adomośc i <j h l>
<h2>Dostarczamy Ci najróżn i ej szych wi adomoś c i ! <jh2>
<jhgroup>
<jheader>
WOFF j est nowym sieciowym standardem krojów pisma. Internet Explorer w wersjach 8 i starszych
wymaga czcionki .eot, ale Internet Explorer 9 obsługuje nowy format WOFF. A chociaż Safari obsługuje .ttf
już od jakiegoś czasu, dopiero niedawno iPhone i iPad zaczęły wspierać ten format; wcześniej potrzebowały
czcionki .svg. Powinieneś więc dostarczać tę samą czcionkę w różnych formatach. W tym celu musisz albo
samodzielnie przekonwertować czcionki, albo użyć narzędzia, takiego jak Font Squirrel z http://wwwjontsquirrel.
com/o Narzędzie @font-face Generator pozwala Ci wgrać czcionki, a następnie przekształcić je na inny format.
Generuje ono również odpowiedni CSS, który umożliwia działanie czcionki w różnych przeglądarkach,
co pokazano na listingu 4.8.
@font-face {
font -fami l y: ' AnagramRegul ar ' ;
src : url ( ' typejanagram-webfont . eot ' ) ;
src : url ( ' typejanagram-webfont . eot ?#i efi x ' ) format ( ' embedded-opentype ' ) ,
url ( ' typejanagram-webfont . woff ' ) format ( ' woff ' ) ,
url ( ' typejanagram-webfont . ttf ' ) format ( ' truetype ' ) ,
url ( ' typejanagram-webfont . svg#AnagramRegul ar ' ) format ( ' svg ' ) ;
font -we i gh t : normal ;
font-styl e : normaI ;
Na listingu 4.8 odwołujemy się do czterech różnych formatów tej samej czcionki. Warto być może
umieścić swoj e czcionki w oddzielnych katalogach, tak j ak robisz to z obrazkami. Kolejność czcionek
94 Rozdział 4. Nowe techn i ki dotyczące u kład u i styl izacj i w CSS3
w powyższym kodzie jest związana z problemem dotyczącym działania Internet Explorera. Było już wiele
wersji powyższego kodu, ale w miarę rozwoju przeglądarek zniknęło kilka problemów. Powyższy kod jest
obecnie2 dostarczany przez Font Squirrel, gdy generujesz pakiet z czcionkami, lecz oczywiście w przyszłości
może się on zmienić.
Google daje Ci również nazwę czcionki, którą możesz umieścić w swoim kodzie CSS:
hl { font -fami l y : ' Yanone Kaffeesatz ' , ari al , seri f; }
Wskazówka
W poprzed n ich przykładach mogłeś zauważyć, że po deklaracj i dodatkowej czcionki podajemy l istę
n i e których " bezpiecznych " do stosowan i a w sieci czcionek, jak na przykład rodzinę czcionek ' Yanone
Kaffeesatz ' , a r i a l , ser i f. Rezerwową czcionkę podajemy, na wypadek gdyby cokolwiek poszło nie
tak podczas pobiera n i a dod atkowej czcion ki . Może się poj awić problem z serwerem i przeglądarka
może być n iezd o l n a do pozyskan ia dodatkowej czcionki, którą chcemy wykorzystać, a poprzez
zam ieszczenie listy czcionek dajemy m ożl iwość awaryj nego użycia czcion ki, o której wiemy, że jest
dostępna. Listy czcionek powinny być dodawane zawsze wtedy, kiedy używa się CSS-owej właściwości
font- fami l y .
Jeśli Font Squirrel albo Google Font Directory nie mogą udostępnić czcionki, której szukasz, wypróbuj
inne serwisy. Ponieważ licencjonowanie stanowi problem już od lat, wielu spośród głównych producentów
czcionek zaczęło albo tworzyć swoje własne struktury pozwalające Ci zgodnie z prawem i bezpiecznie używać
ich czcionek, albo współpracować z inną organizacją, która pomaga dostarczać czcionki.
Serwisy takie jak FontDeck i TypeKit umożliwiają Ci zarejestrowanie się i dokonanie wyboru spośród
szeregu czcionek, z których wszystkie zostały poprawione w celu uzyskania lepszej czytelności na ekranie.
FontDeck i TypeKit dostarczaj ą czcionki na odrobinę różne sposoby, ale konfiguracja, wybór i aktywacja
czcionek w obu serwisach nie powinny nikomu sprawiać problemów. Oba serwisy mają darmowe konta
z pewnymi ograniczeniami i oba oferują różne pakiety cenowe.
Android 2 . 3+
Chrome 13.0+
Fi refox 4.0+
Internet Explorer
iOS Safari 4.0+
Opera 11.0+
Safari 5 . 0+
Listing 4.9 zawiera kod prostego gradientu od białego do czarnego koloru, który pokazano na rysunku 4.5.
div {
hei ght : 200px ;
w i d t h : 200px;
bac kground: ur l (grad i ent . gi f) ; /* dla przeglądarek. które nie potrafią tworzyć gradientów */
bac kground: -moz - l i near-gradi ent (wh i t e , bl a c k) ;
bac kground: -web ki t - l i near-gradi ent (wh i t e , bl a c k) ;
bac kground: - l i near-grad i ent (wh i t e , bl ack) ;
}
96 Rozdział 4 . Nowe techn i ki dotyczące u kład u i styl izacj i w CSS3
jakiś tekst
Najpierw zostaje określony rodzaj gradientu (liniowy lub radialny), po czym następują nawiasy, w których
zapisany jest początkowy i końcowy kolor gradientu. Zauważ, że kod ma cztery różne deklaracj e. Pierwsza
zawiera gradient w postaci pliku graficznego, na wypadek gdyby przeglądarka nie obsługiwała gradientów
CSS3; -moz-l ; nea r-grad; ent jest przeznaczona dla przeglądarek Mozilla (Firefox); -web k i t- l ; nea r-grad; ent
dla przeglądarek opartych na WebKit (Safari i Chrome); ostatnia deklaracja tła jest oficjalną składnią gradientu
CSS3, ale obecnie żadna przeglądarka jej nie obsługuje.
Uwaga
W 2008 roku Web Kit był pierwszym s i l n i kiem , który zaczął używać gradientów, lecz m iał swoj ą własną
składnię. W m iarę jak grad ienty CSS stawały s ię bardziej popularne, Mozi lla zaim plementowała składn ię
znacznie bl iższą tej z oficjalnej s pecyfikacj i . Jednak w styczniu 2011 roku ogłoszono, że Web Kit zmieni
s kładnię gradientów i zaczn ie używać tego samego stylu co Moz i l l a i oficj a l n a s pecyfi kacj a , co jest
wspaniałą wiadomością dla każdego, pon ieważ oryginalna składn ia WebKit była bardziej skomplikowana
i n ie była oparta na standardach . W tej książce używamy nowej s kładn i WebKit.
i nput {
border: none;
-webki t-box-s hadow: O 1px 5px rgba ( O , O , O , . 4 ) ;
box-s hadow: O 1px 5px rgba ( O , O, O, . 4 ) ;
-webki t-border-rad i us : 10px ;
border-radi us : 10px ;
cursor: poi nter;
col or: #fff;
fon t : bol d 1 . 2em Ari al , Hel vet i c a , sans-seri f;
marg i n : O 10px O O ;
padd i ng : 10px 10px 10px 30px;
text-shadow : O 2px 2px rgba ( O , O, O, 0 . 25 ) ;
}
i nput [type= "subm i t " ] {
bac kground: url ( i magesjaccept . png) 8px 55% no-repeat #91B009 ;
bac kground: url ( i magesjaccept . png) 8px 55% no-repeat , -webk i t - l i near-gradi ent (#91B009, #578730) ;
bac kground: url ( i magesjaccept . png) 8px 55% no-repeat , -moz - l i near-grad i ent (#9 1 B009 , #578730) ;
Tworzenie przycisków za pomocą gradientów CSS i wielu tel 97
bac kground: url ( i magesjaccept . png) 8px 55% no-repeat , -l i near-gradi ent (#91BD09 , #578730) ;
}
Możesz uzyskać znacznie więcej niż tylko zwykłe przenikanie dwóch kolorów. Używaj ąc zatrzymania,
możesz dodać wiele kolorów do gradientu, co pokazano na rysunku 4.7. Zatrzymania są dodawane po każdym
przecinku i mogą zawierać pozycję, w której się zaczynają, jak pokazano na listingu 4.1 1 .
div {
hei ght : 200px ;
w i d t h : 200px;
bac kground-col or : #00 0 ;
bac kground: -moz - l i near-gradi ent ( l eft , #000000 0 % , # FFFFFF 2 5 % , #000000 50% , # FFFFFF 7 5 % , #000000 100%) ;
bac kground: -web k i t - l i near-grad i ent (l eft , #000000 0% , # FFFFFF 25%, #000000 50% , # FFFFFF 75%, #000000 100%) ;
bac kground: - l i near-grad i ent ( l eft , #000000 0%, #FFFFFF 25%, #000000 50% , #FFFFFF 75%, #000000 100%) ;
}
Listing 4. 1 1 pokazuje kod użyty do utworzenia lewego obrazka z rysunku 4.7. Gradient staje się bardziej
skomplikowany, ale zasadniczo każdy przecinek rozpoczyna nowy fragment informacji o gradiencie, w którym
przekazuj esz wartość koloru, a następnie procent oznaczający miejsce, w którym ten kolor się zaczyna.
98 Rozdział 4. Nowe techn i ki dotyczące u kład u i styl izacj i w CSS3
Ponadto możesz obrócić gradient przez zmianę deklaracji l eft, na przykład na 45deg albo l eft top,
jak pokazano na listingu 4.12, który tworzy prawy obrazek z rysunku 4.7.
div {
hei ght : 200px ;
w i d t h : 200px;
bac kground-col or : #00 0 ;
bac kground: -moz - l i near-gradi ent (45de g , #000000 0 % , #FFFFFF 2 5 % , #000000 50% , # FFFFFF 7 5 % , #000000 100%) ;
bac kground: -web k i t - l i near-gradi ent (45de g , #000000 0%, #FFFFFF 25%, #000000 50% , #FFFFFF 75%, #000000 100%) ;
bac kground: - l i near-grad i ent (45deg, #000000 0%, # FFFFFF 25%, #000000 50%, # FFFFFF 75%, #000000 100%) ;
}
Istnieje mnóstwo opcji związanych z gradientami CSS3, włączając w to tworzenie gradientów radialnych.
Zalecamy wypróbowanie niektórych spośród generatorów gradientów, by nabrać wprawy:
• http://colorzilla.com/gradient-editor/,
• http://display-inlinejr/projects/css-gradient/.
Android 2 . 3+
Chrome 13.0+
Fi refox 4.0+
Internet Explorer 9 . 0+
iOS Safari 4.0+
Opera 11.0+
Safari 5 . 0+
Pe llentesque habilant morbi tristique senectus et netus. Vestibu lum tortor quam , feug iat vitae,
> ultricies eget. tempor sit amet. ante. Do nec eu libero sit amet quam egestas semper. Aenean
C ultricies mi vitae est.
O
... Pe llentesque habitant morbi tristique
-
en
se nectus et netus et malesuada fames ac
turpis egestas Vestibu lum tortor q uam ,
-
::l
feugiat vitae, ultricies eget, tempo r sit
amet. ante. Donec eu li bero sit amet
>.
-
quam egestas sem pe r. Aenean ultricies
mi vitae es! M auris placerat eleifend leo.
- erat volutpa! Nam dui mi. tincidunt quis.
en accumsan po rttitor, facilisis luctus, metus
CI)
0-
Pe llentesque habitant morbi tristique
O senectus et netus et malesuad a fames ac
ł- turpis egestas Vestibu lum tortor q uam ,
WidOk z
feugiat vitae, ultricies eget, tempor sit na szegO
POkoju h otelO w
ego
amet. ante. Donec eu li bero sit amet
quam eg estas sem pe r. Aenean ultricies
mi vitae es! M auris placerat eleifend leo.
Q uisque sit amet est et sapien
ullamcorpe r pharetra. Vesti bulum erat
wisi, condi mentum sed, commodo vitae, ornare sit amet, w isi. Aenean ferme ntu m , eliteget
ti ncidu nt cond imentum, eros ipsum rutrum orci, sag ittis tempus lacus en im ac dui. Donec non
en im in tu rpis pulvinar facilisis. Ut felis.
* {
marg i n : O ;
paddi n g : O ;
}
body {
fon t : normal . gem Ari al , Hel vet i c a , sans -ser i f ;
pos i t i on : rel ati ve;
padd i ng : 30px 10px 10px 75px;
width: 600px ;
}
hl {
l eft : - IOOpx ;
pos i t i on : absol ute;
top : 160px;
text-al i gn : l eft ;
-webki t-transform: rotate (270deg) ;
-moz-transform: rotate (270deg) ;
-o-trans form: rotate(270deg) ;
-ms-transform: rotate(270deg) ;
trans form : rotate (270deg) ;
fi gure {
bac kground: #fff;
border: lpx sol i d #BFBFB F ;
-webki t-box-s hadow: 2 p x 2px 4px rgba ( O , O , O , 0 . 3) ;
-moz-box-shadow: 2px 2px 4px rgba (O , O , O , 0 . 3) ;
box-s hadow: 2px 2px 4px rgba ( O , O , O , 0 . 3 ) ;
d i spl ay: bl oc k ;
fl oat : ri ght ;
marg i n : 20px 20px 50px 50px ;
paddi ng: 5px;
text-al i gn : center;
-webki t-transform: rotate (lOdeg) ;
100 Rozdział 4. Nowe techn i ki dotyczące u kład u i styl izacj i w CSS3
Na rysunku 4.8 widać efekt użycia funkcji rotate CSS-owej właściwości t ran s form, lecz istnieją jeszcze
inne, których możesz używać: trans l ate, scal e i s kew. Listing 4.14 pokazuje przykład kodu potrzebnego do
implementacji tych efektów (nie zapomnij dodać prefiksów specyficznych dla przeglądarek) . Kod pokazuj e
efekty transformacji zastosowanych na tekście, ale mogą być one wykorzystane na dowolnym elemencie,
włączając w to body.
Możesz również stosować przejścia i animacje. Istnieje różnica między przejściami i animacjami w CSS-ie.
Przejście jest procesem zmiany między dwoma stanami, wywołanym przesunięciem myszą kursora nad obiekt
( : hover) albo kliknięciem przyciskiem myszy ( : act ; ve). Animacje CSS działają natomiast samodzielnie.
Przejścia były dostępne od dawna w Web Kit, a Mozilla i Opera już je implementuj ą; niestety, obecnie
nie robi tego IE. Przejścia wykorzystują pokazaną wcześniej właściwość trans form i mogą być zastosowane
w przypadku pseudoklas : hover albo : act ; ve. Zamiast natychmiastowego zmieniania wartości elementu,
gdy przesuwasz nad nim kursor myszą, możesz utworzyć animację przechodzącą do tych nowych wartości
przy użyciu właściwości CSS tran s ; t ; on. Rysunek 4.9 pokazuje prosty przykład zwykłego tekstowego łącza.
To jlest ·heze
Rysunek 4.9. Łącze tekstowe z efektem hover
Upiększanie witryny za pomocą transformacj i i przejść 101
Rysunek 4.10 pokazuje efekt w trakcie przechodzenia tła łącza do koloru czarnego i tekstu do koloru
białego oraz ostateczny efekt przesunięcia kursora nad łącze. Trudno jest oddać ten efekt w drukowanej,
czarno-białej książce, więc zachęcamy Cię do wypróbowania tego w przeglądarce.
To jest Jacze
Listing 4.15 pokazuje kod potrzebny do uzyskania efektu związanego z przesunięciem kursora myszy.
Docelowy kolor animacji jest zdefiniowany w deklaracji : hover. Bez żadnego przejścia kolory natychmiast
zmieniłyby się na docelowe, ale dla uzyskania efektu stopniowej zmiany koloru używasz właściwości
tran s ; t ; on (oraz prefiksów producentów dla WebKit, Mozilli i Opery) . Kluczowy w tym przypadku
fragment CSS-a - tran s ; t ; on : a l l 1s ease- ; n-przekazuj e przeglądarce, by animowała wszystkie
właściwości (background i co l or) i użyła funkcji czasowej ease- ; n, oraz informację, że całkowity czas
trwania przej ścia to j edna sekunda ( 1 s ) . Możesz opcjonalnie umieścić wartość opóźnienia, tak j ak
w tran s ; t ; on : a l l 1s ease- ; n 1s, opóźniaj ącą rozpoczęcie przej ścia oraz moment, w którym element
powraca do swojego domyślnego stanu.
a {
bae kground: #fff;
border-radi us : 5px;
d i spl ay: bl oe k ;
f l oat : l eft ;
paddi ng: 5px;
text-al i gn : center;
w i d t h : 125px;
-webk i t -trans i t i on : al l ls ease- i n ;
-moz-trans i t i on : al l ls ease- i n ;
-o-trans i t i on : al l l s ease- i n ;
trans i t i on : al l ls ease- i n ;
a : hover
bae kground: #000 ;
eol or: #fff;
Za pomocą przejść uzyskujesz zatem drobne, subtelne efekty, ale możesz również używać ich do
poprawienia ogólnych wrażeń użytkownika strony. Weź jako przykład galerię miniatur zdjęć z rysunku 4. 1 1 .
Używając kodu z listingu 4.16, dzięki kombinacji rotate i sca l e tworzysz ładny efekt wyświetlenia obrazka
w jego pełnym rozmiarze po najechaniu na niego kursorem, i to bez potrzeby użycia JavaScriptu, co pokazano
na rysunku 4.12.
i mg {
bae kground: #fff;
border: 1px sol i d #BFBFB F ;
d i spl ay: bl oe k ;
102 Rozdział 4. Nowe techn i ki dotyczące u kład u i styl izacj i w CSS3
fl oat : l eft ;
hei ght : 125px ;
marg i n : ° 10px ° O ;
paddi ng: 5px;
w i d t h : 125px;
-webki t-box-s hadow: 2px 2px 4px rgba ( O , O , 0, 0 . 3) ;
-moz-box-shadow: 2px 2px 4px rgba (O , O , 0 , 0 . 3) ;
box-s hadow: 2px 2px 4px rgba ( O , O , 0 , 0 . 3 ) ;
-webk i t -trans i t i on : al l ls ease- i n-out ;
-moz-trans i t i on : al l ls ease- i n-out ;
-o-trans i t i on : al l ls ease- i n-out ;
i mg : hover {
-webki t-transform: rotate (lOdeg) scal e (2) ;
-moz-transform: rotate (lOdeg) sca l e (2) ;
-o-trans form: rotate(lOdeg) scal e (2) ;
-ms-transform: rotate (lOdeg) sca l e (2) ;
trans form : rotate (lOdeg) scal e (2) ;
}
�
-.--
Android 2 . 3+
Chrome 13.0+
Fi refox 5 . 0+
Internet Explorer
iOS Safari 4.0+
Opera
Safari 5 . 0+
W pierwszym, prostym przykładzie przesuniesz obrazek z lewej strony ekranu do prawej i jednocześnie
go obrócisz. Pokazywanie zrzutu ekranu z tego przykładu nie ma większego sensu, więc otwórz przeglądarkę
Chrome, Firefox albo Safari i uruchom kod z listingu 4.17.
/* CSS */
div {
fl oat : l eft ;
hei ght : 100%;
pos i t i on : rel ati ve;
width: 100% ;
}
i mg {
pos i t i on : absol ute;
-webki t-anima t i on-name : move l t ;
-webki t-anima t i on-durati on: 5 s ;
-webki t-animat i on - i terat i on-count : i nfi n i te ;
-webki t-animat i on - t i m i ng-func t i o n : l i near;
-moz-animat i on-name : move l t ;
-moz-animat i on-durat i on : 5s ;
-moz-animat i on - i terat i on-count : i nf i n i te ;
-moz-animat i on-t i mi ng-funct i o n : l i near;
animat i on-name : move l t ;
animat i on-durati on: 5 s ;
animat i on - i terat i on-count : i nfi n i te ;
animat i on - t i m i ng-func t i o n : l i near;
}
@-web k i t - keyframes movelt
from {
l eft : O ;
-webki t-transform : rotate (Odeg) ;
}
to {
l eft : 100%;
-webki t-transform : rotate (360deg) ;
104 Rozdział 4. Nowe techn i ki dotyczące u kład u i styl izacj i w CSS3
Przy selektorze i mg znajdują się cztery właściwości CSS dotyczące animacji. Są powtórzone, ponieważ
muszą się jawnie odwoływać do przedrostków -web k i t- i -moz-. Na końcu umieść nazwy właściwości bez
przedrostków z myślą o dniu, w którym wszystkie przeglądarki będą obsługiwać animacje CSS:
• -web k i t/moz -an i mat i on-name - nazwa animacji, której chcesz użyć;
• -web k i t/moz -an i mat i on-durat i on - jak długo animacja będzie trwać;
• -web k i t/moz -an i mat i on- i terat i on -count - ile razy animacja będzie powtórzona;
• -web k i t/moz -an i mat i on-t imi ng-funct i on rodzaj animacji; wybierz spośród ease, l i near,
-
ease- i n, ease-out, ease- i n -out i cub i c-bez i er, którą można dostosowywać.
Teraz zdefiniuj animację @-webki t/moz- keyframes Move I t . Jest to prosta animacja, więc zacznij od
właściwości from, a skończ na wartości to. Wewnątrz tych właściwości użyj zwykłego CSS-a, by przemieścić
oraz obrócić obrazek. W tych animacjach można zmienić prawie każdą właściwość CSS, masz więc dużo
różnych możliwości.
Korzystanie z właściwości from i to to j eszcze nie wszystko. Możesz również umieszczać klatki kluczowe,
używaj ąc procentów, co pozwoli Ci na większą elastyczność. Rysunek 4 . 1 3 pokazuj e baner, który znajduje
się w prawym górnym rogu strony. Dzięki użyciu kodu z listingu 4.18 kolor tła zmienia się płynnie, a klatki
kluczowe są umieszczone co 25 procent czasu animacji.
p {
bac kground: #000 ;
col or: #fff;
fon t : bol d 20px Tahoma , Genev a , sans-ser i f ;
padd i ng : lOpx ;
Tworzenie a n i macj i za pomocą CSS-a 105
@-web k i t - keyframes gl ow {
0% {
bac kground: # FOO ;
}
25% {
bac kground: #06C ;
}
50% {
bac kground: #000 ;
}
75% {
bac kground: #06C ;
}
100% {
bac kground: # FOO ;
}
}
@-moz- keyframes g l ow
0% {
bac kground: # FOO ;
}
25% {
bac kground: #06C ;
}
50% {
bac kground: #000 ;
}
75% {
bac kground: #06C ;
}
100% {
bac kground: # FOO ;
106 Rozdział 4. Nowe techn i ki dotyczące u kład u i styl izacj i w CSS3
keyframes g l ow
0% {
bac kground: # FOO ;
}
25% {
bac kground: #06C ;
}
50% {
bac kground: #000 ;
}
75% {
bac kground: #06C ;
}
100% {
bac kground: # FOO ;
}
}
Wskazówka
Przejścia i a n i m acje m ają wiele zastosowa ń . Nie tyl ko pozwalają dodawać faj n e efekty do stron
i nternetowyc h , ale mogą być ta kże w prosty s posób tworzone i poprawiane przez proje ktantów
i program istów. Po kaza l iśmy w tym rozdziale jedyn ie proste przykłady, by pobudzić Twój a petyt,
a od C iebie zależy, jak je wykorzystasz. Dużym możliwościom towarzyszy jednak duża odpowiedzialność,
więc staraj się nie przesadzać z rozma itym i efekta m i , pon i eważ mogą one szybko zam ienić faj ną
witrynę w kiczowatą. Zbyt wiele przejść i a n i m acj i może równ ież wpłynąć n a wyd ajność przegląd a r k i .
Sugerujemy s pędzić trochę czasu na poszu kiwa n i u w internecie przykładów i badaniu s posobów,
w j akie i n n i projektanci i program iści użyli CSS3 do u l e pszania witryn .
Podsumowanie
W tym rozdziale poznałeś kilka nowych możliwości CSS-a: zapytania o media, własne czcionki, gradienty,
transformacje, przejścia oraz animacj e. Istnieje o wiele więcej funkcji niż te, które opisaliśmy; dotknęliśmy
jedynie powierzchni. Jeśli j esteś zainteresowany CSS-em, sugeruj emy, żebyś kontynuował poszukiwania
i naukę, ponieważ istniej e wiele innych technik i sugerowanych praktyk dotyczących użycia niektórych
spośród tych nowych możliwości. A zatem zgłębiaj CSS i baw się nim.
5
Formularze internetowe HTML5
W
tym rozdziale poznasz formularze internetowe HTMLS oraz ich nowe funkcj onalności dostępne
teraz dla twórców stron internetowych. Oczywiście nie wszystkie te nowe możliwości są w pełni
obsługiwane przez wszystkie przeglądarki, ale jak zobaczysz w przepisach w tym rozdziale, mimo to nieźle
działają i wyglądają. Przy tworzeniu formularzy do wyboru masz ponad dziesięć nowych typów elementu
i nput, kilka nowych atrybutów oraz parę nowych trików CSS-owych.
Walidacja
Mimo udostępnienia kilku nowych typów elementu i nput, bez wątpienia najważniejszą nową możliwością
oferowaną przez HTML jest wbudowana walidacja formularzy. Dawniej trzeba było używać kodu po stronie
serwera, takiego j ak PHP albo CI, do sprawdzania treści przesłanego formularza, a następnie musiałeś
zwracać stronę i wyświetlać użytkownikowi błędy. Alternatywnie można było używać jakiegoś wymyślnego
JavaScriptu do sprawdzania na bieżąco treści wprowadzanych danych i informowania użytkownika, czy
wystąpiły błędy.
Przy użyciu HTMLS walidacja formularzy jest prosta. Możesz dać użytkownikom wbudowaną walidację,
dzięki której otrzymują informację zwrotną, w momencie gdy próbują wysłać formularz, co powoduje, że
formularze łatwiej jest wypełniać, i zwiększa szanse wysłania poprawnych informacji. Oczywiście będziesz
potrzebował j akiegoś kodu po stronie serwera do przetworzenia formularzy.
W przepisach w tym rozdziale do większości elementów formularzy dodajemy atrybut requ i red. Robimy
tak, byś podczas testowania mógł zobaczyć, jakie komunikaty dotyczące walidacji pokazuje przeglądarka
G eśli je pokazuje). Ten atrybut mówi przeglądarce, że formularz nie może być wysłany, zanim element nie
zostanie wypełniony.
Pole tekstowe
To j est pole tekstowe
Wybór Opcja 1 �
[ Prześlij I
[ Wyczyść I
Listing 5 . 1 pokazuje HTML wyświetlaj ący rysunek 5 . 1 . Zauważ, że do sformatowania tego formularza
zostały użyte lista nieuporządkowana ( u l ) i elementy listy ( l i ) . Nie j est to j edyny sposób organizacji
formularza, ale dzięki niemu nie musisz niepotrzebnie używać w tym kodzie <br l>.
<form>
<fi el dset>
<l egend>Legenda el ementu f i el dset - podp i s/tytut tego zbi oru pól </l egend>
<ul >
<l i ><l abel for= " normal " >Zwykte pol e wej ś c i owe</l abe l > <i nput i d= " normal " type= " text " val ue= " zwykte
"'po l e wej śc i owe" /></l i >
<l i><i nput i d= " c hec kbox" type= " c he c kbox" / > <l abel for= " chec kbox ">Jestem przyc i s k i em
"'wyboru</ l abel ></l i >
< l i><i nput i d= " rad i o " type= " rad i o" /> <l abel for= " rad i o " >Jestem przyc i s k i em opcj i </l abel ></l i >
<l i ><l abel for= " fi l e " >Prześl i j pl i k</l abel > < i nput i d= " f i l e " type= " fi l e" /></1 i >
<l i ><l abel for= " password">Hasto</l abel > <i nput i d= " password" type= " password" val ue= "mypassword" /></l i >
<l i ><l abel for= " textarea ">Pol e tekstowe</l abe l > <textarea i d= " textarea" rows= " 5 " col s= "40">To jest
"'po l e tekstowe</textarea></l i >
< l i ><l abel for= " se l ec t " >Wybór</l abe l >
<sel ect i d= " sel ec t " >
<opt i on>Opcj a l</opt i on>
<opt i on>Opcj a 2</opt i on>
<opt i on>Opcj a 3</opt i on>
<opt i on>Opcj a 4</opt i on>
</sel ect></l i >
<l i><i nput type= " subm i t " val ue= " Prześ l i j " /></l i >
<l i><i nput type= " reset" val ue = " Wyczyś ć " /></1 i >
<fu l >
</f i e l dset>
</form>
Tworzenie form u larza do d anych kontaktowych 109
Istnieje kilka nowych typów wejściowych w HTML5, które są użyteczne przy tworzeniu albo modyfikacji
starych formularzy. Ułatwią Ci one jako programiście pracę, lecz pomogą również użytkownikowi wprowadzać
poprawne dane.
<form>
<fi el dset>
<l egend>Dane kontaktowe<jl egend>
<ul >
<l i >
<l abel for= " ema i l " >E-ma i l <jl abe l >
<i nput requi red type= " emai l ll i d= " emai l ll name= I I emai l II />
<jl i >
<l i >
<l abel for= " tel " >Numer tel efonu<jl abe l >
<i nput requi red type= " tel " i d= " tel " name= " t e l " j>
<jl i >
<l i >
<l abel for= " url " >W i tryna<jl abel >
<i nput requi red type= " url ll i d= " url ll name= " url ll />
<jl i >
<ju l >
< i nput type= " subm i t " val ue= " Prześ l i j to" j>
<jf i e l dset>
<jform>
input type="email"
Typ wejściowy ema ; l mówi przeglądarce, że treść tego pola powinna wyglądać jak adres e-mail. Na listingu
5.3 przy polu wejściowym ema ; l umieściliśmy atrybut req u ; red. Poinformuj e on kompatybilne przeglądarki,
by sprawdziły treść tego pola przed wysłaniem formularza. To, czy adres e-mail rzeczywiście istniej e, nie j est
sprawdzane.
<form>
<l abel for= " emai l ">E-mai l <jl abel >
< i nput requ i red type="emai l ll i d= " emai l ll name = " ema i l ll />
< i nput type= " subm i t " j>
<jform>
110 Rozdział 5. Form u l a rze internetowe HTML5
Android 2 . 3+*
Chrome 10.0+
Fi refox 4.0+
Internet Explorer 9 . 0+*
i OS Safa ri 4.0+*
Opera 10.0+
Safari 5 . 0+*
* brak walidacji
Coraz szybszy rozwój przeglądarek, których producenci nieustannie starają się wyprzedzić konkurencję,
sprawił, że pomiędzy nimi istnieją różnice w sposobie obsługiwania walidacji. Testy przedstawione w tabeli
5.2 pokazują różnice w sposobie obsługi formularzy, ich walidacji i informacji zwrotnych. Wcześniejsze
wersje zwracają komunikaty walidacji inne od tych pokazanych w testach.
I
Uwaga
Być może zastanawiasz się, czy m ożesz n ad awać style inform acjom zwrotnym po kazywanym przez
formu l arz. Cóż, w pewnym sensie możesz. Służący do tego CSS omówimy w d a lszej części rozdziału .
test@test.com Nie ma blędu Tak Nie ma blędu Tak Nie ma blędu Tak
[ Wyślij l
Rysunek 5 . 2 . Element input type="tel" w Operze. Wygląda jak zwykłe pole tekstowe
Tworzenie form u larza do d anych kontaktowych 111
Android 2 . 3+*
Chrome 10.0+
Fi refox 4.0+
Internet Explorer 9 . 0+*
iOS Safari 4.0+*
Opera 10.0+
Safari 5 . 0+*
* brak walidacji
<l abel for= " url " >Wi tryna<jl abe l >< i nput requ i red type= " url " i d= " url " name= " url " j>
Android 2 . 3+*
Chrome 10.0+
Fi refox 4.0+
Internet Explorer 9 . 0+*
iOS Safari 4.0+*
Opera 10.0+
Safari 5 . 0+*
* brak walidacji
Treść typu wej ściowego url powinna być prawidłowym URL-em. Znowu, podobnie jak w przypadku
typu ema ; l , to, czy adres rzeczywiście istnieje, nie jest sprawdzane (można to zrobić za pomocą dodatkowego
kodu skryptowego). Tradycyjnym URL-em będzie adres strony internetowej, taki jak http://mojawitryna.com.
Firefox 10 nie akceptuje mojawitryna.com ani www.mojawitryna.com; wymaga adresu zawierającego http://.
Jednak Opera 1 1 automatycznie zamienia mojawitryna.com na prawidłowy adres http://mojawitryna.com,
który pozwala wysłać formularz.
W większości przypadków zawartością tego pola będzie http://mojawitryna.com; jednak zaakceptuje ono
również inne rodzaje adresów URL:
• Jtp://użytkownik:hasło@serwer,
• javascript:window.alert (należy z tym uważać; możesz napisać swoją własną walidację,
aby nie dopuścić do przesyłania JavaScriptu),
112 Rozdział 5 . Form u l a rze internetowe HTML5
• file://serwer/ścieżka,
• tel:12345.
Ze względu na te dodatkowe typy adresów URL w chwili obecnej formularz przeszedłby walidację
z zawartością hello:world. Jest tak dlatego, że większość przeglądarek ma swoj e własne typy adresów URL,
jak na przykład Firefox, który ma about:config. A zatem obecnie wszystko, co zawiera dwukropek w URL-u,
przejdzie walidację. W przyszłości, gdy walidacja w przeglądarkach zostanie udoskonalona, może się to zmienić.
Android 2 . 3+
Chrome 10.0+
Fi refox 4.0+
Internet Explorer 9 . 0+
iOS Safari 4.0+
Opera 10.0+
Safari 5 . 0+
Ciekawym dodatkiem w przeglądarkach opartych na silniku WebKit (takich jak Safari i Chrome) jest
atrybut resu l ts, który daj e dodatkową funkcjonalność:
<l abel for= " searc h " >Wys z u k i wana fraza</l abe l >
<i nput requ i red type= " searc h" resul ts= " 5 " i d= " searc h" name= " searc h" />
Powyższy kod powoduje pokazanie pola wyszukiwania zaprezentowanego na rysunku 5.3. Zwróć uwagę
na ikonę lupy dodaną do pola wejściowego, która wyświetli poprzednio wyszukiwane frazy (jeśli takie istnieją).
Niestety, nie można jeszcze nadawać stylów takim podpowiedziom za pomocą CSS-a lub J avaScriptu.
Tworzenie kontrolek kalendarza i czasu 113
Android *
Chrome *
Fi refox *
Internet Explorer *
iOS Safari 5 . 0+
Opera 10.0+
Safari *
Data/czas Ooka1ny)
I Prześlij I
Rysunek 5 . 5 . Element input type="datetime-Iocal" wyświetlony w Operze 11
Użytkownik może albo wpisać cyfry, na przykład 22: 1 1 , albo użyć przycisków do przewij ania czasu
dostępnych w Operze.
Tworzenie kontrolek kalendarza i czasu 115
input type="month"
Typ wejściowy month wyświetla kalendarz przedstawiony na rysunku 5.4, ale pozwala wybierać jedynie
miesiące. Wartością typu wej ściowego j est wtedy RRRR-MM. Na rysunku 5.8 można w polu miesiąca
wybrać inny miesiąc niż grudzień; można również przejść do poprzedniego lub następnego miesiąca.
6 7 8 9 10 1 1 1 2
13 14 1 5 1 6 1 7 1 8 19
2 0 2 1 22 2 3 24 25 26
2 7 2 8 2 9 30 3 1 1 2
3 4 5 6 7 8 9
I Dzisi aj I
Rysunek 5 . 8 . Element input type="month " wyświetlony w Operze 11
input type="week"
Bardzo podobny do typów wej ściowych date i month, week pozwala użytkownikowi wybrać tylko któryś
tydzień w roku. Wartością typu wej ściowego j est RRRR- WOO. Na rysunku 5.9 można wybrać tydzień,
a w lewej kolumnie udostępniony jest numer tygodnia.
� ---K-----
Który jest tydzień?
I Prześlij ) "'GJ
= ='"1=2=0=
wie cien' ---0=0 12 1 �
= =
Tydzi Pn Wt Śr Cz Pt Sa N
13 26 27 28 29 30 31
14 2 3 4 5 6 7 8
15 9 10 11 12 1 3 14 1 5
16 1 6 1 7 1 8 1 9 2 0 2 1 22
17 23 24 25 26 27 28 2 9
18 30 1 2 3 4 5 6
Dzisiaj
Android *
Chrome 11.0+
Fi refox *
Internet Explorer *
iOS Safari *
Opera 10.0+
Safari 5.1
* brak pola d o wprowadzania liczby. ale zamiast
niego pojawia się awaryjnie pole tekstowe
Wyświetlając kod z listingu 5.6, Opera tworzy kontrolkę, która pozwala użytkownikowi przełączać się
między liczbami (patrz rysunek 5.10). Liczba może być ujemna, a jeśli nie podasz maksimum lub minimum,
może być dowolnie duża. Przy użyciu atrybutu step możesz precyzyjniej określić wymagania. Na rysunku
5 . 1 0 użytkownik może wybierać kolejne liczby od O do 10 (mi n i max ) co 0,5. A zatem może wybrać O; 0,5; 1 ;
1 ,5 i tak dalej . Użytkownik może albo użyć udostępnionych kontrolek, albo wprowadzić liczbę z klawiatury.
< i nput m i n= II Q II max= II IO II step= II O . 5 11 requi red type= lI number" i d= lI numberll name= II number" />
Ponieważ określiłeś zakres kroku, wszystkie wartości spoza zakresu spowodują błędy walidacji, więc
wpisanie liczby 9,99, jak na rysunku 5 . 1 1 , jest niedozwolone.
Android *
Chrome 10
Fi refox *
Internet Explorer *
iOS Safari 5 . 0+
Opera 10.0+
Safari 5 . 0+
* brak suwaka. ale zamiast niego pojawia się
awaryjnie pole tekstowe
Możliwym zastosowaniem typu wej ściowego range może być sekcj a strony lub formularza w rodzaj u
"oceń moje coś tam" albo sterowanie głośnością odtwarzacza wideo lub audio.
Na listingu 5.7 pokazaliśmy jedynie HTML potrzebny do utworzenia prostej kontrolki range, a w jednym
z kolejnych rozdziałów dowiesz się, jak zrobić z niej działającą regulację głośności.
Mamy ograniczoną kontrolę nad tym, jak wygląda suwak, ponieważ obecnie wygląd kontrolek zależy od
przeglądarki. Możesz jednak określić wysokość i szerokość kontrolki zakresu. Jeśli ustawisz wysokość, która
jest większa niż szerokość, regulacja głośności wyświetli się pionowo zamiast domyślnego układu poziomego.
W chwili pisania tej książki ten typ wejściowy obsługują Opera, Chrome i Safari, co ilustruj e rysunek 5 . 1 2 .
Główną różnicą jest t o , że Opera domyślnie pokazuje podziałkę.
••••
•
#000000
Inny...
Kolor
� podstawowe:
. ii .i•
• iu •
• •• • ••••
• • • •
• •• • ••••
I lIll • • · · rJ . i
KoiOIy rie�andardowe:
.iiiiiii
iiiiiiii
• Qdc.:
Ma•. : O
1 60 Cz�. :
�.: O
O
�
desktopową, która obecnie obsługuje ten przydatny typ wej ściowy, jest Opera 1 1 (choć nowe przeglądarki
BlackBerry również obsługują próbnik kolorów) .
Android *
Chrome *
Fi refox *
Internet Explorer *
iOS Safari *
Opera 10.0+
Safari *
Android
Chrome 10.0
Fi refox 4.0
Internet Explorer
iOS Safari 4.0
Opera 10.0+
Safari 5.0
W przykładzie pokazanym na rysunku 5 . 1 4 podajemy wartości typu wej ściowego range w trakcie
przesuwania suwaka. Wartość domyślna jest pusta, ale gdy użytkownik przesuwa suwak, wartość elementu
output zmienia się i jest pokazywana użytkownikowi w czasie rzeczywistym. Listing 5.8 przedstawia kod
tego przykładu.
Głośność . . . . . . . [T.---;-. 7
Rysunek 5 . 14. Element output użyty do wyświetlenia wartości typu wejściowego range w Operze 11
Wcześniej uzyskanie takiego efektu wymagało użycia J avaScriptu, lecz teraz możesz pozwolić przeglądarce,
by zrobiła to za Ciebie.
120 Rozdział 5. Form u l a rze internetowe HTML5
Tekst zastępczy może być słabo widoczny, ponieważ obecnie kolor tekstu j est domyślnie jasnoszary,
co nie daj e wystarczającego kontrastu z domyślnym kolorem tła. Możesz zmienić kolor tekstu w Mozilli
i przeglądarkach opartych na silniku WebKit za pomocą następujących reguł CSS:
i nput : : -webki t - i nput-pl acehol der {col or: red ; }
i nput : -moz-pl acehol der { col or: red ; }
Android 2.2
Chrome 9 . 0+
Fi refox 4.0+
Internet Explorer
iOS Safari 4.0+
Opera 10.0+
Safari 4.0+
Jaki jest Twój ulubiony sport? pl i Jaki jest Twój ulubiony sport? II
l Prześl ij I � ------------,
p iłka ręczna t Prześlij l �kosz
--------------�
ykówka koszykówka
piłka nożna szczypiorniak piłka ręczna
piłka n oż n a piłka n oż n a
h o kej hokej
Android *
Chrome *
Fi refox 4.0+
Internet Explorer *
iOS Safari *
Opera 10.0+
Safari *
Kod elementu data' ; st (pokazany na listingu 5.9) j est podobny do kodu dla elementu se' ect. Jednak
w przypadku elementu s e ' ect użytkownik nie może wpisać swoj ej własnej wartości. Z data' ; st nie ma
takiego problemu. Wykorzystując atrybut ' ; st, powiązuj esz z elementem data' ; st zwykły element ; nput
(może to być również typ ur' , ema ; ' , search albo te ' ) .
<l abel for= " sport ">Jaki j est Twój ul ubi ony sport?<jl abe l >
< i nput l i st=" sportl i st " type= " text" i d= " sport" name = " sport" j>
<datal i st i d= " sport l i st">
<opt i on l abel = " koszykówka" val ue=" koszykówka" j>
<opt i on l abel = " p i ł ka ręczna" val ue= " szczyp i orn i a k " j>
<opt i on l abel = " p i ł ka noina" val ue= " p i ł ka noina" j>
<opt i on l abel = " hokej " val ue=" hokej " j>
</datal i st>
W powyższym przykładzie mamy cztery opcje samo uzupełniania. Jedynie Firefox i Opera pokazuj ą
obecnie opcje auto uzupełniania, ale robią to nieco inaczej : Firefox pokazuje tylko opcje, które są podobne
do tego, co zostało wpisane, natomiast Opera pokazuje całą listę, gdy pole jest aktywne.
Element data' ; st mógłby być generowany z bazy danych lub wywołania AJAX (podobnie do funkcji
autouzupełniania wyszukiwarki Google) i dostarczać użytkownikowi, powiedzmy, 10 najbardziej popularnych
odpowiedzi na pytanie. Piękno tego nowego elementu i atrybutu polega na tym, że jeśli użytkownik korzysta
z przeglądarki, która nie wyświetla tego elementu HTMLS, nie posiada J avaScriptu albo wystąpił problem
z połączeniem ajaksowym, kontrolki formularza wyświetlają się jak zwykłe pole tekstowe, a użytkownik może
normalnie wpisywać tekst.
zawartość atrybutu va' ue, jak i max powinna być wyświetlana wewnątrz elementu progress, tak aby
użytkownik miał informację zwrotną dotyczącą stanu zaawansowania wykonania zadania.
Następujący kod wyświetli element progress w przeglądarce Chrome (rysunek 5 . 1 7). Pasek postępu jest
podświetlony, dzięki czemu lepiej się prezentuje.
<p>Pobi erasz bardzo wainy pl i k , proszę czekać . <jp>
<progress val ue= " 4 5 " max= " lOO" >ukończono <span>45<jspan>%<jprogress>
W przeglądarkach, które nie obsługują elementu progres s, uzyskasz rezultat pokazany na rysunku 5 . 1 8
(zrzut ekranu z IE9).
122 Rozdział 5 . Form u l a rze internetowe HTML5
ukończono 45%
Rysunek 5 . 18. Awaryjne wyświetlanie w przeglądarkach nieobsługujących elementu progres s
Chodzi tu o to, że zielony (lub niebieski - w zależności od systemu operacyjnego i wersji przeglądarki)
pasek lub opis tekstowy powinny być aktualizowane na bieżąco, tak aby użytkownik wiedział, j aka część
pracy została ukończona, a jaka pozostaje do wykonania.
Android *
Chrome 13+
Fi refox 9 . 0+
Internet Explorer *
iOS Safari *
Opera 11.0+
Safari *
Element meter (pokazany na rysunku 5 . 1 9) może być także użyty do wskazania ilości miejsca na dysku
twardym:
<meter m i n= " O " val ue= " 5 1 2 " max=" 1024" >Używasz doktadn i e 50% poj emności Twoj ego twardego dysku<jmeter>
Twoja punktacja:
Rysunek 5 . 19. Element meter w przeglądarce Chrome
Android *
Chrome 13+
Fi refox *
Internet Explorer *
iOS Safari *
Opera 11.0+
Safari *
Przeszukaj witrynę jI
I Szukaj I
Rysunek 5 20. Atrybut autofocus użyty na polu tekstowym w przeglądarce Safari
Android
Chrome 9 . 0+
Fi refox 4.0+
Internet Explorer
iOS Safari
Opera 10.0+
Safari 5 . 0+
124 Rozdział 5. Form u l a rze internetowe HTML5
Atrybut autofocus należy stosować ostrożnie. Automatyczne przeskakiwanie lub przewijanie strony do
jakiegoś miejsca nie jest najlepszą praktyką przy projektowaniu stron, ponieważ użytkownicy wykorzystujący
urządzenia przenośne, czytniki ekranu albo mający niską rozdzielczość ekranu nie zobaczą być może istotnych
treści powyżej tego miejsca. Zaleca się, aby autofocus był używany na stronach, w których element formularza
stanowi główną treść, takich j ak strona kontaktowa albo, co pokazano na rysunku 5.20, strona wyszukiwarki
G ak formularz na Google.com, który posiada autofocus, lecz wykonany za pomocą J avaScriptu). Listing 5 . 1 0
pokazuj e kod użyty d o utworzenia rysunku 5.20.
<l abe l for= " autofocus " >Przeszukaj w i t rynę</l abe l >
< i nput autofocus requ i red type=" searc h " i d= " autofocus" name = " autofocus " />
I
Uwaga
Atrybut autofocus powin ien być użyty na stronie tylko raz. Co ciekawe, jeś l i m asz k i l ka pól na stronie,
wszystkie z atrybutem autofocus, O pera, Chrome i Safari u m ieszczają kursor w ostatn im polu, a Firefox
w pierwszym .
Android
Chrome 10.0+
Fi refox 4.0+
Internet Explorer
iOS Safari
Opera 11.0+
Safari 5 . 0+
Kolejnym przykładem może być przesyłanie wielu plików. Dawniej wymagałoby to JavaScriptu lub kodu
po stronie serwera, wykrywaj ącego, że plik został wybrany, a następnie wyświetlającego kolejną pozycję
pozwalającą wysłać plik. Teraz, przy użyciu kodu z listingu 5 . 1 1 , możesz to zrobić w HTML5.
Firefox, Opera, Safari i Chrome obsługuj ą j uż ten nowy atrybut. Safari i Chrome wyświetlaj ą liczbę
wybranych plików. W przykładzie pokazanym na rysunku 5.21 zostały wybrane trzy pliki. Firefox nie
wyświetla automatycznie, ile plików zostało wybranych; zamiast tego pokazuje pełne ścieżki plików wewnątrz
pola tekstowego, rozdzielaj ąc j e przecinkami, na przykład: "C:\plikl .doc, C:\plik2.pdf'. Opera również
pokazuje pełne ścieżki, przy czym umieszcza każdą z nich w cudzysłowach i rozdziela je średnikami.
Android
Chrome 10.0+
Fi refox 4.0+
Internet Explorer
iOS Safari
Opera 11.0+
Safari 5 . 0+
I
Wskazówka
O prócz atrybutu requ i red możesz równ ież dodać ari a-requ i red= " true " , co przyczyn i s i ę do poprawy
dostępności elementów form u larza. Może zainteresuje Cię informacj a , że WordPress rob i tak domyś lnie
w formu larzach komentarzy.
< i nput ari a-requ i red=" true" requi red type= " text " i d= " name" name=" name" />
126 Rozdział 5. Form u l a rze internetowe HTML5
Android
Chrome 10.0+
Fi refox 4.0+
Internet Explorer
iOS Safari
Opera 11.0+
Safari
Widziałeś na przykład, że typ wejściowy url akceptuje różne rodzaje adresów URL. Możesz temu zapobiec
i zmusić użytkownika do wpisania adresu rozpoczynaj ącego się od http://. Możesz to zrobić za pomocą
własnego wyrażenia regularnego .
Wyrażenie regularne wykorzystane na listingu 5 . 1 2 waliduje tylko adresy URL zaczynające się od http://
albo https://. Akceptuje również subdomeny i łańcuchy zapytań, więc http://ty.witryna.pl/search.aspx?=test
jest prawidłową zawartością, ale mailto:adres nie jest.
< i nput requ i red pattern= " (http l https) : // ( [\w-J +\ . ) + [\w-J + (j [\w- . / ?%&=J *) ? " type= " text " i d= " url " name= " url " />
Wyrażenia regularne mogą być zawiłe i szybko się komplikują, więc najlepiej, jeśli będziesz stosować
jak najprostsze wyrażenia. A jeśli utknąłeś na jakimś problemie, skorzystaj z niezliczonych podpowiedzi
zamieszczanych w internecie.
step
Atrybut step może być stosowany w przypadku typów wejściowych number, range i t ime. Określa on przyrosty
wartości liczb, które dany element i nput może przyjąć. Spójrz dla przykładu na znajdujący się wcześniej w tym
rozdziale listing 5.6 (dotyczący typu wej ściowego n umber).
Nakładanie ograniczeń n a wprowadzane dane 127
Android
Chrome 10.0+
Fi refox
Internet Explorer
iOS Safari
Opera 11.0+
Safari 5.1
min i max
Użyte n a elemencie meter i n a typach wejściowych n umber oraz range atrybuty mi n i max ustalają dozwolony
zakres wartości elementu. Nie są to wymagane atrybuty i możesz użyć albo mi n, albo max, albo ich obu.
Ustalają one reguły walidacji i jeśli wartość elementu nie będzie się mieścić w zakresie od mi n do max, formularz
nie zostanie wysłany. Mogą być użyte na typach wej ściowych date i t i me do nałożenia ograniczenia na
wartość wybieraną przez użytkownika, na przykład w przypadku kalendarza wydarzeń lub wyszukiwania
pomiędzy datami.
Android
Chrome 10.0+
Fi refox
Internet Explorer
iOS Safari
Opera 11.0+
Safari 5.1
Android
Chrome 10.0+
Fi refox 5 . 0+
Internet Explorer
iOS Safari
Opera 11.0+
Safari
128 Rozdział 5. Form u l a rze internetowe HTML5
Możesz umieścić noval i date w elemencie formularza, dzięki czemu w momencie wysyłania go wszelkie
nieprawidłowe formaty lub puste pola zostaną zignorowane:
<form noval i date>
Możesz również umieścić atrybut formnova l i date w każdym poszczególnym elemencie formularza.
Na przykład mógłbyś umieścić go przy typie wejściowym url , a przeglądarka będzie ignorować walidację
tego elementu (choć wątpliwe j est, żebyś chciał podjąć wysiłek używania typu wejściowego url , a następnie
ignorował walidację).
<styl e>
* {marg i n : O; fon t : 13px tahoma, verdana , sans-ser i f ; padd i ng : O ; }
form { paddi ng-top : lOpx; w i d t h : 3 1Opx ; }
l i { e l ear: bot h ; l i st -styl e-type : non e ; marg i n : O O 10px ; }
l abel { d i spl ay : b l oe k ; fl oat : l eft ; marg i n : O lOpx O O ; padd i n g : 5px ; text-al i gn : ri ght ; w i d t h : 100px}
i nput { bae kground-pos i t i on : 3px 5px ; bae kground-repeat : no-repeat ; border-rad i u s : 5px ; padd i n g : 5px 5px 5px
�25px; w i d t h : 155px ; }
i nput : foeus { out l i ne : none ; }
i nput : i nval i d : requi red { bae kground- image : url (aster i s k . png) ; box-s hadow: Opx Opx 5px #fObbI 8 ; border: 2px
�sol i d #fObb I8 ; }
i nput : focus : i nval i d { bae kground- image : url ( i nval i d . png) ; box-s hadow: Opx Opx 5px #bO I 2 1 2 ; border:
�2px sol i d #bO I 2 1 2 ; }
i nput : val i d { bae kground- i mage : url (aeeept . png) ; border: 2px sol i d #7ab526 ; }
i nput [type=submi t] {baekgroun d : #7ab5 2 6 ; border: none; box-s hadow: Opx Opx 5px #7ab526 ; eol or: #fff;
�eursor: poi nter; fl oat : r i g h t ; font-we i gh t : bol d ; padd i ng-l eft : 5px; w i dt h : auto ; }
</styl e>
<form>
<ol >
<l i ><l abel for= " tel ">Tel . : </l abe l >< i nput pl aeehol der= " np : 0 12345 " requ i red type= " tel " i d= " tel "
�name= " tel " /></l i >
<l i ><l abel for="webs i te " >W i tryna : </l abel ><i nput requi red type= " url " i d= " webs i te" name= " webs i te " /></l i >
< l i ><1 abel for=" ema i l . . >E-ma i l : </1 abel ><i nput requ i red type= " emai l " i d = " emai l " name= " ema i l " /></1 i >
< l i >< i nput type= "subm i t " val ue= " Wyśl i j formul arz " /></1 i >
</0 1 >
</form>
Kom u n i katy błędów 129
Witryn a : [O bla-bla.pll
E-mail : [*
�____________-J
Rysunek 5 . 23. CSS3 użyty do odwołania się do różnych stanów pól formularza
Gdy pole jest aktywne, ale jest puste lub zawiera nieprawidłową treść, pojemnik j est czerwony i pokazuje
ikonkę wskazującą błąd. Jeśli zawartość j est poprawna, przeglądarka zmienia kolor pola na zielony i ikonkę
na symbol fajki. A wszystko to bez JavaScriptu!
Nie omówimy wszystkich nowych możliwości CSS-a, ale na rysunku 5.23 użyliśmy następuj ących
pseudoklas z CSS3 Basic User Interface Module! (http://www.w3.orglTR/css3-ui):
• : va l ; d - element formularza otrzymuje tę klasę, gdy jego zawartość jest zgodna z typem
elementu i przeszła walidację.
• : ; nva l ; d - jeśli element formularza ma niepoprawną zawartość, stosowana jest ta klasa.
• : req u ; red - do tej klasy będzie przypisany każdy element formularza, który ma ten atrybut.
Komunikaty błędów
Mamy nadzieję, że śledziłeś poprzednie przykłady i zauważyłeś, iż komunikaty o błędach są wyświetlane
inaczej w Operze i Firefoksie. Opera wyświetla drgaj ący, różowy komunikat o błędzie, podczas gdy Firefox
pokazuj e spokojniejszy, nieruchomy komunikat. Obecnie nie można zmienić sposobu, w jaki te komunikaty
są wyświetlane, ponieważ są generowane przez przeglądarkę. W przyszłości może się to zmienić, ponieważ
obecnie ma miejsce dyskusja, czy umożliwić ich edycję za pomocą CSS-a. A zatem chociaż możesz narzekać,
że komunikaty o błędach wyglądają okropnie lub nie spełniają firmowych wytycznych odnośnie do wyglądu
stron, będą one w danej przeglądarce spójne, co z pewnością oznacza zwycięstwo użyteczności, gdyż
użytkownicy oczekują j ednego stylu błędów podczas korzystania z wybranej przez siebie przeglądarki.
Chociaż nie możesz jeszcze zmienić stylu błędów, możesz zmodyfikować tekst komunikatu błędu przy
użyciu JavaScriptu i metody setCustomVa l ; d ; ty ( ) . Listing 5 . 1 4 zawiera przykład, w którym domyślny
komunikat o błędzie zostaje nadpisany, gdy zostaną dodane nieprawidłowe treści.
<form>
<l abel for= " emai l ">E-mai l <jl abel >
< i nput oni nput="check O " type = " emai l " i d = " emai l " name= " emai l " j>
< i nput type= " subm i t "j>
<jform>
<scri pt>
funct i on chec k ( ) (
var emai l Input = document . getEl ementBy l d ( " emai l " ) ;
ema i l I nput . s et C u s tomVal i d i ty ( " Btąd. Proszę popraw i ć . " ) ;
<jscri pt>
<l i ><l abel for= " j ob " > Praca</l abe l >< i nput l i st = " j obl i s t " requi red type = " tex t " i d= " j o b " name= " j ob" />
<datal i st i d = " j obl i s t " >
<opt i on l abe 1 = " kosmi czny kowa l " v a l u e = " kosm i czny kowa l " >
<opt i on l abel = "mi ędzynarodowy pl ayboy" val ue= "mi ędzynarodowy pl ayboy" >
<opt i on l abel = " twórca stron i nternetowych" val ue=" twórca stron i nternetowyc h " >
<opt i on l abel = " proj e ktant stron i nternetowyc h " val ue= " proj ektant stron i nternetowyc h " >
<opt i on l abel = " złota rąc z ka" val ue = " z łota rączka">
</datal i st>
c/l i >
< l i ><l abe l for= " s a l ary">Przyb l i żone roczne dochody</l abe l ><i nput p l aceho l der= " z ł " requ i red m i no" O "
'-+--s tep= " IOOQ" type=" number" i d = " sal ary" name= "sal ary" /></l i >
</01 >
</f i e l dset>
<fi el dset>
<l egend>Dane konta ktowe</l egend>
<ol >
<l i ><l abel for= " addres s 1 ">Pi erwszy wi ersz adresu</l abel ><i nput req u i red type= " text" i d= " addres s 1 "
�name = " addres s 1 " /></l i >
<l i ><l abel for= " addres s 2 ">Drugi wi ersz adresu</l abe l >< i nput requ i red type= " text" i d = " address 2 "
�name = " address 2 " /></l i >
<l i ><l abel for= " country">Kraj</l abe l >< i nput l i st = " countryl i st " req u i red type= " text " i d= " country"
4-name = " country" />
<datal i st i d = " countryl i st " >
<opt i on l abel = " Po l s ka" val ue= " Pol s ka " >
<opt i on l abel = " W i el ka Brytan i a" val ue= " W i e l ka Brytan i a" >
<opt i on l abel = " USA" val ue= " USA">
</datal i st>
c/l i >
< l i ><l abel for= " tel " >Tel . </l abe l >< i nput pl acehol der= " np . 0 12345" type= "tel " i d= " tel "
�name= " t e l " /></1 i >
<l i ><l abel for= " emai l ">E-mai l </l abel ><i nput requ i red type= " emai l " i d= " ema i l " name= " ema i l " /></l i >
</01 >
</f i e l dset>
<d i v i d= " personal i se" >
<h2>Dostosuj swój profi l </h2>
<l abel for= "col or">Wybi erz kol or strony</ l abel >
<i nput type=" col or" i d= " col or" name= " col or" />
</d i v>
<d i v i d= " range ">
<l abel for= " exc i ted">Jak w s kal i od 1 do 10 podobaj ą C i się formu l arze HTML5?</l abe l >
<i nput m i n= " l " max= " IO" step= " O . 5 " type= II range" i d= " exci ted" name= " ex c i ted" />
<output onformi nput= " val ue=exc i ted . val ue + ' / 10 "' ></output>
<i nput type= " submi t " val ue = " Zap i sz s i ę " />
</d i v>
c/form>
c/body>
</html >
Na rysunku 5.24 użyliśmy typów wejściowych te ' , date, ur' , number, text, ema ; ' , co, or i range. W kilku
przypadkach został także użyty element data' ; st, choć mogliśmy alternatywnie wykorzystać element se' ect,
ale w tym formularzu chcemy, aby ludzie wpisywali swoje stanowiska lub kraj e z klawiatury. Pod koniec
formularza został wykorzystany typ wejściowy range i wraz z nim element output. Wszystkie elementy
zawierające dane, które musimy uzyskać, mają atrybut requ ; red, który uruchomi wbudowany w przeglądarkę
mechanizm walidacji. Aby formularz wyglądał trochę ładniej, dodaliśmy kilka nowych selektorów CSS3,
dzięki czemu pokazaliśmy stany wymagany, prawidłowy i nieprawidłowy elementów.
132 Rozdział 5. Form u l a rze internetowe HTML5
* II ol
Data urodzenia Drugi wiersz adresu
nttpJ/mO)a_
Wl-"
ry_
na-'pI______
Pra ca Tel.
I kosmiczny kowal
ol np 012345
J
przyblizone roczne dochody
*8
E-mail
zl @
f"iiiiii .:J
Jak w skai od 1 do 10 podoba� Ci się fonnuiarze łlTMLS?
D
5.5 / 10
Zapisz się
Rysunek 5 . 24. Formularz rejestracyjny w Operze 11 utworzony przy użyciu kilku elementów HTML 4,
nowych elementów formularzy HTML5 oraz CSS-a
Podsumowanie
W tym rozdziale poznałeś wszystkie nowe typy wejściowe i dowiedziałeś się, j ak działaj ą w przeglądarkach,
jeśli są w pełni obsługiwane. Te nowe typy wej ściowe wraz z wbudowaną natywną walidacj ą i nowymi
funkcjami CSS w dobrych przeglądarkach bardzo ułatwiaj ą tworzenie formularzy i przekazywanie
użytkownikowi informacji zwrotnych. Chociaż czeka nas jeszcze długa droga, zanim wszystkie przeglądarki
nadrobią zaległości, pewnego dnia - im szybciej , tym lepiej - nie będziesz musiał polegać na JavaScripcie,
by tworzyć użyteczne funkcje, takie jak kontrolki wyboru daty, suwaki czy walidacja. Wszystkie nowe typy
wejściowe, takie jak ema ; l , te l i date, działają w przeglądarkach choćby poprzez pokazywanie pola tekstowego,
więc nie ma żadnego usprawiedliwienia, by nie zacząć z nich korzystać.
6
Rysowanie na płótnie
W
2004 roku firma Apple opracowała na potrzeby aplikacji Dashboard systemu operacyjnego Mac OS X
element o nazwie canvas (płótno), przeznaczony do rysowania, który został później zaimplementowany
w przeglądarce Safari. Specyfikacja HTMLS wykorzystała ten element i powiązany z nim zestaw interfejsów
API, aby zapewnić realizację podstawowych funkcji rysowania - przed wprowadzeniem elementu c a nv a s
przeglądarki wymagały do tego celu zastosowania zewnętrznej wtyczki. Ponieważ c a n v a s jest mechanizmem
rysowania bazuj ącym na pikselach, a nie wektorach czy warstwach, podstawowa funkcjonalność jest dość
prymitywna, ale jak dowiesz się w tym rozdziale, i tak umożliwia tworzenie zaawansowanych obrazów.
Android 2 . 1+
Chrome 10.0+
Fi refox 3 . 6+
Internet Explorer 9 . 0+
iOS Safari 3 . 2+
Opera 10.6+
Safari 3 . 2+
134 Rozdział 6 . Rysowanie na płótn ie
I
Uwaga
W przypadku Internet Explorera S i starszych wersj i będziesz potrzebował zewnętrznej bibl iote k i .
Możesz w tym ce lu użyć b i b l i oteki explorercanvas ( http://code.google. com/p/explorercanvas) .
J ako że potrzebujesz jej tyl ko w przypadku I ES, możesz um ieścić ją w poleceniu warun kowym :
< ! - - [i f l te JE 8] ><scri pt src= " excanvas . j s "></scri pt>< ! [end i f] - ->
P oczątki
Listing 6.1 zawiera kod potrzebny do uruchomienia elementu canvas. Jeśli wypróbujesz ten kod w przeglądarce,
na płótnie nie zobaczysz niczego, ponieważ nie został dodany służący do rysowania na nim kod javascriptowy.
Prostym sposobem sprawdzenia, czy płótno zostało wyświetlone przez przeglądarkę, jest dodanie obramowania
elementu przy użyciu CSS-a.
Element canvas ma standardowe atrybuty elementu HTML - nawet w skrajnym przypadku będziesz
potrzebował atrybutów i d, wi dth i h e i ght, aby ustawić rozmiar płótna i móc odwoływać się do niego z poziomu
JavaScriptu. Ponadto elementowi canvas można, j ak każdemu innemu elementowi, nadać styl za pomocą
CSS-a. Na listingu 6 . 1 zostało zastosowane obramowanie, dzięki któremu możesz szybko sprawdzić, czy
płótno zostało umieszczone na stronie i czy ma poprawne wymiary. Mógłbyś również dodać kolor lub styl
dla tła - zostaną one wyświetlone, ponieważ płótno jest domyślnie przezroczyste i nie przesłania niczego,
co się pod nim znaj duje. Właściwość tę można wykorzystać, nakładając płótno na inne elementy HTML,
tak aby można było następnie na nich rysować.
Zauważ, że na listingu 6 . 1 uwzględniliśmy w kodzie znacznik zamykający </canvas>. Wynika to z chęci
udostępnienia pojemnika na treść zastępczą i dostosowania kodu do wymagań przeglądarki Firefox, która
oczekuje treści zastępczej . Dzięki pojemnikowi na treść zastępczą przeglądarka może wyświetlić zawartość
znajduj ącą się pomiędzy znacznikami, jeśli element canvas nie j est przez nią obsługiwany. Podobnie j ak
w przypadku innych elementów, w celu wyświetlenia treści tekstowej mógłbyś tu umieścić atrybut a l t,
ale jeśli znacznik canvas nie jest obsługiwany, zostanie wyświetlona dowolna treść znajdująca się między
otwieraj ącym i zamykającym znacznikiem elementu, co może być przydatne do wyświetlania rozmaitych
elementów, od tekstu do obrazków.
Aby rysować na płótnie z poziomu JavaScriptu, najpierw musisz odwołać się do kontekstu płótna,
na którym chcesz rysować. W tym celu Twój skrypt wyszuka za pomocą identyfikatora element canvas,
a następnie użyje metody getContext, aby odwołać się do dwuwymiarowego kontekstu tego elementu.
Nałożenie siatki na płótno 135
To ostatnie odwołanie tworzy powiązanie przydatne później do zmieniania pikseli płótna, jak pokazano
poniżej :
<canvas i d= "mycanvas" wi dth= " 640" hei ght= "480" ><jcanvas>
<scri pt>
var canvas = document . getEl ementByl d ( ' mycanvas ' ) . getContext ( ' 2d ' ) ;
<jscri pt>
Metodę getContext można również wykorzystać do weryfIkacji skryptu i ustalenia, czy dana przeglądarka
obsługuje rysowanie na płótnie. Zwykłe sprawdzenie z użyciem elementu eanvas zwróci wartość t rue, jeśli
jest on obsługiwany, lub wartość fa 1 se w przeciwnym wypadku:
var canvas = document . getEl ementByl d ( 'mYcanvas ' ) ;
i f (canvas. getContext) {
II element canvasjest obsługiwany
Zależnie od tego, czy przeglądarka obsługuj e funkcjonalność płótna, J avaScript mógłby następnie
rozpocząć realizację odpowiednich procedur.
Współrzęd ne x i y
Ostatnim obszarem, który musimy omówić przed przejściem do podstawowych narzędzi rysujących płótna,
jest wykorzystywany do rysowania na płótnie układ współrzędnych. Jako że płótno bazuje na pikselach,
układ współrzędnych (x, y) jest używany do określania konkretnej pozycji albo aktualizowanego piksela.
Współrzędne te są również wykorzystywane przez różne narzędzia do oznaczania punktów początkowych
i końcowych oraz innych pozycji. Jeśli używałeś programów takich jak Adobe Photoshop, korzystanie
z systemu rysowania opartego o współrzędne nie będzie dla Ciebie niczym nowym. Kluczową sprawą
jest to, że w tym układzie współrzędnych punkt (O, O) znajduje się domyślnie w lewym górnym rogu płótna,
przy wartości x rosnącej przy przesuwaniu się w prawo oraz wartości y rosnącej przy przesuwaniu się w dół.
W ramach wprowadzenia do niektórych narzędzi rysujących i systemu siatki płótna w pierwszym przepisie
w tym rozdziale nałożymy siatkę na płótno, tak abyś ją zobaczył.
I
....
(0. \ 00) (100 . 1 00) (200.100) (300. 100) (400.100) -(500. 1 00) (6ll0. 1 CoO)
I
(0.2lI0) ( 1 00.200) (200.200) (300.200) (400.200) (500.200) (000.200)
I
(0.300) (1 00.300) (200.300) (300.300) (400.300) (500.300) (6ll0.300)
I
(0.400) (1 00.400) (200.400) (300.400) (400.400) (500.400) (600.400)
...
Rysunek 6 . 1 . System współrzędnych siatki płótna narysowany z wykorzystaniem metod line, arc i fillText
Sprawmy, aby siatka była widoczna, wykonując następujące kroki i wykorzystując listing 6.2:
1. Utwórz pustą stronę HTML z takimi znacznikami wewnątrz znacznika body, jakie przedstawiono
na listingu 6.2, w tym z otwierającym i zamykającym znacznikiem canvas oraz zastępczym tekstem
pomiędzy nimi.
2. Dodaj blok styl e z identyfikatorem płótna jako selektorem.
3. Dodaj wywołanie metody wi ndow . addEventL i stener, aby uruchomić funkcję showGr i d, kiedy strona
zostanie załadowana, oraz deklaracje zmiennych płótna i kontekstu.
4. Dodaj funkcję showG r i d rysującą linie, punkty i tekst na płótnie.
<jstyl e>
<scri pt>
II inicjalizuj pozycje x i y
var xPos O;
var yPos = O ;
II oblicz pozycje x i y
xPos x*l i neSpac i ng ;
yPos = y*l i neSpac i ng ;
II sprawdź. czyjesteś na ostatniej linii pionowej. a jeśli tak. wnieść tekstpo lewej stronie
i f (x==numVert i cal Li nes) {
context . t extAl i gn = ' ri ght ' ;
xPos -= 5 ;
el se {
context . textA l i gn ' l eft ' ;
xPos += 5 ;
II sprawdź, czyjesteś na ostatniej linii poziomej, a jeśli tak, umieść tekst powyżej
i f (y==numHori zontal Li nes) {
yPos -= 8 ;
el se {
yPos += 1 2 ;
<jscri pt>
<jhead>
<body>
<h 1>System s i at k i ptótna<jh1>
<canvas i d= "canvas" width="600" hei ght="400">
El ement canvas nie jest obsługi wany przez Twoją przegl ądarkę .
Uruchom tę stronę w i nnej przegl ądarce .
</canvas>
<jbody>
<jhtml >
Nie będziemy zbytnio wchodzić tutaj w szczegóły dotyczące samego kreślenia linii, punktów czy tekstu,
ponieważ odpowiednie metody i efekty zostaną opisane w dalszej części rozdziału. Najważniejsze, aby zrozumieć
proces, z którym masz do czynienia, gdy chcesz rysować na płótnie. W tym przepisie utworzyłeś w HTML-u
element canvas, który ma 600 pikseli szerokości i 400 pikseli wysokości. Elementowi temu został nadany styl
zawierający obramowanie i kolor tła. Ponieważ płótno jest domyślnie przezroczyste i rysowane są na nim
tylko linie, punkty i tekst, po załadowaniu strony w przeglądarce obramowanie oraz kolor tła będą widoczne.
Po wczytaniu strony zostanie uruchomiona funkcja s howGri d. Najpierw musisz utworzyć odwołanie do
elementu canvas, a następnie pobrać kontekst, który będzie używany do rysowania. Gdy masz już kontekst,
możesz określić podstawowe ustawienia, takie j ak szerokość linii (za pomocą metody context . 1 i neWi dth)
czy używany kolor (za pomocą metody context . stro keSty1 e). Następnie ustawiasz odstęp między liniami
siatki na 1 00, a początkowe współrzędne na (0,0) oraz określasz liczbę linii poziomych i pionowych, które
należy narysować. Na podstawie tych informacji kreślone są następnie kolejne linie poziome, rozmieszczone
co 100 pikseli. Po ukończeniu linii poziomych od lewej do prawej kreślone są linie pionowe, każda rysowana
od góry do dołu płótna. Pamiętaj, że w przypadku kierunku y na górze znajduje się wartość O, która wzrasta
w miarę przesuwania się w dół.
Gdy linie siatki są j uż wykreślone, dodawane są punkty przecięcia i ich współrzędne. Aby nanieść punkty,
należy w każdym punkcie przecięcia narysować koło za pomocą narzędzia łuku, któremu przyjrzysz się bardziej
szczegółowo w dalszej części rozdziału. Etykiety ze współrzędnymi są wyświetlane za pomocą metody fi 1 1 Text.
Tworzen ie prostych kształtów i l i n i i 139
Domyślnie tekst będzie wyrównany do lewej i górnej krawędzi, co sprawdza się w przypadku wszystkich
punktów siatki z wyjątkiem wysuniętej najbardziej na prawo kolumny i najniższego wiersza. Gdybyś próbował
wyświetlić te etykiety, nie zmieniając wyrównania tekstu, ich zawartość nie byłaby widoczna, ponieważ byłyby
narysowane w kontekście, ale poza obszarem wyświetlania płótna. Zamiast tego skrypt określa, czy punkt
jest w ostatniej kolumnie lub w dolnym rzędzie, i odpowiednio modyfIkuje wyrównanie dla tej pozycji.
Uwaga
Pon ieważ płótno bazuje na operacjach przeprowadzanych na pikselach, w dwuwym iarowej rzeczywistości
kluczowe znaczenie ma kolejność rysowani a . W Canvas API nie istnieje pojęcie warstw, więc kolejność,
w ja kiej rozbudowujesz rysunek, wpływa na końcowy rezultat. Jeśli na przykład dodasz na płótn ie tekst
drogowskazu przed narysowan iem samego znaku posiadającego nie przezroczyste tło, Twój tekst zostanie
przykryty. Zam iast tego powin ieneś narysować tło znaku, a następnie um ieścić na n i m tekst.
Właśnie narysowałeś swój pierwszy rysunek na płótnie i j ednocześnie dowiedziałeś się, jak działa system
siatki z kontekstem płótna. Teraz spójrzmy na podstawowe kształty, które można rysować, metody oraz
niektóre efekty dostępne w ramach funkcjonalności płótna.
Narzędzia płótna
Płótno oferuje podstawowy zestaw narzędzi, za pomocą których możesz tworzyć wiele prostych i złożonych
kształtów. W kolejnym przepisie otrzymasz zwięzłe informacje o każdym z tych narzędzi i poznasz kilka
prostych sposobów ich użycia.
• Rectangle - rysuje we wskazanym miejscu prostokąt o określonej szerokości i wysokości;
• Line - tworzy linię pomiędzy punktami A i B;
• Path - tworzy ścieżkę za pomocą co najmniej jednej linii lub krzywych;
• Arc - tworzy łuk o zadanych wymiarach; jest też używany do tworzenia okręgów;
• Curve - tworzy jeden z dwóch rodzajów krzywych: Beziera lub kwadratową.
Oprócz podstawowego zestawu narzędzi udostępnianych przez płótno istnieje kilka efektów, które mogą
być używane w połączeniu z narzędziami:
• Fili - określa parametry wypełnienia kształtu;
• Stroke - określa rodzaj linii używanych w kształtach;
• Gradient - pozwala na wykorzystanie liniowych albo radialnych wzorów wypełnień w kształtach;
• Transparency określa poziom przezroczystości wszystkich lub wybranych kształtów na płótnie
-
kształtów na płótnie.
W tym rozdziale spotkałeś się już z metodami dotyczącymi powyższych efektów. Oto niektóre z bardziej
popularnych metod dostępnych dla kształtów:
• st rokeStyl e - określa kolor lub styl dla linii wokół kształtów;
• fi 1 1 Styl e - określa kolor lub styl używany wewnątrz kształtów;
• s hadowOffsetX/shadowOffsetY - określaj ą odległość cienia względem kształtu;
• shadowBl ur - określa poziom efektu rozmycia;
• shadowCo l or - określa kolor cienia;
• createL i nearGrad i ent - tworzy gradient liniowy wewnątrz kształtu;
• createRad i a l Grad i ent - tworzy gradient radialny wewnątrz kształtu.
Jeśli więc zastosujesz kilka dostępnych efektów na kwadracie, który narysowałeś wcześniej za pomocą
fi 1 1 Rect, możesz otrzymać dość uroczy fioletowy kwadrat z fioletowym cieniem (patrz listing 6.3). W przypadku
któregokolwiek z efektów dotyczących koloru, takich jak strokeStyl e, fi 1 1 Styl e czy shadowCo l or, do określania
kolorów nie musisz używać formatu RGBA; możesz skorzystać z wartości szesnastkowych albo HSLA. Musisz
jednak pamiętać, że wszystkie style i efekty należy zastosować przed użyciem metody fi 1 1 Rect, gdyż metoda
ta nakazuje kontekstowi płótna wyświetlenie kształtu, a ponieważ kształt j est wyświetlany za pomocą
poszczególnych pikseli, nie ma sposobu, aby zmodyfikować go po narysowaniu. Aby zmodyfikować kształt,
należy go przerysować, o czym przeczytasz w dalszej części rozdziału.
Tworzen ie prostych kształtów i l i n i i 141
<canvas i d= " canvas" w i dt h = " 640" hei ght = " 480 " ><jcanvas>
<scri pt>
var canvas = document . getEl ementByl d ( ' canvas ' ) . getContext ( ' 2d ' ) ;
canvas . s hadowOffsetX = 1 0 ;
canvas . s hadowOffsetY = 1 0 ;
canvas . s hadowBl ur = 1 0 ;
canvas . s hadowCol or = ' rgba (200 , O , 200 , . 3 ) ' ;
canvas . fi 1 1 Styl e = ' rgba (200, O , 200, 1) ' ;
canvas . fi 1 1 Rect ( O , O , 100, 100) ;
<jscri pt>
Dostępne kody kolorów uwzględniaj ą ustawienia przezroczystości, co może być niezmiernie przydatne
podczas tworzenia obrazów na pierwszy rzut oka zawieraj ących warstwy. Na listingu 6.3 półprzezroczystej
wartości RGBA cienia użyto, aby uzyskać najbardziej pożądany efekt cienia.
Istnieją dwie inne metody związane z rysowaniem prostokątów: el earReet (poz-x , poz-y , szerokość,
wysokość) i stro keReet (poz-x, poz-y , szerokość, wysokość) . Metoda el earReet czyści piksele w danym
prostokątnym obszarze, co powoduje usunięcie w nim wszelkich zmian pikseli i przywrócenie standardowego,
przezroczystego stanu. J ak dowiesz się w dalszej części rozdziału, metoda e l ea rReet jest niezwykle pomocna
w animacji i może być wykorzystywana do wyczyszczenia całego płótna (przez użycie jego wymiarów jako
szerokości i wysokości) lub j ego fragmentu. Natomiast stro keReet w połączeniu z l ; neW; dth rysuje linię
o określonych przez Ciebie współrzędnych oraz szerokości i wysokości. Użycie kombinacji kształtów i efektów,
jak na listingu 6.4, skutkuj e jeszcze bardziej oryginalnym rysunkiem.
var canvas = document . getEl ementByl d ( ' canvas ' ) . getContext ( ' 2d ' ) ;
canvas . s hadowOffsetX = 1 0 ;
canvas . s hadowOffsetY = 1 0 ;
canvas . s hadowBl ur = 1 0 ;
canvas . s hadowCol or = ' rgba (200 , O , 200 , . 3 ) ' ;
canvas . fi 1 1 Styl e = ' rgba (200, O , 200, 1) ' ;
canvas . strokeStyl e = ' #09c ' ;
canvas . l i neWidth = 5;
canvas . fi I I Rect ( O , O , 100 , 100) ;
canvas . c l earRect (25 , 2 5 , 50 , 50) ;
canvas . strokeRect(25, 25 , 50, 50) ;
<canvas i d= " canvas" w i dt h = " 640" hei ght = " 480 " ><jcanvas>
<scri pt>
var canvas = document . getEl ementByl d ( ' canvas ' ) . getContext ( ' 2d ' ) ;
var grd = canvas . createLi nearGrad i ent ( O , 200 , 200 , O) ;
142 Rozdział 6 . Rysowanie na płótn ie
var grd = eanvas . e reateRad i al Gradi ent (300 , 2 5 0 , 2 , 200 , 200 , 250) ;
grd . addCol orStop ( O , ' #000 ' ) ;
grd . addCo l orStop ( 1 , ' #eee ' ) ;
eanvas . fi l l Styl e = grd ;
eanvas . fi l l Reet (200 , 200 , 200 , 200) ;
eanvas . e l osePath () ;
<jseri pt>
Powyższy kod tworzy w lewym górnym rogu prostokąt z gradientem liniowym, a w prawym dolnym
rogu prostokąt z gradientem radialnym. Obie metody gradientowe używają różnych parametrów do określania
zachowania gradientu:
ereateLi nearGrad i ent (startX, start Y , kon i eeX, kon i e c Y)
ereateRad i a l Grad i ent (startX, start Y , star tProm i eń , kon i eeX, kon i e c Y , kon i ecPromień)
Metoda gradientu radialnego może się wydawać nieco skomplikowana, więc warto poćwiczyć jej stosowanie;
wypróbuj różne parametry addCo l orStop, aby zobaczyć, co się stanie.
Na listingu 6.6 kreślenie linii rozpoczynamy blisko lewego górnego rogu, w punkcie (10, 10), a za pomocą
metody l ; neTo ustawiamy koniec linii w punkcie (630, 470), w pobliżu prawego dolnego rogu płótna, j ak
pokazano na rysunku 6.3.
<eanvas i d= " eanvas" w i dt h = " 640" hei ght = " 480 " ><jeanvas>
<seri pt>
var eanvas = doeument . getEl ementByl d ( ' eanvas ' ) . getContext ( ' 2d ' ) ;
eanva s . moveTo ( l O , lO) ;
eanvas . l i neTo (630,470) ;
eanvas . stroke ( ) ;
<jseri pt>
Tworzen ie prostych kształtów i l i n i i 143
Pamiętaj, że aby uwzględnić na rysunku takie własności linii jak szerokość czy kolor, należy je zdefiniować
przed wywołaniem metody stroke.
Metody l ; neTo możesz użyć wielokrotnie, aby narysować różne kształty, tworząc "ścieżkę", którą podąża
linia. Spójrzmy na proces rysowania trójkąta na listingu 6.7, na którym metodę l ; neTo wykorzystuje się trzy
razy do narysowania trzech boków trójkąta.
<eanvas i d= " eanvas" w i dt h = " 640" hei ght = " 480 " ><jeanvas>
<seri pt>
var eanvas = doeument o getEl ementByl d ( ' eanvas ' ) o getContext ( ' 2d ' ) ;
eanvas o stroke ( ) ;
<jseri pt>
Aby linie tworzyły jedną ścieżkę, należy użyć dwóch kolejnych metod: beg ; n Path i el osePath. beg; n Path
mówi płótnu, że kolejne linie albo krzywe należą do jednego obiektu ścieżki, który j est "zamykany" po
wywołaniu metody e l osePath. Ś cieżka złożona z linii lub krzywych może być wtedy traktowana - analogicznie
do prostokąta - jako j eden obiekt i jednolicie formatowana.
Podglądaj ąc kształt utworzony przy użyciu kodu z listingu 6.7, zauważysz, że w miej scu, gdzie koniec
linii zbiega się z j ej początkiem (lewy górny róg), linia j est nieco nierówna, gdyż oba końce nie łączą się
płynnie. Istnieje na to j ednak sposób - właściwość l ; neCap, która przyjmuj e wartości butt, round albo
square. To rozwiąże kwestię punktu końcowego, ale co z pozostałymi wierzchołkami trójkąta? Możesz użyć
do tego właściwości l ; neJ o ; n, która przyjmuje wartości beve l , m; ter albo round:
eanvas o l i neCap = ' round ' ;
eanvas o l i neJoi n = ' round ' ;
144 Rozdział 6 . Rysowanie na płótn ie
Jak się niebawem przekonasz, ścieżki mogą zawierać segmenty składające się nie tylko z linii, ale również
z krzywych.
<jstyl e>
<scri pt>
II zmienne globalne płótna i kontekstu
var canva s ;
v a r context ;
II wyczyść płótno
context . cl earRec t ( O , O , canvas . wi dt h , canvas . he i g ht ) ;
II rozpocznij ścieżkę
context . begi nPath () ;
<jscri pt>
<jhead>
<body>
<h 1>Użyc i e śc i eżek pfótna : <jh1>
<canvas i d = " canvas " wi dth= " 400" he i ght="400 " >
El ement canvas n i e j est obsfugi wany w Twojej przegl ądarc e .
<jcanvas>
<br>
Li czba boków: <i nput t ype= II number" i d = " numS i des" m i n = " 3 " step= " l " val ue= " 7 " /><br>
Prom i e ń : < i nput type= II number" i d = " rad i us " m i n= " IO " step= " l " val ue= " 150" /><br>
<button i d= " drawPo l ygon">Narysuj wi e l o kąt<jbutton>
<jbody>
<jhtml >
146 Rozdział 6 . Rysowanie na płótn ie
Po kliknięciu przycisku Narysuj wielokqt zostaj e wywołana funkcja d rawPo l ygon. Funkcja ta pobiera
najpierw od użytkownika dane dotyczące liczby boków i promienia wielokąta. Następnie znajduje na płótnie
punkt środkowy, aby umieścić wielokąt w centrum płótna. Wreszcie płótno zostaje przy użyciu funkcji
c l ea rRect wyczyszczone, tak aby za każdym razem, gdy użytkownik tworzy nowy wielokąt, był on rysowany
na pustym płótnie.
Teraz, gdy płótno j est przygotowane, rozpoczynamy ścieżkę za pomocą metody beg; nPath. Następnie
przy użyciu pewnych wymyślnych algorytmów wyznaczamy początkowe współrzędne (x, y) i przechodzimy
w pętli przez każdy wierzchołek, używając do tworzenia odcinków metody l ; neTo. Po wykreśleniu wszystkich
odcinków ścieżka jest zamykana za pomocą metody cl osePath. Metoda ta łączy wszystkie odcinki w jedną
ścieżkę, której następnie używamy do narysowania linii i wypełnienia kształtu, w wyniku czego na płótnie
rysowany jest wielokąt. Rysunek 6.4 prezentuje przykład.
Liczba boków: 5
�rnrim � l�
� -------"�
�
I Narysuj wielokąt I
Rysunek 6.4. Wielokąt o pięciu bokach i promieniu 150
utworzony z wykorzystaniem funkcjonalności ścieżki
Spójrzmy na przykład z listingu 6.9, który daje efekt pokazany na rysunku 6.5.
<canvas i d= " canvas" w i dt h = " 640" hei ght = " 480 " ><jcanvas>
<scri pt>
var canvas = document . getEl ementByl d ( ' canvas ' ) . getContext ( ' 2d ' ) ;
Dodawanie tekstu 147
canvas . stroke ( ) ;
<jscri pt>
Na listingu 6.9 wartość prze c i wn i eDoRuchuWskazówek została ustawiona na true. Spróbuj ustawić ją na
fal se i zobacz, co jest rysowane.
Aby narysować koło, użyj metody arc ( ) , wpisując jako kąt początkowy o, a jako końcowy 2*Math . P I :
canvas . arc ( lO O , 1 5 0 , 60, O , 2 * Mat h . P I , fal se) ;
canvas . fi l l Styl e = ' #000 ' ;
canvas . fi l l () ;
canvas . strokeStyl e =' #000 ' ;
canvas . l i neWi dth =2;
Rysowanie krzywych
Na płótnie wykorzystywane są dwa rodzaj e krzywych: quadrat i cCurveTo i bez i erCu rveTo . Różnią się one
tym, że quadrat i cCurveTo posiada jeden punkt kontrolny, natomiast bez i erCurveTo dwa. Punkty kontrolne
-
umożliwiają dodawanie krzywych do linii, dzięki czemu można tworzyć bardziej złożone kształty:
canvas . quadrat i cCurveTo (pX, p Y , kon i eeX, kon i e c Y) ;
canvas . bezi erCurveT o (plX, p l Y , p2X, p2Y, kon i eeX, kon i ec Y) ;
W obu przypadkach rodzaj czcionki, jej grubość oraz rozmiar można zmieniać za pośrednictwem
właściwości font, a styl wypełnienia i obramowania może być kontrolowany poprzez właściwości fi 1 1 Styl e
i strokeStyl e, co pokazano na listingu 6 . 1 0 .
148 Rozdział 6 . Rysowanie na płótn ie
<scri pt>
var canvas = document . getEl ementByl d ( ' canvas ' ) . getContext ( ' 2d ' ) ;
canvas . font = ' bol d 80px Tahoma ' ;
canvas . fi l l Styl e = ' #000 ' ;
canvas . fi l l Text ( ' HTML5 Canvas ' , 1 0 , 100) ;
Listing 6 . 1 0 daje efekt pokazany na rysunku 6.6. Zauważ, że w wywołaniach metod fi 1 l Text i stro keText
po ciągu znaków podane są współrzędne x i y miejsca, gdzie tekst zostanie narysowany. W drugim przykładzie
tekst jest dodatkowo wyrównany do środka i wykorzystane są zarówno fi 1 1 Text, jak i strokeText, więc jeśli
chciałbyś otrzymać wyłącznie obrys tekstu, usuń wywołanie metody fi 1 1 Text.
HTM LS Ca nva s
D=Olr[fq] [b� �@) ITUW@)@
Rysunek 6 . 6 . Tekst narysowany n a płótnie
<canvas i d= " canvas" wi dt h = " 34 2 " hei ght= " 25 6 " ><jcanvas>
<scri pt>
var canvas = document . getEl ementByl d ( ' canvas ' ) . getContext ( ' 2d ' ) ;
var canvas lmage = new Image () ;
funct i on drawCanvaslmage () {
canva s . drawlmage (canvas lmage, 43 , O) ;
};
Przycinanie obrazka 149
canvas lmage . addEventLi stener ( ' l oad ' , drawCanvaslmage , fal se) ;
W wersji podstawowej metoda draw lmage pobiera adres URL obrazu, a następnie współrzędne x i y
punktu, w którym obrazek ma być wyświetlony. Obraz nie jest w żaden sposób przeskalowany ani przycięty.
Zdarzenie l oad z listingu 6 . 1 1 służy do sprawdzenia, czy przed próbą wyświetlenia obrazka na płótnie został
on załadowany. Jeśli przekażesz adres URL obrazka bezpośrednio do zmiennych, nic się nie wyświetli,
ponieważ obraz nie będzie jeszcze załadowany na stronie. Aby zatem upewnić się, że obraz został załadowany,
przed próbą jego wyświetlenia nasłuchuj e się zdarzenia załadowania obrazka.
Jak widać, obraz na rysunku 6.7 z trudnością mieści się we wnętrzu ramki płótna. Rozmiar obrazu można
zmienić za pomocą metody drawlmage z uwzględnieniem skalowania szerokości i wysokości:
drawlmage (obrazek , x, y, szerokość, wysokość) ;
W ten sposób skrypt uwzględnia dodatkowo wartości szerokości i wysokości - przykładowo wywołanie
canva s . drawlmage(canvaslmage , 50 , 50 , 150 , 2 1 1 ) ; zmieni rozmiar obrazu na 150 pikseli szerokości i 2 1 1
pikseli wysokości oraz umieści go w punkcie oddalonym o 5 0 pikseli o d góry i o d lewej strony.
funct i on croplmage ( ) {
canvas . drawlmage (canvaslmage,
O, IIpozycja x ramki kadrowania n a źródłowym obrazku
O, IIpozycja y ramki kadrowania n a źródłowym obrazku
168 , II szerokość ramki kadrowania na źródłowym obrazku
2 3 6 , Ilwysokość ramki kadrowania na źródłowym obrazku
1 1 O , IIpozycja x ramki na płótnie
1 1 O , IIpozycja y ramki na płótnie
2 5 0 , II szerokość ramki na płótnie
250 Ilwysokość ramki na płótnie
);
};
canvas lmage . addEventLi stener ( ' l oad ' , c roplmage , fal se) ;
Poza typowym wstawianiem obrazka na płótnie API umożliwia również wykorzystanie obrazka do
wypełnienia danego kształtu. Można to zrealizować dzięki metodzie createPattern. Najpierw przekazywany
jest obrazek, który chcemy wyświetlić, a następnie tworzona jest zmienna wzorca z obrazkiem. Na podstawie
zadanych współrzędnych i rozmiaru rysowany j est kształt. Styl wypełnienia kształtu zostaj e nadany na
podstawie utworzonego wcześniej wzoru, jak pokazano poniżej :
var canvas lmage = new Image () ;
funct i on createlmagePattern () {
var pattern = canvas . createPattern (canvas lrnage , ' repeat ' ) ;
canvas . rect ( O , O , 640, 480) ;
canvas . fi l l Styl e =pattern ;
canvas . fi l l ( ) ;
};
canvas lmage . addEvent Li stener ( ' l oad ' , c reatelmagePattern , fal se) ;
razem, kiedy wywoływana jest metoda drawImage, ramka jest przesuwana do następnej klatki. Po osiągnięciu
ostatniej klatki ramka jest cofana do pierwszej klatki źródłowego obrazka. Aby zrealizować ten przykład,
wykorzystaj następujące kroki oraz listing 6.13:
1. Utwórz stronę z listingu 6.13 ze znacznikami style i body, płótnem oraz przyciskami
do włączania i wyłączania animacji.
2. Dodaj definicje zmiennych globalnych, funkcję init oraz procedurę obsługi zdarzeń
window.addEventHandler.
3. Dodaj funkcję animateSprite, która rysuje przycięte zdjęcie.
4. Dodaj funkcje startAnimation i stopAnimation, powiązane z przyciskami do włączania
i wyłączania animacji.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>6.13. Animacja sprite’a</title>
<style>
#canvas {
/* umieść obramowanie wokół płótna */
border:1px solid #03F;
}
</style>
<script>
// zmienne canvas i context służące do rysowania
var canvas;
var context;
// zmienna animacji
var intervalRef;
// inicjalizacja płótna
function init() {
// ustaw procedury obsługi przycisków
var btnStart = document.getElementById('start');
var btnStop = document.getElementById('stop');
btnStart.addEventListener('click',startAnimation,false);
btnStop.addEventListener('click',stopAnimation,false);
</script>
</head>
<body>
<h1>Animacja mapy sprite’ów na płótnie</h1>
<canvas id="canvas" width="100" height="100">
Element canvas nie jest obsługiwany w tej przeglądarce.
</canvas>
<br>
<button id="start">Włącz</button>
<button id="stop">Wyłącz</button>
</body>
</html>
Jeżeli po wywołaniu funkcji startAnimation użytkownik kliknie przycisk Włącz, funkcja ustawia interwał
pomiędzy uruchomieniami funkcji animateSprite na wartość 100 milisekund. Oto istota taktowania animacji:
dopóki nie zostanie wywołana funkcja stopAnimation, która wyczyści wartość interwału, co 100 milisekund
będzie wywoływana funkcja animateSprite. Za każdym razem, kiedy zostanie ona wywołana, zostanie
wyświetlony odpowiedni kadr obrazka recyklingu. Obrazek recyklingu ma 300 pikseli szerokości i zawiera
trzy kadry o wymiarach 100×100 pikseli. Po każdym kolejnym kadrowaniu ramka jest przesuwana o 100
pikseli w prawo, jak pokazano na rysunku 6.8. Jeśli kadrowanie wypada na ostatnich 100 pikselach, ramka
jest przesuwana z powrotem na 0.
Przekształcenia płótna 153
Zauważ, że czyszczenie płótna przed każdym wywołaniem drawImage nie jest konieczne, gdyż obrazek
sprite’a ma nieprzezroczyste tło, a za każdym razem rysujesz na całym płótnie o wymiarach 100×100 pikseli.
Uwaga
Mimo że element canvas nie obsługuje warstw na samym płótnie, istnieje sposób na odzwierciedlenie
funkcjonalności warstw. Jako że płótno jest elementem, który może być formatowany i ma domyślnie
przezroczyste tło, nic nie powstrzymuje programistów przed definiowaniem wielu elementów canvas
spozycjonowanych jeden na drugim. Aby kontrolować położenie warstw na stronie, należy wykorzystać
na elemencie atrybut stylu z-index. Nietrudno wyobrazić sobie grę, która ma płótno tła, podczas gdy
sprite postaci jest ładowany na mniejszym płótnie umieszczonym na innych płótnach. Jeśli implementujesz
stronę z wieloma płótnami, zwróć uwagę na kwestię wydajności, ponieważ do używania poszczególnych
elementów canvas konieczne jest utrzymywanie odwołań do nich.
Przekształcenia płótna
Oprócz narzędzi i efektów na płótnie dostępny jest zestaw narzędzi transformacyjnych. Oto trzy narzędzia
przekształcania:
rotate(kąt) — obraca leżącą u jego podstaw siatkę kontekstu płótna tak, że wszystko, co jest
dodawane do obróconego kontekstu, użytkownik widzi jako obrócone; kąt jest podawany
w radianach i jest wyznaczany zgodnie z kierunkiem ruchu wskazówek zegara.
scale(x, y) — umożliwia programiście sterowanie skalą jednostek x i y; metoda ta przyjmuje
jako parametry jednostkę skali x i jednostkę skali y.
transform — pozwala programiście na dostęp bezpośrednio do macierzy transformacji.
użycie metody translate w celu przesunięcia pozycji w przeciwnym kierunku. Drugą i preferowaną metodą
jest użycie metod save i restore.
Metody save i restore pozwalają na zapisanie bieżącego kontekstu przed dokonaniem przekształcenia
oraz późniejsze przywrócenie zapisanej wersji kontekstu. Ten sposób wykorzystamy w różnych przepisach
wraz z metodą rotate, żeby zademonstrować transformacje na płótnie. Metoda rotate przyjmuje liczbę
radianów, o jaką ma obrócić kierunek płótna. Aby przekształcić kąt w stopniach na radiany, należy użyć
następującego wzoru:
Dzięki wykorzystaniu takich przekształceń jak metody rotate i scale możesz użyć płótna do utworzenia
ciekawych animacji, jak pokazano w kolejnym przepisie.
Uwaga
Jak w przypadku każdego podstawowego zestawu wywołań API, które nadają się do wykonywania
złożonych i powtarzalnych funkcji, takich jak rysowanie kwadratów czy okręgów, w oparciu o podstawowe
składniki, zostały opracowane rozmaite rozszerzone biblioteki wywołań. Część z nich funkcjonuje
jako samodzielne produkty i może stanowić podstawę do opakowywania funkcji płótna w prostsze
wywołania. Biblioteki te mają zazwyczaj rozbudowaną funkcjonalność, co ułatwia realizację bardziej
złożonych zadań. Niektóre z nich warto przetestować: netron, canvas toolkit, EaselJS, jCanvaScript
czy gury. Jesteśmy zresztą pewni, że wraz ze zwiększaniem się popularności płótna HTML5 wzrastać
będzie także liczba i funkcjonalność bibliotek.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>6.14. Poruszające się koło zębate</title>
<style>
Animowanie obrazka 155
#canvas {
/* zastosuj proste obramowanie do płótna */
border:1px solid #03F;
}
</style>
<script>
// zmienne animacji
var stepCounter; // licznik bieżącego kroku
var stepDegrees; // o ile obrócić w następnym kroku
var stepDistance; // jak daleko przesunąć obrazek w każdym kroku
var stepSpeed; // jak szybko obracać i przesuwać obrazek
var stepsFullRevolution; // ile kroków jest w pełnym obrocie
// zmień położenie
xpos += stepDistance;
</script>
</head>
<body>
<h1>Toczące się koło zębate</h1>
<canvas id="canvas" width="600" height="100">
Element canvas nie jest obsługiwany w tej przeglądarce.
</canvas>
</body>
</html>
Kiedy po załadowaniu obrazu wywoływana jest funkcja moveGear, pierwszym wykonywanym przez kod
działaniem jest wyczyszczenie płótna przez wywołanie metody clearRect. Należy to zrobić, żeby „resztki”
poprzednio narysowanego obrazka nie pozostały na płótnie. Następnie skrypt zapisuje bieżący kontekst,
zwiększa wartość zmiennej położenia w poziomie i za pomocą funkcji translate umieszcza punkt początkowy
w nowym miejscu. W kolejnym kroku w oparciu o nowy punkt początkowy skrypt za pomocą metody rotate
obraca obraz o ustawioną wcześniej wartość stepDegrees. Wreszcie obrazek jest rysowany, a ukierunkowanie
kontekstu przywracane na potrzeby następnej funkcji rysującej.
Po narysowaniu obrazka koła zębatego w nowym miejscu i pod nowym kątem, porównując położenie
w poziomie i szerokość płótna, sprawdzamy, czy obraz został przeniesiony poza prawą krawędź płótna.
Jeżeli tak się stało, animacja zostaje zatrzymana. Jeśli jednak obraz jest nadal widoczny na płótnie, skrypt
zwiększa licznik kroków i określa kolejny czas przerwania, aby ponownie uruchomić funkcję moveGear.
Wszystkie ustawienia animacji można regulować poprzez zmianę zmiennych animacji na początku
skryptu. Rysunek 6.9 przedstawia różne etapy animacji obrazu na płótnie.
Animacja pionowego wykresu słupkowego 157
Rysunek 6.9. Kadry animacji obrazka sprite’a w miarę wyświetlania w każdym cyklu
Uwaga
W interakcje z płótnem — czy to grając w gry, czy przesuwając kształty — możesz wejść poprzez
przechwytywanie zdarzeń myszy, takich jak mouseover i click. Współrzędne podane w zdarzeniu
mogą być odwzorowywane przez przesunięcie współrzędnych x i y w stosunku do pozycji płótna na
stronie. Jeśli na przykład (x, y) ze zdarzenia kliknięcia myszy to (150, 200), a przesunięcie płótna
to 100 dla x i 100 dla y, wtedy współrzędne kliknięcia myszką na płótnie to (50, 100). Znając te
współrzędne na siatce płótna, możesz ustalić, czy kliknięcie miało miejsce na konkretnym kształcie
narysowanym na płótnie.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>6.15. Rosnący wykres słupkowy</title>
158 Rozdział 6. Rysowanie na płótnie
<style>
#graph {
/* obramuj płótno */
border:1px solid #03F;
}
</style>
<script>
// ustawienia wykresu
var chartMargin;
var chartAxisSpace;
var chartWidth;
var chartHeight;
function initSettings() {
// ustawienia wykresu
chartMargin = 20; // margines wokół całego płótna
chartAxisSpace = 50; // obszar na osie x i y
Animacja pionowego wykresu słupkowego 159
function drawAxis() {
// ustaw grubość linii odpowiadającą szerokości osi
context.lineWidth = 2;
// deklaruj x, y i h słupka
// szerokość słupka została określona wcześniej
var barStartX = 0;
var barStartY = 0;
var barHeight = 0;
</script>
</head>
<body>
<h1>Rosnący wykres słupkowy</h1>
<canvas id="graph" width="600" height="400">
Ta przeglądarka nie obsługuje elementu canvas.
</canvas>
</body>
</html>
Po załadowaniu strony zostanie wywołana funkcja initGraph, która z kolei wywoła funkcję initSettings,
a ta zainicjalizuje wszystkie ustawienia wykresu. Funkcja initGraph wywoła następnie funkcję drawAxis.
Funkcja drawAxis narysuje dwie linie, będące osiami x i y, a następnie w oparciu o liczbę oznaczeń określoną
162 Rozdział 6. Rysowanie na płótnie
podczas inicjalizacji doda oznaczenia danych do osi y. W przypadku osi x dla każdego słupka zostanie
wykorzystana właściwość title danych JSON wykresu — zauważ, że etykieta każdego słupka jest wyrównywana
względem jego środka. Wreszcie funkcja doda tytuł osi x oraz tytuł osi y, który jest obrócony o 90 stopni
w kierunku przeciwnym do kierunku ruchu wskazówek zegara.
Funkcja initGraph wywoła następnie funkcję growBars. Funkcja growBars jest sercem tej prostej animacji
i zwiększa krok animacji w celu określenia na podstawie wartości końcowej słupka z chartData, o ile procent
powiększyć każdy słupek. Innymi słowy, każdy słupek jest proporcjonalnie zwiększany za każdym razem,
kiedy wykonywana jest funkcja growBars. Po obliczeniu nowych wymiarów każdego słupka funkcja growBars
wywoła pomocniczą funkcję drawBar z zadanymi wymiarami słupka, aby faktycznie narysować słupek na
płótnie. Kiedy już funkcja growBars przejdzie przez wszystkie słupki wykresu, sprawdzi, czy krok ten był
ostatnim krokiem w animacji. Jeśli kroków jest więcej, funkcja ustawia czas ponownego wywołania funkcji
growBars po inkrementacji indeksu kroku. Rysunek 6.11 pokazuje, w jaki sposób wykres słupkowy rośnie
w kolejnych kadrach animacji.
Aby zmienić prędkość albo tempo wzrostu słupków, wystarczy zmodyfikować zmienne numSteps
i growSpeed w funkcji initSettings.
Wskazówka
Jak widzisz, tworzenie animowanych, a nawet interaktywnych wykresów za pomocą płótna HTML5
jest całkiem proste. Jeśli chcesz poznać nieograniczone możliwości wykorzystania płótna HTML5 do
tworzenia wykresów — od podstawowych wykresów liniowych po skomplikowane wykresy w kształcie
róży, możesz się zapoznać z biblioteką wykresów RGraph (http://www.rgraph.net/). Twórcy biblioteki
wykonali ciężką pracę, a biblioteka wykorzystuje potencjał płótna w powiązaniu z JavaScriptem. Dopóki
postępujesz zgodnie z umową licencyjną, jeśli tylko chcesz, możesz ją włączać do własnych projektów.
Podsumowanie
W tym rozdziale poznałeś Canvas API. API to ma w kwestii rysowania do zaoferowania o wiele więcej, niż
zostało opisane. Jednak znając podstawy, dysponujesz narzędziami do zgłębiania bardziej zaawansowanych
metod i zagadnień Canvas API. Zachęcamy Cię do wykorzystywania jego pełnych możliwości oraz wykazania
się własną artystyczną kreatywnością.
7
Osadzanie wideo w HTML5
J esteśmy pewni, że jeśli ostatnich kilku lat nie spędziłeś w pustelni, korzystałeś z witryn takich jak YouTube
i Vimeo, innymi słowy, z witryn z osadzoną na stronie zawartością wideo. Chociaż wyżej wymienione
serwisy są najpopularniejszymi witrynami do udostępniania wideo, umieszczaliśmy je online w ten czy inny
sposób od jakichś dziesięciu lat. Problemem było to, że wobec braku standardu prezentacji wideo musieliśmy
używać wtyczek, takich jak Flash, lub używać elementu object do odtwarzania plików QuickTime.
Możesz uważać, że problem nie istnieje, ponieważ powyższe rozwiązania na ogół dobrze się sprawdzają.
Problemem jest jednak to, że przy udostępnianiu filmów nie należy polegać na zewnętrznych wtyczkach.
W HTML5 znajduje się nowy, ustandaryzowany sposób natywnego renderowania wideo na stronie
internetowej — możesz użyć elementu video.
W tym rozdziale omówimy podstawowe zagadnienia związane z elementem video, a następnie zajmiemy
się niektórymi kodekami oraz, tradycyjnie, pewnymi problemami związanymi z ich odmiennym działaniem
w różnych przeglądarkach. Dowiesz się także, jak poprawić przystępność wideo za pomocą napisów i podpisów,
a na końcu użyjesz nowego API mediów do utworzenia własnego odtwarzacza wideo i kontrolek.
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>7.1. Prosty sposób użycia elementu video</title>
</head>
<body>
<video src="mojfilm.mp4"></video>
<!--
video to samozamykający się element, więc może on być również użyty w sposób następujący
164 Rozdział 7. Osadzanie wideo w HTML5
Proste, prawda? Na podstawowym poziomie to wszystko, czego potrzebujesz, aby użyć elementu video
w HTML5. Niestety, w rzeczywistości nie jest to takie proste. Teraz, kiedy zaostrzyliśmy Twój apetyt,
omówimy kilka kwestii, o których powinieneś wiedzieć, zanim zaczniesz korzystać z video.
Android 2.1+
Chrome 10.0+
Firefox 3.6+
Internet Explorer 9.0+
iOS Safari 3.2+
Opera 10.6+
Safari 4.0+
Jak widać, z jednej strony element video jest powszechnie obsługiwany w aktualnych wersjach przeglądarek,
ale z drugiej strony nie jest natywnie obsługiwany przez Internet Explorer w wersjach 6, 7 i 8, a te przeglądarki
mają ogromny udział w rynku. W dalszej części rozdziału pokażemy, jak opracować zastępczą treść dla
przeglądarek i urządzeń, które jeszcze nie obsługują elementu video. Zanim jednak do tego dojdziemy,
powinieneś się zapoznać z różnymi rodzajami kodeków wideo.
Internet
Kodek Android Chrome Firefox iOS Safari Opera Safari
Explorer
H.264 - 13+ - 9+ 4+ - 5+
Ogg Theora - 13+ 5+ - - 11+ -
WebM 2.3 13+ 5+ * - 11+ -
* poprzez wtyczkę: http://www.webmproject.org/ie/
Dołączanie filmów za pomocą elementu video 165
Początkowo w specyfikacji HTML5 dążono do tego, aby wszystkie przeglądarki obsługiwały kodeki Ogg
Theora (Ogg Theora jest kodekiem wideo, Ogg Vorbis — audio). Jednak wobec protestów firm Apple i Nokia
kodeki te zostały usunięte ze specyfikacji, by nigdy do niej nie powrócić. Nie umieszczono w niej również
żadnych innych sugestii odnośnie do kodeków.
Obecnie kodek Ogg Theora obsługują natywnie przeglądarki Chrome i Firefox. Safari posiada natomiast
natywne wsparcie jedynie dla kodeka H.264, który jest również obsługiwany przez Chrome oraz urządzenia
iPhone i iPad.
W maju 2010 roku firma Google poinformowała o pracach nad innym formatem multimedialnym,
projektem WebM (znanym również jako VP8). Jest to kodek typu open source, a jego obsługa została
szybko zaimplementowana w przeglądarkach Chrome, Android, Firefox i Opera.
Z kolei Internet Explorer 9 wspiera natywnie kodek H.264, a WebM będzie obsługiwał tylko za
pośrednictwem dodatkowo instalowanego kodeka, co nie jest idealnym rozwiązaniem.
Wskazówka
Świetnie, dowiedziałeś się o różnych formatach, ale jak możesz je utworzyć? Cóż, popularne pakiety
oprogramowania wideo nie eksportują obecnie ani do Ogg, ani do WebM, ale wyeksportują plik H.264.
iPhone eksportuje plik .mov, a większość kamer cyfrowych wyeksportuje filmy do plików .mov albo .avi.
Dostępne jest oprogramowanie do konwersji plików wideo, w tym Firefogg — rozszerzenie do Firefoksa,
które koduje wideo Ogg, albo Handbrake do kodowania wideo H.264. My polecamy MiroVideoConverter
(http://www.mirovideoconverter.com/) — bezpłatny, łatwy w użyciu program, który konwertuje filmy
na kilka różnych typów wideo.
166 Rozdział 7. Osadzanie wideo w HTML5
Uwaga
W idealnym świecie przeglądarki i urządzenia wybierałyby po prostu pierwszy plik, który mogłyby
odtworzyć. Tak zwykle dzieje się w najpopularniejszych przeglądarkach w komputerach stacjonarnych
i na laptopach, ale błąd przeglądarki iPada uniemożliwia jej załadowanie czegokolwiek oprócz pierwszego
źródła wideo. Dlatego właśnie plik .mp4 należy zadeklarować jako pierwszy, jak na listingu 7.2.
Element video posiada atrybuty height i width, choć w celu ustawienia jego wysokości i szerokości
możesz także użyć CSS-a. Posiada również atrybut controls, który wyświetla domyślne kontrolki wideo.
Na listingu 7.2 w obrębie elementu video znajdują się trzy elementy source. Każdy z nich wskazuje na
pojedyncze wideo i zawiera atrybut type, który mówi przeglądarce, jakiego typu plik jest dostarczany.
Jeśli przeglądarka nie rozpoznaje typu, nie będzie pobierać pliku, dzięki czemu nie będzie niepotrzebnie
wykorzystywać zasobów sieci, a strona będzie się szybciej ładować. Przeglądarka wybierze pierwszy plik,
który będzie w stanie odtworzyć.
Kod z listingu 7.2 zapewnia wyświetlenie wideo w przeglądarkach Chrome, Firefox, Internet Explorer 9
i Safari oraz na iPadzie, iPhonie i urządzeniach z systemem Android, a wszystko to bez potrzeby instalowania
zewnętrznych komponentów, co jest świetną wiadomością. Ale co można zaoferować użytkownikom
korzystającym z przeglądarek Internet Explorer 6, 7 i 8, które nie obsługują w tej chwili znacznika video?
Możesz też być bardziej pomocny i zaoferować bezpośrednie pobieranie filmu wideo:
<a href="video.mp4">Pobierz nasz film w formacie MP4</a>
<a href="video.webm">Pobierz nasz film w formacie WebM</a>
<a href="video.ogv">Pobierz nasz film w formacie Ogg</a>
Złożenie tego wszystkiego razem daje Ci kod z listingu 7.4, który udostępnia wideo we wszystkich
najpopularniejszych aktualnie przeglądarkach — czy to za pośrednictwem natywnego elementu video,
czy też awaryjnie za pomocą treści Flash.
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>7.4. Wideo z treścią zastępczą działające w różnych przeglądarkach</title>
</head>
<body>
<video width="640" height="480" controls>
<!-- wideo dla Safari i IE9; ze względu na iPada MP4 musi być pierwszym formatem -->
<source src="cablecar.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' />
<!-- wideo dla Chrome, Firefoksa i Opery -->
<source src="cablecar.webm" type="video/webm" />
<source src="cablecar.ogv" type='video/ogg; codecs="theora, vorbis"' />
Nie musisz wybierać serwisu YouTube jako witryny przechowującej wideo; plik możesz przechowywać
samodzielnie, a do jego odtwarzania używać własnego odtwarzacza Flash. Mógłbyś zaprojektować
odtwarzacz samodzielnie albo użyć jednego z wielu dostępnych w internecie, takich jak popularny, łatwy
do skonfigurowania JW Player, jak pokazano na listingu 7.5.
<!-- zastępcza treść dla starszych przeglądarek o mniejszych możliwościach, wykorzystująca JW Playera -->
<script type='text/javascript' src='swfobject.js'></script>
<div id='mediaplayer'></div>
<script type="text/javascript">
var so = new SWFObject('player.swf','playerID','480','270','9');
so.addParam('allowfullscreen','true');
so.addParam('allowscriptaccess','always');
so.addVariable('file', 'video.mp4');
so.write('mediaplayer');
</script>
</video>
Wskazówka
Jednym z problemów, które mogą się pojawić w trakcie tworzenia treści wideo w HTML5, jest kwestia
typów MIME. Przy opracowywaniu przykładów do tej książki natknęliśmy się na ten problem po
załadowaniu plików .ogv i .webm na serwer WWW, kiedy próbowaliśmy odtworzyć je w Firefoksie.
Filmy odtwarzały się poprawnie na naszym lokalnym komputerze, a także w Operze i Chrome po
umieszczeniu ich na serwerze WWW (Apache), natomiast nie działały w Firefoksie. Wynika to z faktu,
że Firefox musi wiedzieć, iż serwer obsługuje określony typ MIME. Nie wszystkie serwery obsługują
nowe formaty wideo, a żeby to robiły, musisz dokonać odpowiednich zmian konfiguracyjnych, takich jak
dodanie następujących wierszy w pliku .htaccess albo httpd.conf, w zależności od platformy serwera:
AddType video/mp4 .mp4
AddType video/ogg .ogv
AddType video/webm .webm
Mamy już teraz kompletny kod umożliwiający obsługę wideo we wszystkich najpopularniejszych
przeglądarkach i urządzeniach. Dostępnych jest jednak kilka dodatkowych nowych atrybutów.
Włączanie wideo we wszystkich przeglądarkach 169
Atrybut src
Listing 7.1 z początku tego rozdziału pokazuje bardzo prosty przykład elementu video zawierającego atrybut
src. Użycie src oznacza jednak, że jesteś ograniczony do jednego pliku wideo, więc wobec problemów
z kodekami i przeglądarkami atrybut ten jest obecnie w zasadzie bezużyteczny. Na potrzeby odtwarzania
filmów w różnych przeglądarkach, zamiast korzystać z atrybutu src, rozważ umieszczenie w obrębie video
elementów source, tak jak na listingu 7.4.
Atrybut poster
Atrybut poster jest używany do wyświetlenia w miejscu wideo pojedynczego obrazka, w czasie gdy jest ono
pobierane albo po prostu podczas oczekiwania na jego rozpoczęcie. Obrazek poster ma pozwolić widzowi
zorientować się, co to za film wideo albo o czym on jest. Obrazek może być plikiem .gif, .jpg albo .png (choć
przezroczysty plik PNG nie przesłoni wideo).
Jeśli atrybut poster nie jest określony, przeglądarka wyświetla po prostu pierwszą klatkę filmu, co może
w zupełności odpowiadać Twoim potrzebom. Jeśli jednak tak nie jest, ustaw obrazek poster w następujący
sposób:
<video width="640" height="480" poster="poster.gif">
Warto śledzić rozwój sytuacji w kwestii konieczności umieszczenia dla obrazka poster tekstu
alternatywnego alt. Nic nie jest jeszcze przesądzone, ale istnieją pewne zastrzeżenia dotyczące obsługi,
więc atrybut poster może w przyszłości ulec zmianie.
Atrybut preload
Jeśli na stronie umieściłeś względnie duży plik wideo lub też jesteś pewien, że użytkownik będzie chciał
obejrzeć osadzony przez Ciebie na stronie filmik (tak jak w przypadku strony YouTube), możesz nakazać
przeglądarce rozpoczęcie pobierania pliku wideo natychmiast po wczytaniu strony. Atrybut preload może
przyjąć trzy możliwe wartości:
preload="auto"
Wartość auto (możesz też po prostu umieścić samo preload) informuje przeglądarkę, aby pobrała film
po załadowaniu strony.
preload="none"
Wartość preload="none" mówi przeglądarce, aby nie pobierała filmu. Dopiero kiedy użytkownik uruchomi
wideo, przeglądarka zacznie je pobierać.
preload="metadata"
Atrybut metadata pobiera informacje (metadane) o wideo, w tym jego czas trwania, pierwszy kadr, wymiary
i listę odtwarzania. Przeglądarka nie powinna pobrać samego wideo, dopóki użytkownik go nie uruchomi.
W zamyśle po użyciu tego atrybutu film zostałby domyślnie wyciszony, a użytkownik musiałby zwiększyć
poziom głośności. Być może w przyszłości na potrzeby regulacji domyślnej głośności dźwięku będą dostępne
inne wartości, takie jak audio="2" albo audio="low". W jednym z kolejnych przepisów dowiesz się, jak można
zmienić głośność za pomocą JavaScriptu.
Atrybut loop
Jeśli element video posiada atrybut loop, po zakończeniu odtwarzania rozpocznie się ono od początku.
Atrybut ten jest atrybutem logicznym — albo jest włączony, albo wyłączony.
Atrybut autoplay
Atrybut logiczny autoplay wymusza na przeglądarce rozpoczęcie pobierania i odtwarzania wideo po
wczytaniu strony. W ten sposób działają na przykład strony YouTube i wiele odtwarzanych automatycznie
reklam wideo. Istnieją pewne obawy związane z dostępnością i łatwością wykorzystania odtwarzanych
Włączanie wideo we wszystkich przeglądarkach 171
automatycznie filmów wideo, nie wspominając już o tym, jak filmy te mogą być irytujące, ale atrybut
autoplay znajduje się w specyfikacji ze względu na to, że pozwala w ustandaryzowany sposób wymusić
automatyczne odtwarzanie wideo, gdybyś naprawdę tego potrzebował. Nie musisz się uciekać do różnych
javascriptowych sztuczek, aby osiągnąć ten efekt. Zauważ, że dostępne są też różne rozszerzenia przeglądarek
wyłączające autoplay.
Uwaga
W systemie iOS atrybuty autoplay i preload zostały celowo wyłączone ze względu na potencjalne
koszty i problemy związane z prędkością wczytywania u użytkownika. Żadne dane nie są pobierane,
dopóki użytkownik nie zdecyduje się ręcznie uruchomić filmu. Istnieje kilka technik (a raczej sztuczek)
pozwalających na obejście tej blokady, ponieważ jednak są to techniki nieoficjalne, nie powinny być
wykorzystywane i nie zostały omówione w tej książce.
Atrybut controls
Atrybut controls dodaje do wideo domyślne, właściwe dla danej przeglądarki kontrolki zawierające przycisk
odtwarzania/pauzy, pasek przewijania, informację o czasie trwania i czasie od początku filmu oraz kontrolki
sterowania głośnością.
Jest to atrybut logiczny, więc albo jest dołączony, albo nie. Oto przykład:
<video height="300" width="300" controls>
…
</video>
Jeśli element video nie zawiera atrybutu controls, zostanie wyświetlony pierwszy kadr filmu lub obrazek
poster. Użytkownik będzie musiał kliknąć film prawym przyciskiem myszy i skorzystać z listy opcji. Lepiej
jest zatem umieścić kontrolki za pomocą atrybutu controls. Jeżeli chcesz, możesz wykorzystać swoje własne
kontrolki — o tym podyskutujemy w dalszej części tego rozdziału. Rysunek 7.2 przedstawia domyślne kontrolki
w głównych przeglądarkach.
Uwaga
Choć to dziwne, jeśli w Firefoksie nie masz włączonej obsługi JavaScriptu, kontrolki nie są wyświetlane.
Inne przeglądarki wyświetlają kontrolki poprawnie bez pomocy JavaScriptu.
172 Rozdział 7. Osadzanie wideo w HTML5
Element track jest nowym dodatkiem do specyfikacji HTML5. Standardem dla napisów i podpisów był
pierwotnie WebSRT, ale jego rolę ma przejąć Web Video Text Track (WebVTT). Ponieważ technologia ta
jest nowa i wciąż wymaga ogromu pracy, nie pojawiły się jeszcze jej działające przykłady ani implementacje
w przeglądarkach. Możesz jednak wyświetlać napisy za pomocą JavaScriptu oraz pliku WebVTT (.vtt).
Listing 7.6 przedstawia prosty przykład z hipotetycznymi danymi z pliku WebVTT (w oparciu o obecną
specyfikację). Możesz utworzyć i edytować pliki .vtt, używając prostego edytora tekstu, takiego jak Notatnik.
Plik .vtt zawiera identyfikator, czas, w jakim ma być wyświetlana treść, a następnie samą treść, która powinna
zostać wyświetlona.
0
00:00:0,000 --> 00:00:2,000
To jest pierwszy fragment napisów
1
00:00:3,000 --> 00:00:5,000
To jest drugi fragment
2
00:00:7,000 --> 00:00:15,000
Jak mogłeś zgadnąć, to jest trzeci fragment napisów
Ponieważ żadna przeglądarka nie obsługuje jeszcze natywnie elementu track, do wykonania tego zadania
potrzebujesz javascriptowej wtyczki. Na listingu 7.7 zastosowano dostępną na stronie https://github.com/
icelab/jquery-videosub wtyczkę jQuery o nazwie VideoSub, która jest oparta o oryginalny skrypt MooTools
Thomasa Sturma (http://www.storiesinflight.com/js_videosub/). Wtyczka opakowuje video w element div,
Tworzenie wideo z napisami 173
a następnie dodaje kolejny div, zawierający napisy. W tym przykładzie domyślnie ukryjesz napisy i dodasz
łącze pozwalające je wyświetlić. Rysunek 7.3 przedstawia przykład działania napisów. Kompletny kod znajduje
się na listingu 7.7.
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>7.7. Napisy wideo</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script src="jquery.videosub.js"></script>
<script>
$(function(){
$('video').videoSub({
containerClass : 'videosub-container',
barClass : 'videosub-bar',
useBarDefaultStyle : false
});
$('.videosub-bar').hide();
$('#subtitlestoggle').click(function() {
$(this).text($(this).text() == 'Pokaż napisy' ? 'Ukryj napisy' : 'Pokaż napisy');
$('.videosub-bar').toggle();
});
});
</script>
<style>
body {
font-family: arial, Arial, Helvetica, sans-serif;
}
.videosub-container {
width: 640px;
}
174 Rozdział 7. Osadzanie wideo w HTML5
.videosub-bar {
background: black;
bottom: 40px;
color: yellow;
font-size: 1.3em;
font-weight: bold;
padding: 10px 20px;
position: absolute;
text-align: center;
width: 560px;
}
a#subtitlestoggle {
background: black;
color: yellow;
display: block;
font-weight: bold;
padding: 10px;
position: absolute;
right: 0;
text-decoration: none;
top: 0;
}
a#subtitlestoggle:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<video width="640" height="480" controls>
<!-- wideo dla Safari i IE9; ze względu na iPada MP4 musi być pierwszym formatem -->
<source src="cablecar.mp4" type="video/mp4" />
</body>
</html>
1
00:00:0,000 --> 00:00:2,000
<b>To jest</b> <i>pierwszy fragment</i> <u>napisów</u>
Możesz również zastosować klasę CSS, gdybyś chciał dołączyć inne formatowanie — na przykład użyć
niestandardowej czcionki. Aby to zrobić, należy dodać wewnętrzny znacznik c z jakąś klasą CSS:
1
00:00:0,000 --> 00:00:2,000
<c.myclassname>To jest</c> pierwszy fragment napisów
Dostępnych jest więcej możliwości, takich jak położenie czy wielkość tekstu. Aby znaleźć więcej informacji
na temat tego rozwijającego się standardu, sprawdź witrynę http://www.delphiki.com/webvtt/.
API mediów
HTML5 udostępnia rozwinięte i ekscytujące API dla multimediów. Korzystając z tego API i JavaScriptu,
możesz wpływać na działanie wideo na stronach internetowych. Poniżej znajduje się lista dostępnych
zdarzeń API:
abort loadstart
canplay pause
canplaythrough play
canshowcurrentframe playing
dataunavailable progress
durationchange ratechange
emptied seeked
empty seeking
ended suspend
error timeupdate
loadeddata volumechange
loadedmetadata waiting
Nie wszystkie z powyższych zdarzeń i właściwości są już dostępne, ale większości, w tym tych istotnych,
które pozwalają tworzyć własne kontrolki odtwarzacza wideo, można już używać. Nie będziemy tu wyjaśniać
wszystkich możliwości API — ograniczymy się tylko do potrzebnych do utworzenia własnego odtwarzacza;
176 Rozdział 7. Osadzanie wideo w HTML5
jeśli jednak jesteś zainteresowany zdobyciem większej wiedzy na temat tego API, polecamy materiały na stronie
http://www.w3.org/2010/05/video/mediaevents.html oraz bardzo szczegółową specyfikację pod adresem
http://www.w3.org/TR/html5/media-elements.html#mediaevents.
Podstawową funkcją potrzebną w przypadku własnych kontrolek jest przycisk Odtwórz, ale za pomocą
JavaScriptu i API mediów możesz zrobić znacznie więcej, co ilustruje kod z listingu 7.8.
1. Możesz opakować element video elementem <div id="video-wrapper"> i dodać <div
id="controls">, który będzie zawierał przyciski, suwaki i inne komponenty przydatne do
wyświetlenia kontrolek. Uwzględniliśmy ten fragment na początku sekcji body strony z tego
przepisu, aby był on bardziej przejrzysty, ale w praktyce powinieneś unikać umieszczania
kontrolek w zasadniczym kodzie, a zamiast tego tworzyć je „w locie” za pomocą JavaScriptu,
tak by widzieli je tylko użytkownicy z włączonym JavaScriptem.
2. Możesz zadeklarować element video jako obiekt, do którego można się odwoływać, a następnie
usunąć domyślne kontrolki przeglądarki przez pozbycie się atrybutu controls umieszczonego na
potrzeby użytkowników bez JavaScriptu. Przycisk Odtwórz pozostanie nieaktywny do momentu,
kiedy film będzie gotowy do odtwarzania.
Tworzenie niestandardowych kontrolek 177
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>7.8. Niestandardowe kontrolki wideo</title>
<style>
body {
font: bold .8em Arial, Helvetica, sans-serif;
}
video {
display: block;
}
#video-wrapper {
-moz-box-shadow: 0 0 20px rgba(0, 0, 0, .8);
-webkit-box-shadow: 0 0 20px rgba(0, 0, 0, .8);
box-shadow: 0 0 20px rgba(0, 0, 0, .8);
display: block;
margin: 20px auto;
overflow: hidden;
position: relative;
width: 568px;
}
#controls {
background: rgba(0, 0, 0, .3);
bottom: 0;
height: 30px;
left: 0;
padding: 35px 10px 10px;
position: absolute;
width: 548px;
z-index: 1;
}
button {
background: rgba(255, 255, 255, .7);
border: none; -moz-border-radius: 15px;
-webkit-border-radius: 15px;
border-radius: 15px;
cursor: pointer;
padding: 5px
}
#play {
width: 70px;
}
#time, #duration {
color: #fff;
position: absolute;
top: 0;
}
#time {
left: 10px;
178 Rozdział 7. Osadzanie wideo w HTML5
}
#duration {
right: 10px;
text-align: right
}
input[type="range"] {
position: absolute;
}
#seekbar {
top: 8px;
width: 465px;
left: 50px;
}
#volume {
width: 50px;
}
#mute {
float: right;
width: 60px;
}
label[for="volume"] {
color: #fff;
float: right;
margin: 5px 55px 0 15px;
}
button.speed {
font-size: .8em
}
</style>
<script>
var video = null;
var seekbar = null;
var playBtn = null;
// inicjalizuj stronę
function init() {
// odwołania do przycisków
playBtn = document.getElementById('play');
muteBtn = document.getElementById('mute');
rewindBtn = document.getElementById('rewind');
ffBtn = document.getElementById('ff');
fullscreenBtn = document.getElementById('fullscreen');
ffBtn.addEventListener('click',fastforward,false);
fullscreenBtn.addEventListener('click',fullscreen,false);
playBtn.disabled = false;
seekbar.value = 0;
}
video.addEventListener('ratechange', function() {
//mógłbyś alternatywnie wyświetlić aktualną prędkość odtwarzania
//console.log(video.playbackRate);
}, false);
video.addEventListener('play', function() {
playBtn.innerHTML = 'Wstrzymaj';
}, false);
video.addEventListener('pause', function() {
playBtn.innerHTML = 'Odtwórz';
}, false);
function playPause() {
if (ifPlaying()) {
video.pause();
playBtn.innerHTML = 'Odtwórz';
} else {
video.play();
playBtn.innerHTML = 'Wstrzymaj';
}
};
video.muted = true;
muteBtn.innerHTML = 'Dźwięk';
}
else {
video.muted = false;
muteBtn.innerHTML = 'Wycisz';
}
}
//zmień głośność
function changeVolume() {
video.volume = volume.value;
}
</script>
</head>
<body>
Tworzenie niestandardowych kontrolek 181
<div id="video-wrapper">
<div id="controls">
<button id="play">Odtwórz</button>
<p id="time">0:00</p>
<p id="duration">0:00</p>
<label for="volume"> Głośność:<input id="volume" name="volume" type="range" min="0" max="1"
´step="0.1" required /></label>
<button id="mute">Wycisz</button>
<input id="seekbar" name="seekbar" type="range" min="0" max="1" step="0.1" required />
<button id="rewind" class="speed">Do tyłu</button>
<button id="ff" class="speed">Do przodu</button>
<button id="fullscreen">Pełny ekran</button>
</div>
<!-- wideo dla Safari i IE9; ze względu na iPada MP4 musi być pierwszym formatem -->
<source src="cablecar.mp4" type="video/mp4" />
</video>
</div>
</body>
</html>
Na początku kodu umieściliśmy trochę CSS-a dotyczącego pewnych podstawowych stylów i rozmieszczenia
przycisków. W tym przykładzie nie przesadzaliśmy z obstylowaniem, ale za pomocą CSS-a i API JavaScriptu
mógłbyś stworzyć właściwie każdy układ odtwarzacza wideo, jaki byś tylko chciał.
Przejdźmy teraz do JavaScriptu. Najpierw należy powiązać zmienne z niektórymi elementami: samym
wideo, paskiem przewijania i przyciskiem Odtwórz. Dla wygody użytkowników bez obsługi JavaScriptu
dołączyliśmy do elementu video atrybut controls, który następnie za pomocą JavaScriptu usuwamy, gdyż
nie chcemy, żeby domyślne kontrolki były widoczne.
W kolejnym fragmencie kodu mamy do czynienia z bardzo istotnym procesem. Zanim będzie można
rozpocząć odtwarzanie filmu, przeglądarka musi zdobyć wystarczającą ilość związanych z nim informacji.
Niektóre przeglądarki nie potrafią na przykład pozyskać długości filmu, ponieważ nadal pobierają jego części.
Zatem w przypadku dużego pliku wideo będziesz być może musiał trochę poczekać, zanim przeglądarka
będzie gotowa do jego odtworzenia. W niniejszym przepisie wykrywamy wartość readyState, która może
przyjąć jedną z pięciu wartości:
have_nothing (0),
have_metadata (1),
have_current_data (2),
have_future_data (3),
have_enough_data (4).
Wartość have_nothing oznacza, że przeglądarka nie posiada żadnych informacji na temat filmu. Pozostałe
wartości wskazują, że przeglądarka posiada jakieś informacje na jego temat, w tym czas trwania, wysokość
i szerokość, aktualną pozycję oraz następną ramkę. W bieżącym przepisie oczekujemy momentu, w którym
182 Rozdział 7. Osadzanie wideo w HTML5
wartość readyState będzie większa od 0; kiedy tak się stanie, czas trwania zostanie zwrócony, a jego wartość
na ekranie — zaktualizowana. Aktywowany zostaje również przycisk Odtwórz, dzięki czemu użytkownik
może uruchomić film. W niniejszym przepisie, kiedy film jest gotowy, niewiele się już w kodzie dzieje, ale
mógłby to być moment, w którym tworzone są kontrolki wideo, pokazywane lub ukrywane ładowanie
grafiki, a może nawet wyświetlany film.
Alternatywnie mógłbyś użyć obiektu nasłuchującego zdarzenia loadeddata, które nastąpi, gdy przeglądarka
będzie miała na tyle dużo informacji na temat filmu, aby umożliwić rozpoczęcie odtwarzania:
video.addEventListener("loadeddata", function(){//zrób, co trzeba}, false);
Znasz już teraz czas trwania filmu, więc przy użyciu video.duration możesz uzyskać jego całkowitą
długość, którą przypiszesz do paska przewijania input[type="range"], tak aby miał on punkt końcowy.
Następnie należy umieścić punkt początkowy suwaka na początku (zero sekund). Niektóre zdarzenia
addEventListener są wykorzystywane do wykrycia, czy czas odtwarzania filmu wideo został zaktualizowany
(timeupdate), a jeśli tak się stało, aktualizowane są ekran odtwarzania i pasek przewijania.
Nawet jeśli usunąłeś domyślne kontrolki wideo i utworzyłeś własne przyciski, także Odtwórz i Wycisz,
użytkownik może, w zależności od przeglądarki, uzyskać po kliknięciu wideo prawym przyciskiem myszy
dostęp do podstawowych opcji: odtwarzania i zatrzymania, pokazania kontrolek, wyciszenia, zapisu wideo
i innych. Tych domyślnych ustawień przeglądarki nie można usunąć ani zmienić, ale odpowiednie zdarzenia
można śledzić — w przepisie znajduje się fragment kodu, w którym pobierana jest informacja o rozpoczęciu
lub zatrzymaniu odtwarzania, a w odpowiedzi zostaje zmieniony tekst przycisku.
Aby zmienić tekst przycisku Odtwórz, warto potwierdzić moment zakończenia filmu — to o tyle przydatna
informacja, że wraz z zakończeniem filmu mogłyby mieć miejsce kolejne działania, takie jak rozpoczęcie
kolejnego filmu z listy odtwarzania. Zakończenie filmu możesz potwierdzić przez wykrycie przy użyciu obiektu
nasłuchującego zdarzenia ended:
video.addEventListener("ended", function(){//zrób, co trzeba}, false);
W wywołaniach addEventListener znajdują się różne funkcje używane do wywołania innych funkcji wideo,
takich jak sprawdzenie, czy wideo jest odtwarzane, ustawienie głośności (video.volume = volume.value),
odtwarzanie wideo w trybie pełnego ekranu (video.webkitEnterFullscreen() — działa tylko w przeglądarkach
WebKit) czy sprawdzenie tempa odtwarzania (playbackRate). Została również dodana funkcjonalność
odtwarzania, aby użytkownik mógł sterować pozycją i kierunkiem wideo — dzięki paskowi przewijania
użytkownik może przejść do dowolnego miejsca na osi czasu (video.currentTime = seekbar.value), dostępne
są również przyciski przewijania do przodu i do tyłu.
Przeglądarki mają jednak swoje dziwactwa, jeśli chodzi o pracę z niestandardowymi kontrolkami:
W chwili pisania tej książki jedynym sposobem, aby wyświetlić film na pełnym ekranie, było użycie
funkcji webkitEnterFullscreen, która działała wyłącznie w przeglądarce Safari. Sądząc po nazwie,
nie jest to jeszcze standard odtwarzania filmów na pełnym ekranie i taki standard może się nigdy
nie pojawić w specyfikacji. Producenci przeglądarek wiedzą jednak, czego pragną użytkownicy,
więc jest bardzo prawdopodobne, że w przyszłości pojawią się pewne niestandardowe rozwiązania
właściwe dla poszczególnych przeglądarek. Istnieją także inne sposoby odtwarzania wideo na pełnym
ekranie, wykorzystujące kombinację JavaScriptu i CSS-a, a niektóre przeglądarki dodatkowo
umożliwiają przejście do trybu pełnoekranowego po kliknięciu prawym przyciskiem myszy samego
filmu wideo.
Podsumowanie 183
Pasek przewijania <input type="range"> obsługują obecnie tylko Safari, Chrome i Opera, przy
czym tylko w Operze może on posiadać przezroczyste tło — w Chrome prezentuje się raczej
brzydko, a w Safari po prostu nie jest widoczny. Firefox i IE9 wyświetlają domyślnie pole tekstowe,
w którym jest prezentowany aktualizujący się czas.
Przycisk przewijania do przodu w IE9 nie działa płynnie, a w Firefoksie w ogóle nie działa. Przycisk
przewijania do tyłu nie działa jeszcze w żadnej przeglądarce.
Podsumowanie
W tym rozdziale przeanalizowałeś różne kwestie związane z elementem video, takie jak jego obsługa
w przeglądarkach, ale największy problem dotyczy obsługi kodeków, i to właśnie ten aspekt szczególnie
warto śledzić.
Oprócz informacji o kodekach zdobyłeś również wiedzę na temat sposobów natywnego odtwarzania
filmów wideo we wszystkich aktualnych wersjach najpopularniejszych przeglądarek, bez konieczności
korzystania z zewnętrznych wtyczek, takich jak Flash. W przypadku starszych przeglądarek, które nie
obsługują elementu video, możesz użyć awaryjnie łącza albo osadzonego pliku Flash. Stworzyłeś również
odtwarzacz wideo z napisami, używając najnowszego standardu plików WebVTT, choć jest to raczkujący
jeszcze standard, więc należy na bieżąco śledzić wszelkie zmiany i sposoby jego zastosowania.
Na koniec omówiliśmy kilka składników API mediów, umożliwiającego tworzenie własnych kontrolek,
które możesz formatować za pomocą CSS-a.
184 Rozdział 7. Osadzanie wideo w HTML5
8
Osadzanie dźwięku w HTML5
A nalogicznie do elementu video, nowy element audio pozwala na osadzenie pliku dźwiękowego na stronie
bez potrzeby używania dodatkowych wtyczek, takich jak Flash. W nowoczesnych przeglądarkach
internetowych, włącznie z przeglądarkami dostępnymi na wielu urządzeniach przenośnych, dźwięk jest
odtwarzany natywnie — nie muszą one już polegać na zewnętrznych wtyczkach.
W tym rozdziale omówimy podstawowe zagadnienia związane z elementem audio, a następnie przyjrzymy
się sposobom rozwiązywania problemów pojawiających się w różnych przeglądarkach. Następnie poznasz
API, dzięki któremu możesz stworzyć swój własny odtwarzacz dźwięku.
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>8.1. Prosty przykład audio</title>
</head>
<body>
<audio src="music.mp3" controls />
</body>
</html>
Kod jest bardzo prosty, ale tak jak w przypadku elementu video, mogą się pojawić pewne problemy.
Jeśli kod z listingu 8.1 spróbujesz zastosować z plikiem .mp3 i nie usłyszysz dźwięku, przyczyną może być
problem z kodekami.
Męki z kodekami opisaliśmy bardziej szczegółowo w rozdziale 7., a chociaż informacje tam podane
dotyczą konkretnie typów plików wideo, sytuacja z typami plików dźwiękowych jest analogiczna.
186 Rozdział 8. Osadzanie dźwięku w HTML5
Dwa główne kodeki audio to Ogg Vorbis (.ogg) i MP3 (.mp3). Możesz również pomyśleć o formacie WAV
(.wav), ale pliki tego typu są zazwyczaj duże, przez co nie nadają się do ładowania przez sieć, a dodatkowo
nie obsługują metadanych, takich jak wykonawca i tytuł. W tej książce skoncentrujemy się więc na obu
głównych kodekach. Chociaż format MP3 może być uważany niemal za standardowy typ pliku, jest on
częścią grupy MPEG4/H.264, a więc jest typem „zamkniętym” i komercyjnym. Z drugiej strony Ogg uważa
się za „wolny” i „otwarty”. Zdania co do tego, który typ pliku daje dźwięk o lepszej jakości, są podzielone,
ale podstawową kwestię stanowi zróżnicowana obsługa w przeglądarkach. W tabeli 8.1 przedstawiono aktualną
sytuację dotyczącą odtwarzania w przeglądarkach dźwięku w różnych formatach.
Internet
Kodek Android Chrome Firefox iPhone Opera Safari
Explorer
Ogg Vorbis - 13+ 4+ - - 11+ -
MP3 2.3 13+ - 9+ 4+ - 5+
WAV - - - - - - -
<audio controls>
<source src="music.mp3" type="audio/mp3" />
<source src="music.ogg" type="audio/ogg" />
</audio>
Element audio nie wymaga określenia wysokości lub szerokości, ponieważ domyślnie każda przeglądarka
posiada swój własny odtwarzacz dźwięku, ale możesz wskazać wysokość i szerokość przy użyciu CSS-a:
audio {display: block; width: 90px; height: 28px;}
multimedialnego, ale dostępnych jest wiele innych odtwarzaczy. Mógłbyś też zresztą utworzyć własny
odtwarzacz, a niezbędne pliki — w naszym przypadku player.swf i swfobject.js — bez problemu znajdziesz
w internecie.
Tylko starsze przeglądarki pobierają pliki swfobject.swf oraz player.swf i odtwarzają plik dźwiękowy
w programie Flash Player.
Listing 8.4 przedstawia cały kod. Zapewnia on odtwarzanie dźwięku we wszystkich najpopularniejszych
przeglądarkach — czy to za pomocą natywnego elementu audio, czy to, w razie potrzeby, przy użyciu
zastępczego odtwarzacza Flash Player.
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>8.4. Odtwarzanie dźwięku w różnych przeglądarkach</title>
</head>
<body>
<audio controls>
<source src="music.mp3" type="audio/mp3" />
<source src="music.ogg" type="audio/ogg" />
<script type="text/javascript" src="swfobject.js"></script>
<div id="mediaplayer"></div>
<script>
var so = new SWFObject('player.swf','playerID','480','24','9');
so.addParam('allowfullscreen','true');
so.addParam('allowscriptaccess','always');
so.addVariable('file', 'music.mp3');
so.write('mediaplayer');
</script>
</audio>
<p>Pobierz plik dźwiękowy: <a href="music.mp3">plik MP3, 3 MB</a>; <a href="music.ogg">plik Ogg, 3 MB</a></p>
</body>
</html>
Atrybut src
Listing 8.1 z początku rozdziału pokazuje prosty przykład elementu audio i zawiera atrybut src. Użycie src
oznacza jednak, że jesteś ograniczony do jednego pliku dźwiękowego. Kiedyś, w idealnym świecie, będzie
to wszystko, czego będziesz potrzebować, jednak w chwili obecnej — ze względu na problemy z kodekami
i przeglądarkami — sposób ten jest w zasadzie bezużyteczny. Zamiast korzystać z src, rozważ zastosowanie
w obrębie elementu audio elementu source (jak na listingu 8.4), tak abyś mógł odtwarzać dźwięk w różnych
przeglądarkach.
Atrybut preload
Jeśli plik dźwiękowy osadzony na Twojej stronie ma dużą objętość albo jesteś pewien, że użytkownik będzie
chciał go słuchać, możesz nakazać przeglądarce rozpoczęcie pobierania pliku po wczytaniu strony. Atrybut
preload może przyjąć jedną z trzech wartości:
preload="auto"
Wartość auto (możesz też po prostu pozostawić samo preload) informuje przeglądarkę, aby pobrała plik
dźwiękowy po załadowaniu strony.
preload="none"
Wartość preload="none" mówi przeglądarce, aby nie pobierała zawartości dźwiękowej. Dopiero kiedy
użytkownik uruchomi odtwarzanie dźwięku, przeglądarka zacznie go pobierać.
preload="metadata"
Atrybut metadata pobiera informacje (metadane) o ścieżce dźwiękowej, w tym jej czas trwania i listę
utworów. Przeglądarka nie powinna pobrać samej zawartości dźwiękowej, dopóki użytkownik jej nie
uruchomi.
Atrybut loop
Jeśli element audio posiada atrybut loop, po zakończeniu odtwarzania rozpocznie się ono od początku.
Atrybut ten jest atrybutem logicznym — albo jest włączony, albo wyłączony.
Atrybut autoplay
Atrybut logiczny autoplay wymusza na przeglądarce rozpoczęcie pobierania i odtwarzania dźwięku po
wczytaniu strony. W ten sposób odtwarzanych jest na przykład wiele reklam. Istnieją pewne obawy związane
z dostępnością i łatwością wykorzystania odtwarzanych automatycznie dźwięków, nie wspominając już o tym,
jak dźwięki mogą być irytujące, ale atrybut autoplay znajduje się w specyfikacji ze względu na to, że pozwala
w ustandaryzowany sposób wymusić automatyczne odtwarzanie dźwięku, gdybyś naprawdę tego potrzebował.
Nie musisz uciekać się do różnych javascriptowych sztuczek, aby osiągnąć ten efekt. Zauważ, że dostępne
są też różne rozszerzenia przeglądarek wyłączające autoplay.
Uwaga
W systemie iOS atrybuty autoplay i preload zostały celowo wyłączone ze względu na potencjalne
koszty i problemy związane z prędkością wczytywania u użytkownika. Żadne dane nie są pobierane,
dopóki użytkownik nie zdecyduje się ręcznie uruchomić dźwięku. Istnieje kilka technik (a raczej
sztuczek) pozwalających na obejście tej blokady, ponieważ jednak są to techniki nieoficjalne,
nie powinny być wykorzystywane i nie zostały omówione w tej książce.
API mediów 189
Atrybut controls
Atrybut controls dodaje do pliku dźwiękowego domyślne, właściwe dla danej przeglądarki kontrolki
zawierające przycisk odtwarzania/pauzy, pasek przewijania, informację o czasie trwania i czasie od początku
utworu oraz kontrolki sterowania głośnością. Oto przykład:
<audio controls>
…
</audio>
Jest to atrybut logiczny, więc albo go dołączasz, albo nie. Jeśli nie zostanie dołączony, nic nie zostanie
wyświetlone i wówczas oczywiście użytkownik nie będzie mógł sterować dźwiękiem. Zdajesz sobie chyba
zatem sprawę, jak ważne jest, aby nie korzystać z atrybutu autoplay bez równoczesnego użycia atrybutu
controls.
Jeśli chcesz, możesz wykorzystać API do utworzenia własnych kontrolek rozpoczynania i wstrzymywania
odtwarzania — podyskutujemy o tym w dalszej części tego rozdziału. Rysunek 8.1 pokazuje domyślne
kontrolki w najpopularniejszych przeglądarkach.
Wskazówka
Co dziwne, jeśli w Firefoksie nie masz włączonej obsługi JavaScriptu, kontrolki nie są wyświetlane.
Inne przeglądarki wyświetlają kontrolki poprawnie bez pomocy JavaScriptu.
API mediów
HTML5 udostępnia rozwinięte i ekscytujące API dla multimediów. Korzystając z tego API i JavaScriptu,
możesz wpływać na odtwarzanie plików dźwiękowych na stronach internetowych. Poniżej znajduje się lista
dostępnych zdarzeń API:
190 Rozdział 8. Osadzanie dźwięku w HTML5
abort loadstart
canplay pause
canplaythrough play
canshowcurrentframe playing
dataunavailable progress
durationchange ratechange
emptied seeked
empty seeking
ended suspend
error timeupdate
loadeddata volumechange
loadedmetadata waiting
Nie wszystkie z powyższych zdarzeń i właściwości są już dostępne, ale większości, w tym tych istotnych,
które pozwalają tworzyć własne kontrolki odtwarzacza dźwięku, można już używać. Nie będziemy tu
opisywać wszystkich możliwości API — ograniczymy się do tych, które są istotne dla kolejnych przepisów.
Jeśli jednak jesteś zainteresowany zdobyciem większej wiedzy na temat tego API, polecamy materiały
na stronie http://www.w3.org/2010/05/video/mediaevents.html (to demonstracja wideo, ale API dotyczy
również odtwarzania dźwięku) oraz bardzo szczegółową specyfikację pod adresem http://www.w3.org/TR/
html5/media-elements.html#mediaevents.
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>8.5. Mikser</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script>
$(function(){
$('audio').each(function(index){
$(this).removeAttr("controls");
$(this).addClass('stopped');
var currentTime = $(this).next('div');
currentTime.html("")
$(this).click(function() {
if (this.paused == false) {
this.pause();
$(this).addClass('stopped');
this.currentTime = 0;
} else {
this.play();
$(this).removeClass('stopped');
}
});
this.addEventListener('timeupdate', function() {
currentTime.html(formatTime(this.currentTime));
}, false);
});
});
192 Rozdział 8. Osadzanie dźwięku w HTML5
function formatTime(seconds) {
seconds = Math.round(seconds);
minutes = Math.floor(seconds / 60);
minutes = (minutes >= 10) ? minutes : '0' + minutes;
seconds = Math.floor(seconds % 60);
seconds = (seconds >= 10) ? seconds : '0' + seconds;
return minutes + ':' + seconds;
}
</script>
<style>
* {
padding: 0;
margin: 0;
}
ul {
list-style-type: none;
margin: 50px auto;
width: 205px;
}
li {
float: left;
margin: 0 0 5px 5px;
}
li:nth-child(odd) {
clear: both;
margin-left: 0;
}
p {
clear: both;
}
audio {
background: url(images/stop.png) center center no-repeat #ccc;
-webkit-border-radius: 50px;
-moz-border-radius: 50px;
border-radius: 50px;
-moz-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5);
-webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5);
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5);
cursor: pointer;
display: block;
height: 100px;
margin: 0 0 5px;
width: 100px;
}
audio.stopped {
background: url(images/play.png) center center no-repeat #fff;
}
li div {
clear: both;
text-align: center;
}
Tworzenie miksera 193
</style>
</head>
<body>
<ul>
<li>
<audio controls loop>
<source src="guitar.mp3" type="audio/mp3" />
<source src="guitar.ogg" type="audio/ogg" />
</audio>
<div></div>
</li>
<li>
<audio controls loop>
<source src="beat.mp3" type="audio/mp3" />
<source src="beat.ogg" type="audio/ogg" />
</audio>
<div></div>
</li>
<li>
<audio controls loop>
<source src="turntable.mp3" type="audio/mp3" />
<source src="turntable.ogg" type="audio/ogg" />
</audio>
<div></div>
</li>
<li>
<audio controls loop>
<source src="clap.mp3" type="audio/mp3" />
<source src="clap.ogg" type="audio/ogg" />
</audio>
<div></div>
</li>
<li>
<audio controls loop>
<source src="boxingball.mp3" type="audio/mp3" />
<source src="boxingball.ogg" type="audio/ogg" />
</audio>
<div></div>
</li>
<li>
<audio controls loop>
<source src="synth6.mp3" type="audio/mp3" />
<source src="synth6.ogg" type="audio/ogg" />
</audio>
<div></div>
</li>
</ul>
<p>Utwory z <a href="http://beatstorm.com">beatstorm.com</a>; <a
´href="http://www.vocaldownloads.com">vocaldownloads.com</a>; <a
´href="http://www.freesound.org">freesound.org</a></p>
</body>
</html>
W pierwszej kolejności ukrywane są domyślne kontrolki i dzięki zastosowaniu CSS-a wyświetlany jest
przycisk Odtwórz. Kiedy użytkownik kliknie element audio, sprawdzamy, czy plik był wcześniej wstrzymany.
Jeśli tak było, za pomocą metody play dźwięk zostaje uruchomiony. Jeśli plik był odtwarzany, zostaje
194 Rozdział 8. Osadzanie dźwięku w HTML5
wstrzymany za pomocą metody pause. Podczas odtwarzania lub wstrzymywania przełączana jest klasa,
która obrazuje różne działania przycisków. Kiedy dźwięk jest wstrzymywany, odtwarzanie pliku zostaje
faktycznie zatrzymane, gdyż jego atrybut currentTime przyjmuje wartość 0. Natomiast do pokazania w trakcie
odtwarzania czasu, jaki upłynął od początku utworu, wykorzystywane jest zdarzenie nasłuchu timeupdate.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>8.6. Odtwarzacz radia internetowego</title>
Dodawanie internetowego radia 195
<script>
// zmienne lokalne elementów
var radioPlayer;
var radioStatus;
var radioControls;
var volumeControl;
var buttonPlayPause;
// funkcja inicjalizująca
function init() {
break;
}
}
// uaktualnij status
radioStatus.innerHTML = 'Buforowanie...';
radioPlayer.play();
}
// funkcje Odtwórz/Wstrzymaj
// obsługa kliknięcia przycisku Odtwórz/Wstrzymaj
function playPauseClicked() {
// sprawdź, czy odtwarzanie trwa, czy jest wstrzymane
if (radioPlayer.ended || radioPlayer.paused) {
// odtwarzacz jest wstrzymany; teraz odtwarzaj
playerPlay();
} else {
// odtwarzacz w trybie odtwarzania; teraz wstrzymaj
playerPause();
}
}
// aktualizuj status
buttonPlayPause.innerHTML = 'Wstrzymaj';
radioStatus.innerHTML = 'Teraz odtwarzam...';
// ponowne uruchomienie odtwarzania
radioPlayer.play();
}
// wstrzymaj odtwarzanie
function playerPause() {
// aktualizuj status
buttonPlayPause.innerHTML = 'Odtwórz';
radioStatus.innerHTML = 'Wstrzymano';
// wstrzymaj odtwarzanie
radioPlayer.pause();
}
volume_bar = document.getElementById('volume_bar');
volume_bar.innerHTML = parseInt(newVolume*100) + '%';
198 Rozdział 8. Osadzanie dźwięku w HTML5
volume_bar.style.width=newWidth+'px';
}
// inicjalizuj ładowanie
window.addEventListener('load',init,false);
</script>
<style>
* {
color: #000;
font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
margin: 0;
padding: 0;
}
#radioContainer {
background-color: #ccc;
border: 15px solid rgba(0,0,0,.5);
-moz-border-radius: 25px;
-webkit-border-radius: 25px;
border-radius: 25px;
-moz-box-shadow: 0px 0px 4px #000;
-webkit-box-shadow: 0px 0px 4px #000;
box-shadow: 0px 0 35px rgba(1, 1, 1, 0.7), inset 0px 0 35px rgba(1, 1, 1, 0.7);
padding: 20px;
margin: 40px auto;
overflow: hidden;
width: 420px;
}
h1 {
font-size: 1.8em;
margin: 0 0 10px;
text-align: center;
}
h2 {
margin: 10px 0 5px;
}
label {
left: -9999px;
position: absolute;
}
#stationList {
background-color:#fff;
border: 1px solid #000;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
Dodawanie internetowego radia 199
color:#333;
cursor: pointer;
padding: 5px;
text-align: center;
width: 100%;
}
#radioPlayer {
float: left;
width: 100%;
}
#currentStation {
color:#333;
text-shadow:#999;
}
#radioControls {
text-align:left;
visibility:hidden;
margin-top:10px;
}
#radioControls * {
float: left;
}
#volumeControl {
border: 1px solid #fff;
cursor: pointer;
float:left;
height: 25px;
margin: 0 5px;
position:relative;
width:60px;
}
#volume_background {
background-color:#ccc;
height:25px;
width:60px;
}
#volume_bar {
background:#fff;
color:#333;
height:25px;
position:absolute;
text-align:center;
width:0px;
}
button {
background: #fff;
border: none;
-moz-border-radius: 12px;
-webkit-border-radius: 12px;
border-radius: 12px;
cursor: pointer;
height: 27px;
200 Rozdział 8. Osadzanie dźwięku w HTML5
padding: 0 5px;
}
</style>
</head>
<body>
<div id="radioContainer">
<h1>Odtwarzacz internetowego radia w HTML5</h1>
<audio id="audioPlayer">
<p>Twoja przeglądarka nie obsługuje elementu audio.</p>
</audio>
<div id="stations">
<label for="stationList">Rozgłośnie</label>
<select id="stationList" size="5" onChange="setStation();"></select>
</div>
<div id="radioPlayer">
<h2 id="radioStatus"></h2>
<p id="currentStation"></p>
<div id="radioControls">
<div id="volumeTitle">Głośność</div>
<div id="volumeControl" onClick="volumeChangeClicked(event);">
<div id="volume_background">
<div id="volume_bar"></div>
</div>
</div>
<button id="buttonPlayPause" onClick="playPauseClicked();"></button>
</div>
</div>
</div>
</body>
</html>
Strumienie są albo w formacie OGG, albo MP3. Strumienie OGG będą działać w Firefoksie, Operze
i Chrome; strumienie MP3 — w Chrome, IE9 i Safari. Jeśli dany format nie jest obsługiwany w przeglądarce,
powinien zostać wyświetlony komunikat o błędzie.
1. Użyj HTML-a oraz CSS-a, aby utworzyć układ i kontrolki odtwarzacza.
2. Utwórz opartą o JSON listę stacji radiowych i ich adresów URL.
3. Jeśli wystąpił błąd, użyj API do wykrycia, jakiego jest on typu.
4. Dodaj każdą stację jako element option do listy stacji.
5. Gdy stacja zostanie wybrana, uruchom funkcję setStation, która zmienia src elementu audio
i natychmiast odtwarza dźwięk.
Po wczytaniu strony uruchamiana jest funkcja init. Odpowiada ona za skonfigurowanie odtwarzacza
dźwięku, tak abyś mógł się do niego odwołać i nim manipulować, a także ustawia kontrolki, takie jak Odtwórz
i regulacja głośności. Następnie wczytuje stacje za pomocą funkcji loadStations. Funkcja ta przechodzi w pętli
przez listę JSON stacji radiowych i tworzy listę wyboru dla użytkownika. Element select zawiera zdarzenie
onChange, wywołujące funkcję setStation, która pobiera wybraną stację radiową i przypisuje jej adres URL
jako src elementu audio, a także zmienia atrybut type (na MP3 albo OGG) tego elementu, tak aby prawidłowo
się on odtworzył. Jeśli przeglądarka nie może odtworzyć danego typu dźwięku (np. w Firefoksie albo Operze
nie da się odtworzyć Radia Jazz FM, którego strumień jest zapisany w formacie MP3), sygnalizowany jest
błąd z tekstem błędu zależnym od jego typu.
Podsumowanie 201
Mimo że źródło odtwarzacza zostało określone, nie zadziała on, dopóki mu tego nie nakażesz, co jest
realizowane za pomocą radioPlayer.play(). Podczas odtwarzania strumienia generowane są zdarzenia
o nazwie durationchange. Zdarzenie to uruchamia funkcję o nazwie streamPlaying, która uaktualnia na
ekranie tekst mówiący o tym, co stacja nadaje, zmienia przycisk Odtwórz na Wstrzymaj oraz wyświetla
pozostałe kontrolki.
Ostatnie kontrolki dotyczą przycisku Odtwórz/Wstrzymaj oraz regulacji głośności. Kiedy zostaje
kliknięty przycisk Odtwórz/Wstrzymaj, uruchamiana jest funkcja sprawdzająca aktualny status: jeśli dźwięk
jest wstrzymany lub odtwarzanie się zakończyło, uruchamiane jest .play(), a w przeciwnym przypadku
.pause(). W poprzednim rozdziale, dotyczącym wideo, do regulacji głośności użyłeś nowego elementu
HTML5 — range input. Tutaj tworzona jest wersja przyjazna dla wielu przeglądarek, która wykrywa punkt,
w jakim pasek głośności został kliknięty, i aktualizuje poziom głośności dźwięku za pomocą .volume.
Podsumowanie
W tym rozdziale dowiedziałeś się, jak odtworzyć plik dźwiękowy w przeglądarce bez potrzeby użycia
zewnętrznej wtyczki, zwykle Flasha. Ponieważ jest to nowa funkcja przeglądarek, czasami zachowują
się one niezgodnie z oczekiwaniami. Podobnie jak w przypadku elementu video, należy wziąć pod uwagę
problemy z kodekami, ale w przypadku starszych przeglądarek można zastosować rozwiązania zastępcze.
Poznałeś również różne elementy nowego API, dzięki którym możesz wpływać na zachowanie pliku
dźwiękowego w przeglądarce, sterując jego odtwarzaniem i wstrzymywaniem, regulacją głośności, a także
zmianą odtwarzanego pliku.
202 Rozdział 8. Osadzanie dźwięku w HTML5
9
Dokonywanie zmian
w historii przeglądarki
W tym rozdziale omówimy aktualizacje interfejsu historii w HTML5, szczególnie dwie nowe metody
w History API (pushState i replaceState), oraz zapoznamy się z kilkoma przepisami dotyczącymi
używania ich do przechodzenia między stronami w sesji. Ponadto poznasz zdarzenie popstate, nauczysz się
używać History API do przechowywania czegoś więcej niż tylko informacji o odwiedzanych stronach oraz
zaznajomisz się z zaawansowanymi zagadnieniami, takimi jak bezpieczeństwo i dodatkowe biblioteki.
Podstawy historii
Javascriptowe History API jest używane w witrynach internetowych od JavaScriptu 1.0, lecz przed
pojawieniem się HTML5 nie było znacząco aktualizowane. Wraz z pojawieniem się AJAX-a i aplikacji
działających bez użycia kolejno ładowanych stron korzystanie z obiektu historii do przechodzenia dalej,
wstecz albo do określonego wpisu w sesji stało się problematyczne. W związku z tym wiele frameworków
aplikacyjnych, w tym YUI, zawiera własne techniki zarządzania sesją przeglądarki.
Dawniej, aby dodać stronę do historii przeglądarki bez konieczności przejścia na inną stronę, trzeba
było zmienić adres URL poprzez dodanie do niego kotwicy za pomocą symbolu #. Za pomocą metod
history.pushState i history.replaceState możesz teraz, odpowiednio, dodawać wpisy do historii i ją
modyfikować, a jeśli połączysz je ze zdarzeniem popstate okna, będziesz w stanie dać użytkownikowi ulepszone
możliwości nawigowania między stronami. Dzięki możliwości dodawania do historii i modyfikowania
historii stron na witrynie History API lepiej obsługuje przycisk Wstecz w bogatych aplikacjach korzystających
z technologii AJAX. Możesz teraz zmieniać „stan widoku” aplikacji po prostu przez pozorne dodanie strony
albo kontekstu do historii.
Możesz myśleć o historii sesji przeglądarki albo o stronach, do których użytkownik przechodził na
Twojej witrynie, jak o stosie, na szczyt którego są odkładane oglądane strony. Gdy użytkownik klika lub
dotyka przycisk Wstecz przeglądarki, strony są zdejmowane ze stosu jedna po drugiej. Korzystając jedynie
z JavaScriptu, możesz przejść dalej, wstecz albo o określoną liczbę stron w obu kierunkach za pomocą
poniższych poleceń:
204 Rozdział 9. Dokonywanie zmian w historii przeglądarki
window.history.forward,
window.history.back,
window.history.go([delta]).
Metody te są nadal dostępne w History API, ale teraz, w HTML5, za pośrednictwem tego API możesz
dodatkowo dynamicznie dodawać wpisy do historii, przechwytywać zdarzenia przejścia między stronami,
a nawet kontrolować kontekst stron.
Kompatybilność przeglądarek
Rozszerzenia History API są obecnie obsługiwane w przeglądarkach pokazanych w tabeli 9.1.
Android 2.2+
Chrome 5.0+
Firefox 4.0+
Internet Explorer -
iOS Safari 3.0+
Opera 11.0+
Safari 5.0+
Parametr data reprezentuje stan strony i zostaje automatycznie powiązany z nowym wpisem. Jak później
zobaczysz, wpis ten może być pobrany przez zdarzenie okna popstate. Przez stan rozumiemy bieżący kontekst
wyświetlania strony. Może to być na przykład przepis, który dana osoba aktualnie przegląda, pochodzący
z wywołania bazy danych wykonanego choćby przez AJAX.
Niektóre przeglądarki nakładają ograniczenia na wielkość parametru data, gdyż obiekt stanu jest
przechowywany przez przeglądarkę lokalnie na dysku użytkownika. W Firefoksie ten limit wynosi 640 000
znaków serializowanej reprezentacji obiektu stanu. Jeśli masz zamiar przekroczyć ten limit lub mógłbyś
potencjalnie to zrobić, powinieneś użyć sessionStorage albo localStorage (patrz rozdział 11.).
Dodawanie do historii wpisów za pomocą pushState 205
Uwaga
Niektóre przeglądarki, na przykład Firefox, ignorują parametr title i nie wyświetlają użytkownikowi
tej wartości na liście historii sesji. Zachowanie to zależy od przeglądarki i mamy nadzieję, że zostanie
poprawione w przyszłych aktualizacjach.
Jeśli podany jest parametr url, to zastąpi on adres URL w pasku adresu przeglądarki, ale nie spowoduje,
że przeglądarka ściągnie stronę z witryny. Pozwala to użytkownikom dodać zakładkę z adresem URL i powrócić
do niego później. Oczywiście po stronie serwera potrzebny jest kod do obsługi adresów z zakładek, które nie
reprezentują prawdziwych stron. Przekazany URL może być ścieżką względną albo bezwzględną, jednak musi
się ona znajdować w tej samej domenie co aktualny adres URL. Jeśli użyta jest ścieżka względna, będzie ona
oparta na bieżącym adresie dokumentu. Domyślną ścieżką w przypadku nieprzekazania parametru URL albo
przekazania tylko łańcucha zapytania (takiego jak "?page=5") jest aktualny adres URL dokumentu.
Wykorzystanie metody pushState przypomina używanie znaku kratki (hash, #) do kontrolowania
kontekstu strony, powszechnego w dynamicznych aplikacjach zbudowanych w oparciu o AJAX:
window.location = "#foo";
Jednak metoda pushState zapewnia większą elastyczność niż dostęp oparty na znaku kratki:
Metoda pushState pozwala na pozostanie na tej samej stronie albo zmianę adresu URL. Jeśli zostanie
użyty znak kratki, przeglądarka pozostaje przy tym samym adresie URL.
Metoda pushState pozwala zachować informacje kontekstowe w stanie historii. Jak zobaczysz
w dalszej części rozdziału, może to być pomocne.
Warto podkreślić ostatni punkt, ponieważ oznacza on znaczny postęp w porównaniu ze sposobem
związanym z dodawaniem znaku kratki do adresów URL. Przy każdym wpisie w historii pushState pozwala
zapisać obiekt danych, który może przechowywać informacje kontekstowe o stanie strony. Dane mogą być
tak proste jak ciąg znaków lub tak złożone jak zserializowany obiekt JSON. Załóżmy na przykład, że masz
stronę, która pokazuje slajdy z prezentacji. Każdy slajd może mieć powiązane ze sobą informacje o tym,
jak ma być wyświetlony, na przykład dotyczące napisów, ramki, podziękowań i tak dalej. Informacje te
przechowuje obiekt stanu, dzięki czemu możesz łatwo zmienić styl strony w oparciu o slajd, przy którym
się znajdujesz. W dalszej części tego rozdziału znajdziesz przepis dotyczący wykorzystania obiektu stanu.
Użyjmy metody pushState obiektu history do dodania nowego wpisu do historii sesji. Po załadowaniu
strona sprawdzi dostępność History API w przeglądarce przez wywołanie typeof na metodzie
history.pushState. Jeżeli metoda zwraca „undefined”, to znaczy, że przeglądarka nie obsługuje History API.
Po zastosowaniu tego testu możesz użyć innego sposobu implementacji historii albo ograniczyć użytkownikowi
funkcjonalność w oparciu o jego aktualną przeglądarkę:
1. Utwórz pustą stronę HTML z elementem div, aby pokazać aktualny widok (exhibit).
2. Dodaj przycisk, który po kliknięciu uruchamia javascriptową funkcję nextExhibit.
3. Dodaj funkcję nextExhibit, by wykonać metodę pushState.
4. Dodaj wywołanie metody pushState, aby dopisać kontekst widoku surykatki do historii:
history.pushState('Surykatka','Widok surykatki','meerkats.html');
5. Zaktualizuj div widoku, aby pokazać użytkownikowi, że jest przy widoku surykatki:
document.getElementById("exhibit").innerHTML = "Przy surykatkach.";
206 Rozdział 9. Dokonywanie zmian w historii przeglądarki
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>9.1. W zoo</title>
</head>
<body>
<script>
// inicjalizuj obsługę przycisku
function init() {
// powiąż funkcję obsługi kliknięcia z przyciskiem
var btnNextExhibit = document.getElementById('btnNextExhibit');
btnNextExhibit.addEventListener('click',nextExhibit,false);
}
function nextExhibit() {
// Sprawdź, czy metoda pushState w History API jest dostępna
history.pushState('Surykatka','Widok surykatki','meerkats.html');
} else {
}
}
Gdy załadujesz stronę w przeglądarce, na podstawie tytułu strony oraz wyświetlonego na niej komunikatu
stwierdzisz, że jesteś przy wejściu do zoo. Jeśli klikniesz przycisk Odwiedź surykatki, w historii zostanie
zapisana bieżąca strona, a następnie zmieni się tytuł i adres URL okna przeglądarki bez faktycznego przejścia
do nowej strony. JavaScript zmieni komunikat informujący użytkownika o widoku surykatki. Pod każdym
względem będzie to z punktu widzenia użytkownika wyglądało tak, jakby z serwera została załadowana nowa
strona. Jest to jednak tylko nowy kontekst, który dodałeś do historii poprzez pushState.
Tworzenie przeglądarki obrazków 207
Wskazówka
W niektórych przeglądarkach, na przykład Firefox, podanie w metodzie pushState parametru url,
który ma wartość nieprawidłowej strony, spowoduje wyjątek zabezpieczeń. Aby temu zaradzić, podaj
prawidłowy adres URL lub użyj opcjonalnego parametru stanu, co usunie wyjątek.
Jeśli wyświetlisz w przeglądarce listę historii, jako ostatnią zobaczysz stronę „W zoo”, a po kliknięciu
przycisku Wstecz zostaniesz przeniesiony z powrotem do tej strony. Komunikat na stronie nie zmieni się
na „Jesteś przy wejściu do zoo.” — później dowiesz się, jak to zmienić.
Pamiętaj, że adres URL jest opcjonalny, ale może być użyteczny dla powracających na stronę
użytkowników. Oczywiście zapisane wpisy, którym na serwerze nie odpowiada prawdziwa strona,
będą musiały być obsłużone przez kod znajdujący się na serwerze.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Obrazek 1</title>
<style>
div.imgView { height: 300px; }
div.imgView img { height: 300px; }
div.imgRow img { height: 100px; }
a { cursor: pointer }
</style>
</head>
<body>
<script>
// zmienna przechowująca numer bieżącego obrazka
var currentImg = 1;
// ustaw następny slajd we wpisach historii wraz z obiektem stanu i domyślnymi ustawieniami
history.pushState( imgNum, imgTitle, '?img=' + imgNum );
document.getElementById('imgSlide').src = 'images/slide' + imgNum + '.jpg';
document.getElementById('imageInfo').innerHTML = imgTitle;
}
</script>
<!-- obszar wyświetlania stanu historii — ustaw na pierwszym obrazku (po załadowaniu strony)-->
<div id='stateInfo'>Obrazek 1</div>
</body>
</html>
Jeśli uruchomisz powyższy przepis i wybierzesz miniatury do obejrzenia, zobaczysz wpisy umieszczane
w historii przeglądarki, jakby ładowała ona oddzielne strony. Rysunek 9.1 pokazuje sytuację po wybraniu
zdjęć 3, 5, 4 i 2. Po lewej stronie rysunek pokazuje rozwijane menu przycisku Wstecz przeglądarki. Po prawej
widać to, co jest wyświetlane na stronie po wybraniu zdjęć przez użytkownika. Zauważ, że Obrazek 1 znajduje
się na dole listy, ponieważ jest to oryginalny tytuł strony HTML. Po pierwszym obrazku dynamicznie
utworzony tytuł zostaje dodany do listy historii. Ostatni wybrany i aktualnie pokazywany obrazek (Obrazek 2)
nie jest wymieniony w historii, ponieważ znajduje się on w bieżącym kontekście.
Pobieranie stanu w przeglądarce obrazków 209
Rysunek 9.1. Lista rozwijana historii przycisku Wstecz dotycząca wybranych obrazków
Uwaga
Na powyższym listingu użyliśmy włączonego do strony skryptu służącego do utworzenia przycisków
miniatur po załadowaniu strony. Normalnie posłużylibyśmy się zdarzeniem domReady poprzez framework,
taki jak jQuery. Aby zaoszczędzić miejsce i mieć kod niezależny od dodatkowych bibliotek, użyliśmy
wspomnianego skryptu.
Po uruchomieniu tego przepisu zobaczysz, że jeśli klikniesz przycisk Wstecz w przeglądarce, lista stron
w elemencie div o identyfikatorze pageInfo pozostanie taka sama. Czy nie byłoby dobrze, gdybyś mógł
przechwycić zdarzenie kliknięcia przez użytkownika przycisku Wstecz, aby zaktualizować wyświetlaną stronę?
HTML5 udostępnia w tym celu zdarzenie pobrania stanu ze stosu, co zostało omówione w kolejnym przepisie.
Zdarzenie pobrania stanu (popstate) jest wyzwalane na javascriptowym obiekcie window i jest powiązane
z funkcją obsługi, do której odwołujemy się w powyższym wierszu jako funcRef. Jeśli zatem użytkownik
przechodzi przez swoje wpisy w historii za pomocą przycisku Wstecz, możesz wykonać wszystkie niezbędne
działania, aby na stronie HTML załadować poprawny kontekst.
Jak pamiętasz z wywołania metody pushState, wraz z każdym umieszczonym w historii wpisem możesz
zapisać dane albo stan związany z tym wpisem. Zdarzenie popstate przekazuje stan, który zapisałeś,
do procedury obsługi zdarzenia i pozwala skryptowi na dostęp do obiektu stanu. Do danych stanu możesz
się odwołać przez atrybut (tylko do odczytu) zdarzenia event.state.
Na listingu 9.2 utworzyłeś prostą przeglądarkę obrazków, która pozwala użytkownikowi wybrać
miniaturę obrazka. Po jej wybraniu obrazek jest ładowany do obszaru wyświetlania, a w historii jest
umieszczany nowy wpis dotyczący strony. Zapisanie strony w historii nie jest jednak pomocne, jeśli nie
możesz pokazać właściwego obrazka, gdy użytkownik użyje przycisku Wstecz w przeglądarce albo skrypt
210 Rozdział 9. Dokonywanie zmian w historii przeglądarki
wywoła history.back lub history.go. W tym przepisie dodasz procedurę obsługi zdarzenia pobrania stanu
w celu przechwycenia zdarzenia przejścia do innej strony i załadowania właściwego obrazka oraz jego
szczegółów. Dodaj zaznaczony fragment kodu z listingu 9.3 do listingu 9.2, wstawiając go pomiędzy istniejące
wiersze skryptu, tak jak zostało to pokazane poniżej.
…
<script>
Po włączeniu funkcji obsługi zdarzenia na listingu 9.3 uruchom kod. Kiedy strona zostanie załadowana,
wybierz drugą, a następnie trzecią miniaturę. Spowoduje to wykonanie dla każdej z nich metody pushState,
co doda je do listy pozycji w historii. Po dodaniu powyższej procedury obsługi zdarzenia popstate za każdym
razem, kiedy użytkownik kliknie w przeglądarce przycisk Wstecz lub Dalej, strona przechwyci zdarzenie
i załaduje użytkownikowi odpowiedni obrazek na podstawie zapisanego w zdarzeniu stanu.
Teraz kliknij przycisk Wstecz w przeglądarce. Zostanie wywołana procedura obsługi zdarzenia popstate.
Najpierw, przez przypisanie stanu zdarzenia do zmiennej przechowującej numer bieżącego obrazka, stan
zostanie pobrany, a następnie użyty do wyświetlenia odpowiedniego obrazka i ustawienia tytułu. W celu
weryfikacji możesz następnie zaktualizować element div o identyfikatorze stateInfo, aby zobaczyć, co zostało
pobrane z historii (patrz rysunek 9.2).
Zauważ, że zdarzenie jest wywoływane nawet po wybraniu jednej z pozycji na liście rozwijanej historii
w przeglądarce. Pobaw się z kodem, aby zobaczyć, jak zdarzenia pushState i popstate obsługują przechodzenie
po stronach historii.
Zmiana historii za pomocą replaceState 211
Wskazówka
Użytkownik może przechodzić wstecz w historii przeglądarki o dowolną liczbę kroków poprzez menu
historii lub polecenia History API w skryptach, takie jak go i back. Najlepiej jest powiązać element
danych z wpisu historii z kluczem, który pozwala Ci dokładnie określić, jaki jest kontekst wpisu.
Może to być na przykład unikalny numer jakiegoś elementu, numer slajdu lub jakiś inny unikatowy
indeks. W ten sposób podczas obsługiwania zdarzenia popstate kod będzie „wiedział”, który element
przetwarzać zamiast ostatniego.
Po załadowaniu strony, kiedy dopiero co znalazła się ona w przeglądarce, tytuł i adres URL są zachowywane
we wpisie w historii. Jednak wraz z tą informacją nie są zapisywane żadne dane kontekstowe. Aby zapisać
dane w celu ich przechowania we wpisie w historii wraz z tytułem i adresem URL, po wczytaniu strony możesz
wywołać replaceState z tym samym tytułem, adresem URL i Twoimi dodatkowymi danymi stanu. Spowoduje
to zastąpienie aktualnego wpisu tą samą informacją o stronie oraz Twoimi dodatkowymi danymi. Po załadowaniu
strony użytkownik mógłby na przykład sprecyzować kryteria wyszukiwania, a po każdej poprawce zapisane
ustawienia byłyby zastępowane metodą replaceState.
W tym przepisie zastosowaliśmy replaceState, aby pokazać sytuację, w której strona nie jest ładowana
ponownie, ale zmienia stan bieżącego wpisu w historii. Po załadowaniu strony użytkownik może kliknąć
przycisk zastępujący obecny stan, którym jest klucz page oraz wartość idxCounter ze zaktualizowaną
wartością licznika. Wartość licznika jest po prostu zwiększana za każdym razem, kiedy stan jest zastępowany.
Aby zbudować stronę zgodnie z listingiem 9.4, wykonaj następujące kroki:
1. Utwórz pustą stronę HTML z pustym elementem div o identyfikatorze stateInfo, który będzie
wykorzystywany, aby za każdym razem zobaczyć, co ustawiasz.
2. Dodaj przycisk, który po kliknięciu uruchamia javascriptową funkcję o nazwie nextState.
3. Dodaj funkcję nextState z wywołaniem history.replaceState:
history.replaceState({page: idxCounter}, "page "+idxCounter, "?page="+idxCounter);
4. Uaktualnij w nextState element div o identyfikatorze stateInfo, pokazujący Ci, że replaceState
zostało wykonane, oraz zwiększ wartość licznika idxCounter.
212 Rozdział 9. Dokonywanie zmian w historii przeglądarki
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Strona</title>
<script>
var idxCounter = 1; // licznik przechowujący stan strony
</script>
</head>
<body>
<button id="btnNextState">Zastąp stan</button>
<div id="stateInfo"></div>
</body>
</html>
Za każdym razem, kiedy klikniesz przycisk Zastąp stan, zauważysz, że stan rzeczywiście jest zastępowany,
ponieważ w adresie URL będzie widoczny dodany przez Ciebie łańcuch zapytania: page=<idxCounter>. Jeśli
spojrzysz na rozwijane menu przycisku Wstecz przeglądarki, zauważysz również, że po wywołaniu metody
replaceState do historii nie jest dodawany nowy wpis. Zauważ, że dla stanu w replaceState zastosowałeś
inny styl danych. Użyłeś serializowanego obiektu w stylu JSON z kluczem, stroną i wartością idxCounter.
W przypadku elementu danych metod pushState lub replaceState nie jesteś ograniczony jedynie do napisów,
ale możesz zapisywać złożone obiekty.
Zmiana historii strony 213
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Strona</title>
<script>
function loadPages() {
function logAction(strAction) {
document.getElementById('stateInfo').innerHTML += strAction + '<br>';
alert(strAction);
}
</script>
</head>
<body>
214 Rozdział 9. Dokonywanie zmian w historii przeglądarki
<div id="stateInfo"></div>
</body>
</html>
Po załadowaniu skrypt wywołuje funkcję loadPages, która odkłada na stos dwie kolejne strony, wykonuje
replaceState, uruchamia dwa polecenia back historii, a następnie przesuwa się w niej o dwa kroki do przodu.
Oto rezultat wykonania przepisu z okna przeglądarki Firefox:
zapisanie strony 1
zapisanie strony 2
zastąpienie strony 2 stroną 3
cofnięcie się o jeden krok
Pobranie — pozycja: http://localhost/listing.9.5.html?page=1, stan: {"page":1}
ponowne cofnięcie się o jeden krok
Pobranie — pozycja: http://localhost/listing.9.5.html, stan: null
zrobienie dwóch kroków naprzód
Pobranie — pozycja: http://localhost/listing.9.5.html?page=3, stan: {"page":3}
Rysunek 9.3. Wpisy stosu historii i bieżący wskaźnik podczas wykonywania przepisu
Należy pamiętać, że zdarzenie popstate nie zadziała, dopóki nie zostanie wywołane zdarzenie okna
onload. Jeśli przed zdarzeniem okna onload będą wywoływane metody, które zwykle aktywowałyby zdarzenie
popstate, wtedy zazwyczaj tylko ostatnie zdarzenie popstate zostanie aktywowane w przeglądarce.
Wskazówka
Ten przepis pokazuje, jak metoda replaceState może zastąpić stan strony po wykonaniu metody
pushState. Jak widziałeś, stan oryginalnej strony to null, ponieważ oryginalna strona została
załadowana przez przeglądarkę, a nie przez pushState. Jeśli musisz powiązać stan z oryginalną
stroną, po załadowaniu strony wykonaj replaceState w celu powiązania strony z Twoimi danymi stanu.
Używanie zaawansowanych obiektów danych stanu do przenoszenia informacji 215
Poniższy kod pokazuje, jak mogą być przekazywane bardziej złożone obiekty:
var stateObj = { page: 1, title: "Mój slajd numer 1", author: "Paweł"};
history.pushState(stateObj, 'Slajd 1', 'slide1.html');
Obiekt danych stanu, wprowadzany do historii, jest doskonałym sposobem na przechowywanie wyborów
użytkownika, preferencji albo działań wykonanych na stronie. Zauważ jednak, że obiekty danych stanu gubią
się, jeśli użytkownik wyczyści historię przeglądarki, i mogą mieć ograniczenia ilości danych określone przez
poszczególne przeglądarki. Jednak w przypadku większości zastosowań nie powinno to stanowić problemu.
W tym przepisie nauczysz się przechowywać dane we wpisach w historii w formacie JSON, a następnie
wyciągać je z powrotem przez zdarzenie window.onpopstate. Listing 9.6 wyświetla prosty pokaz slajdów przy
użyciu obrazków slajdów i preferencji użytkownika. Preferencje użytkownika są przechowywane w każdym
wpisie w historii, tak aby po przejściu za pomocą przycisku Wstecz do wcześniejszego slajdu preferencje
zostały przywrócone. Przywrócenie preferencji odbywa się w procedurze obsługi zdarzenia popstate.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Slajd 1</title>
</head>
<body>
<script>
// ustaw numery pierwszego i ostatniego slajdu
var minSlide = 1;
var maxSlide = 5;
Ten kod pozwala użytkownikowi dodać ramkę obrazka oraz wprowadzić notatkę dotyczącą każdego
slajdu. Możesz udostępnić dowolną liczbę opcji. Służący do debugowania element div o identyfikatorze
stateInfo pokazuje kontekst danych, w miarę jak wpisy są pobierane z historii. Stan obiektu JSON możesz
wyświetlić za pomocą metody JSON.stringify. Do każdej przechowywanej wartości obiektu stanu możesz
się odwołać za pomocą klucza. Aby uzyskać wartość stanu ramki, należy wywołać event.state.border.
Kiedy użytkownik kliknie w celu przejścia do następnego slajdu, tworzony jest obiekt z aktualnymi
ustawieniami i numerem slajdu. Jest on następnie przekazywany do wywołania replaceState, aby zapisać
stan przed pokazaniem następnego slajdu. Następnie przywracane są domyślne ustawienia i pokazywany
jest następny w kolejności slajd. Oczywiście stan wcześniej wyświetlanych slajdów, które pojawiają się po
bieżącym slajdzie, nie jest brany pod uwagę, ponieważ każdy następny slajd jest umieszczany we wpisach
w historii wraz ze swoim początkowym stanem. Aby rozwiązać ten problem, możesz użyć pewnych nowych
w HTML5 technik przechowywania danych po stronie klienta, które poznasz w rozdziale 11.
Dzięki takim regułom zachowań przeglądarki możliwość niewłaściwego wykorzystania History API
jest zminimalizowana. Można sprawdzić działanie jednej z tych reguł, próbując zmienić adres URL na adres
w innej domenie.
Uwaga
Przeglądarki mogą nałożyć ograniczenia i przycinać stos historii, aby zapobiec przeciążeniu potencjalnymi
atakami „zalewania”. Każda przeglądarka ma swoją ustaloną liczbę wpisów, ale kolejność usuwania
jest zgodna z metodą FIFO (ang. first in, first out — pierwszy na wejściu, pierwszy na wyjściu).
Odbyło się wiele dyskusji na temat zezwalania JavaScriptowi na sterowanie wyświetlaniem adresu URL
przeglądarki bez rzeczywistego dokonywania zmiany strony lub powodowania załadowania nowej strony.
Główne obawy budzi to, że metody takie jak pushState lub replaceState mogą być użyte do wyłudzania
Testowanie bezpieczeństwa historii 219
osobistych i poufnych informacji, gdyż użytkownikowi może się wydawać, że znajduje się w innym miejscu,
niż jest w rzeczywistości. Możesz sobie wyobrazić, co by się działo, jeśli wszyscy mieliby możliwość zmieniania
adresu na całkowicie dowolny. Przeglądarki jednak, zgodnie ze specyfikacją History API, muszą walidować
adres używany w parametrze url. Jeśli używana jest ścieżka bezwzględna, adres musi się znajdować w tej
samej domenie co oryginalna strona. Sprawdźmy, czy przeglądarka chroni przed potencjalnym nadużyciem
przez próbę zapisania w historii strony z inną domeną. Listing 9.7 pokazuje bardzo prostą stronę
umożliwiającą zapisanie nowego stanu. Wypróbuj go i sprawdź, czy działa w Twojej przeglądarce,
przez wykonanie następujących kroków i utworzenie kopii listingu 9.7:
1. Utwórz pustą stronę HTML, która ma przycisk uruchamiający funkcję pushPage.
2. Dodaj funkcję pushPage, która sprawdza dostępność metody pushState, a następnie zapisuje
nowy kontekst w postaci page.html.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>9.7. Zapisywanie stron w tej samej domenie</title>
</head>
<body>
<script>
// inicjalizacja obsługi przycisku
function init() {
// załącz obsługę kliknięcia przycisku
var btnPushPage = document.getElementById('btnPushPage');
btnPushPage.addEventListener('click',pushPage,false);
}
</script>
<button id="btnPushPage">Spróbuj wykonać zapis w historii</button>
</body>
</html>
Zmodyfikujmy teraz nieco powyższy listing, aby zobaczyć, co się stanie, jeśli ktoś spróbuje użyć metody
pushState do sfałszowania domeny URL. Zmień parametr url metody pushState, jak pokazano na listingu
220 Rozdział 9. Dokonywanie zmian w historii przeglądarki
9.8, na ścieżkę bezwzględną domeny innej niż ta, na której aktualnie uruchamiasz stronę. Jako przykład
wybraliśmy www.asite.com.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>9.8. Ustawianie innej domeny</title>
</head>
<body>
<script>
// inicjalizacja obsługi przycisku
function init() {
// załącz obsługę kliknięcia przycisku
var btnPushPage = document.getElementById('btnPushPage');
btnPushPage.addEventListener('click',pushPage,false);
}
</script>
<button id="btnPushPage">Spróbuj wykonać zapis w historii</button>
</body>
</html>
Kiedy przeglądarka zapisuje nowy stan w historii za pomocą metody pushState, sprawdza przekazany
adres URL. Jeśli adres URL jest pełną ścieżką, a domena jest różna od tej, która jest „nadpisywana”, wywołanie
nie powiedzie się, zwróci wyjątek albo po prostu nie zrobi niczego, zależnie od przeglądarki. To samo dotyczy
metody replaceState.
Pomocne biblioteki
Jako twórcy stron internetowych musimy się interesować nie tylko aspektami bezpieczeństwa History API,
ale również obsługą tego API w różnych przeglądarkach, a w ramach danej przeglądarki w różnych jej wersjach.
W przypadku kilku nowych API HTML5 poziom ich implementacji w poszczególnych przeglądarkach
znacznie się różni, a kompatybilność wstecz w przewidywalnej przyszłości wciąż będzie problemem, dopóki
Podsumowanie 221
większość użytkowników nie zaktualizuje ich do nowych wersji. W przypadku History API oznacza to,
że dla uzyskania kompatybilności wstecz funkcjonalności zapamiętywania stanu strony musisz nadal
obsługiwać metodę z kotwicą w adresie oraz zdarzenie hashChange:
window.onhashchange = function() {
alert("Kotwica została zmieniona!");
};
window.location.hash = Math.random();
Nie oznacza to jednak, że musisz zrezygnować z korzyści płynących z nowych funkcji historii HTML5.
Jak w przypadku większości problemów ze zgodnością przeglądarek, inni programiści byli świadomi istnienia
tego problemu i stworzyli biblioteki do obsługi różnic nie tylko między przeglądarkami, ale także pomiędzy
ich wersjami. W przypadku History API główną biblioteką jest w tej chwili history.js.
Oczywiście możesz samodzielnie zaprogramować obsługę różnic między przeglądarkami, ale możesz
także ściągnąć z GitHuba (https://github.com/balupton/History.js) plik history.js, udostępniający prostą
opakowującą bibliotekę javascriptową, która używa metod historii HTML5, jeśli są obsługiwane, lecz w razie
potrzeby automatycznie odwołuje się awaryjnie do metody z kotwicą. Ogólnie składnia jest podobna do
metod, zdarzeń i atrybutów History API, które widziałeś w tym rozdziale. Niestety nie mamy tutaj miejsca
na wypróbowanie tej biblioteki, ale udostępnia ona następujące funkcje:
obsługa wielu przeglądarek,
wsparcie frameworków aplikacyjnych, w tym jQuery, MooTools i Prototype,
wsteczna kompatybilność ze starszymi przeglądarkami za pomocą znaczników ze znakiem #.
Podsumowanie
History API dostępne w JavaScripcie ma bardzo użyteczne funkcje i udostępnia twórcom stron internetowych
możliwość zmiany historii użytkownika w danej witrynie bez zmieniania samej strony. Witryny takie jak
GitHub i Flickr już zaczęły z powodzeniem używać History API, dzięki czemu są bardziej przyjazne dla
użytkownika.
W tym rozdziale zbadałeś, jak metody pushState i replaceState oraz zdarzenie popstate działają
w połączeniu z listą wpisów historii (patrz rysunek 9.4).
Rysunek 9.4. History API z metodami pushState i replaceState oraz zdarzeniem popstate
222 Rozdział 9. Dokonywanie zmian w historii przeglądarki
Powinieneś mieć teraz wyobrażenie o tym, w jaki sposób możesz zastosować te możliwości we własnej
witrynie albo aplikacji.
10
Wykorzystanie Geolocation API
do uzyskania informacji
o położeniu geograficznym
Będące częścią HTML5 Geolocation API udostępnia wbudowane metody, które umożliwiają uzyskanie
dość szczegółowych informacji o lokalizacji.
Dzięki Geolocation API przeglądarka może teraz za pomocą wartości szerokości i długości geograficznej
podać Ci z określoną dokładnością swoje położenie na świecie. Stopień dokładności zależy od kilku czynników,
a programiści mogą mieć na niego wpływ. Czy zastanawiasz się, po co Ci jakakolwiek szerokość i długość
geograficzna, jeśli nie znasz współrzędnych geograficznych kawiarni za rogiem? Otóż aby odnajdywanie
położenia geograficznego było przydatne, metody opisywania lokalizacji muszą być uniwersalne i jednolite,
co zapewnia właśnie układ współrzędnych geograficznych. Jak dowiesz się z dalszej części tego rozdziału,
istnieją usługi, które na podstawie pobranych współrzędnych geograficznych dostarczają dodatkowych
informacji. Dzięki wykorzystaniu zbioru współrzędnych, szczególnie biorąc pod uwagę duże geograficzne
bazy danych takich dostawców jak Google, łatwo jest także wykonać odwrócone geokodowanie (patrz
wskazówka poniżej).
Odwrócone geokodowanie
Odwrócone geokodowanie — przeciwieństwo geokodowania — jest praktyką przekształcania pary
współrzędnych geograficznych na adres fizyczny. Takich informacji dostarczają różni usługodawcy
— często wykorzystywane są na przykład usługi Google Maps JavaScript API v3 (https://developers.
google.com/maps/documentation/javascript/services#ReverseGeocoding).
Kompatybilność przeglądarek
Geolocation API to wciąż nowa funkcjonalność, ma jednak tak duże możliwości, że odpowiednie definicje
API są możliwie szybko przyswajane przez najpopularniejsze przeglądarki. Tabela 10.1 pokazuje aktualny
stan obsługi API przez przeglądarki.
Android 2.1+
Chrome 9.0+
Firefox 3.5+
Internet Explorer 9.0+
iOS Safari 3.2+
Opera 10.6+
Safari 5.0+
Metoda getCurrentPosition posiada jeden wymagany parametr (nazwę funkcji zwrotnej wywoływanej
w przypadku pomyślnego uzyskania informacji o lokalizacji) oraz dwa parametry opcjonalne (funkcja
zwrotna wywoływana w przypadku błędu i obiekt z opcjami):
getCurrentPosition (udaneWywołanie [, nieudaneWywołanie] [, opcjeLokalizacji])
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
226 Rozdział 10. Wykorzystanie Geolocation API do uzyskania informacji o położeniu geograficznym
</script>
</head>
<body>
<div id='btnFindMe'>
<button id="findMe">Znajdź mnie</button>
</div>
<div id="myLocation"></div>
</body>
</html>
Ustalanie położenia geograficznego za pomocą zwykłego wywołania getCurrentPosition 227
Po kliknięciu przycisku Znajdź mnie zostanie wywołana funkcja findMe(). Pierwszym zadaniem funkcji
jest sprawdzenie, czy przeglądarka, której używasz, obsługuje Geolocation API. Zadanie to jest realizowane
za pomocą następującego kodu:
if (navigator.geolocation) {
Dane obiektu lokalizacji są podzielone na obiekt coords i obiekt timestamp w formacie DOMTimeStamp.
Wewnątrz funkcji geoSuccess możesz teraz wyświetlać różne właściwości obiektu coords, odwołując się
do każdej z nich przez główny obiekt lokalizacji. Aby na przykład pobrać szerokość geograficzną lokalizacji,
używasz position.coords.latitude. Każda przeglądarka obsługuje pola opcjonalne nieco inaczej. Rysunek
10.1 pokazuje wyniki z Chrome.
228 Rozdział 10. Wykorzystanie Geolocation API do uzyskania informacji o położeniu geograficznym
Wskazówka
W miarę korzystania z Geolocation API możesz zauważyć, że w niektórych przypadkach Twoja strona
nie zwraca żadnego wyniku lub zwraca błąd przekroczenia limitu czasu (co zobaczysz w następnym
przepisie). Zwykle będzie to rezultat błędu jednej z funkcji zwrotnych Twojego kodu. Ponieważ metody
dotyczące lokalizacji są asynchroniczne, błędy te — zależnie od przeglądarki — mogą (ale nie muszą)
przedostawać się do okna przeglądarki. Do identyfikacji problemu może się przydać wykorzystanie
w funkcjach zwrotnych debugowania za pomocą wywołań console.log().
1. W kodzie z poprzedniego przepisu zmień przycisk na Pokaż mnie na mapie, a wywoływaną funkcję
na mapMe.
2. Dodaj znacznik skryptu Google Maps JavaScript API V3 Overlay (zauważ, że w przypadku wersji 3
zestawu narzędzi Google Maps nie potrzebujesz już klucza deweloperskiego):
<script src="http://maps.google.com/maps/api/js?sensor=false">
3. Zmodyfikuj wywołanie getCurrentPosition, dodając funkcję obsługi błędów:
navigator.geolocation.getCurrentPosition(geoSuccess, geoErrorHandler);
4. Dodaj funkcję geoErrorHandler(error), która będzie obsługiwać wszelkie błędy zwracane przez
wywołanie getCurrentPosition.
5. Popraw elementy div w obrębie elementu body, tak aby odpowiadały tym z listingu 10.2, a więc aby
elementy o identyfikatorach mapCanvas i myLocation oraz element zawierający przycisk Pokaż mnie
na mapie zostały opakowane dodatkowym elementem div.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>10.2. Pokaż mnie na mapie, uwzględniając obsługę błędów</title>
<style>
#container {
width:500px;
}
#mapCanvas {
width:500px;
height:300px;
border-style:solid;
border-width:2px;
margin: 22px 0;
}
#btnMapMe {
float:left;
}
#myLocation {
float:right;
}
</style>
<script src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script>
// inicjalizuj stronę
function init() {
// dodaj procedurę nasłuchu kliknięć przycisku
var btnMapMe = document.getElementById('mapMe');
btnMapMe.addEventListener('click',mapMe,false);
}
break;
default:
errMessage += 'Nieznany błąd.';
break;
}
// inicjalizuj stronę
window.addEventListener('load',init,false);
</script>
</head>
<body>
<div id="container">
<div id="mapCanvas"></div>
<div id="btnMapMe">
<button id="mapMe">Pokaż mnie na mapie</button>
</div>
<div id="myLocation"></div>
</div>
</body>
</html>
Po kliknięciu przycisku Pokaż mnie na mapie zostanie wywołana funkcja mapMe. Tak jak poprzednio,
sprawdzamy, czy Geolocation API jest obsługiwane — jeżeli tak, wykonywana jest metoda getCurrentPosition.
Jeśli wykonanie getCurrentPosition powiedzie się, wywoływana jest funkcja geoSuccess — współrzędne
są pobierane, wyświetlane w elemencie div o identyfikatorze myLocation, a następnie wykorzystywane do
utworzenia instancji mapy Google ze znacznikiem i oknem informacyjnym. Wynik będzie podobny do tego
z rysunku 10.2, oczywiście z uwzględnieniem Twojej lokalizacji.
W wywołaniu metody getCurrentPosition dodany jest drugi parametr, o nazwie geoErrorHandler,
będący procedurą obsługi błędu. Metody dotyczące lokalizacji zwracają trzy rodzaje błędów:
PERMISSION_DENIED (1) — żądanie nie powiodło się, ponieważ użytkownik nie zgodził się
na udostępnienie informacji o lokalizacji;
POSITION_UNAVAILABLE (2) — przeglądarka nie potrafi określić lokalizacji urządzenia;
TIMEOUT (3) — zwracany, jeśli został zdefiniowany i przekroczony limit czasu oczekiwania.
232 Rozdział 10. Wykorzystanie Geolocation API do uzyskania informacji o położeniu geograficznym
Procedura obsługi błędów pozwala przechwycić zwrócony błąd i podjąć odpowiednie działanie. Częsty
błąd, PERMISSION_DENIED, wynika z tego, że użytkownik nie udostępnił informacji wymaganych przez
wywołanie Geolocation API na stronie. Aby wyświetlić ten błąd, załaduj ponownie swoją stronę, a gdy
przeglądarka poprosi Cię o zgodę na udostępnienie informacji o lokalizacji, wybierz brak udostępnienia
lub nie pozwól na dostęp do Twojego położenia geograficznego. Procedura obsługi błędów geoErrorHandler
zostanie wywołana z przekazanym jej obiektem błędu lokalizacji. Obiekt błędu lokalizacji, w naszym kodzie
nazwany error, będzie zawierać dwa atrybuty: code i message. Opisany wcześniej kod jest numeryczną stałą,
która definiuje typ błędu, natomiast komunikat może zawierać skierowaną do Ciebie jako programisty
opcjonalną wiadomość tekstową wyjaśniającą przyczynę błędu. W omawianym przypadku — ze względu
na zablokowanie dostępu do informacji o lokalizacji — zostanie przekazany kod błędu PERMISSION_DENIED,
a Ty możesz wyświetlić własny komunikat.
Uwaga
Mozilla dodaje do możliwych kodów błędów czwartą wartość — UNKNOWN_ERROR (0). Błąd ten jest
zwracany, gdy pobieranie lokalizacji nie powiodło się z nieznanego, nieobjętego innymi kodami
błędów powodu. Polecenie switch na listingu 10.2 przechwyci domyślnie każdy nieznany błąd, w tym
UNKNOWN_ERROR zwracany przez Mozillę. W tym przypadku atrybut message obiektu błędu może być bardziej
przydatny dla określenia przyczyny błędu.
W tym przepisie wykorzystujemy Google Maps JavaScript API V3 Overlay do pokazania użytkownikowi
jego położenia za pomocą znacznika. Otwieramy również przypisane do tego znacznika okno informacyjne,
wyświetlające jego współrzędne geograficzne. Mozilla udostępnia dodatkowy atrybut położenia, address,
którego nie ma w specyfikacji W3C. Udostępnia on fizyczne położenie współrzędnych. Adres ten może
oczywiście być niedokładny, gdyż margines błędu lokalizacji może być zbyt duży, ale kiedy jest dostępny,
pozwala ominąć konieczność skorzystania z innej usługi w celu odwróconego geokodowania współrzędnych.
Jeżeli obiekt adresu jest dostępny, możesz wyciągnąć z niego atrybuty miasta i regionu oraz dopisać
odpowiednie wartości do okienka informacyjnego. Oto atrybuty obiektu adresowego:
city — DOMString zawierający miasto;
country — DOMString zawierający kraj;
Określanie odległości za pomocą opcji lokalizacji 233
Jeśli obiekt adresu nie jest dostępny, możesz użyć dostarczanej przez Google usługi odwróconego
geokodowania, co zobaczysz w następnym przepisie, w którym przyjrzysz się trzeciemu parametrowi
metody getCurrentPosition i możliwościom jego wykorzystania w zależności od konkretnych potrzeb.
Uwaga
W przypadku opcji timeout do limitu czasu nie jest wliczany czas oczekiwania na zezwolenie
użytkownika na dostęp do informacji o lokalizacji, gdy został wyświetlony odpowiedni panel
lub okno dialogowe. Liczony jest wyłącznie czas wykonania właściwego wywołania.
Po wczytaniu strony przeglądarka wywoła funkcję setLocation, która z kolei wywoła metodę
getCurrentPosition z zestawem opcji. Po wywołaniu funkcji zwrotnej wykorzystamy współrzędne
geograficzne do utworzenia instancji mapy Google, wykonania odwróconego geokodowania i obliczenia
odległości do różnych miast.
1. Dodaj wywołanie metody setLocation do atrybutu onload elementu body oraz funkcję
setLocation, przekazując w wywołaniu getCurrentPosition obiekt opcji lokalizacji.
2. Zaktualizuj znacznik skryptu Google Maps JavaScript API V3 Overlay, aby załadować bibliotekę
geometry, wykorzystywaną do obliczenia odległości:
<script src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry">
3. Dodaj funkcję reverseGeoCode, która przyjmuje współrzędne geograficzne i pobiera informacje
adresowe z geokodera Google.
4. Dodaj funkcję calculateDistance, która używa metody computeDistanceBetween do obliczenia
odległości do Warszawy, Gdańska i Krakowa.
5. Popraw elementy div w obrębie elementu body, tak aby odpowiadały tym z listingu 10.3, a więc
aby w kontenerze div znalazły się: element div o identyfikatorze mapCanvas, informacje o położeniu
geograficznym oraz elementy div z odległościami do miast.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>10.3. Oblicz odległości</title>
<style>
#container {
width:500px;
}
#mapCanvas {
width:500px;
height:300px;
border-style:solid;
border-width:2px;
margin: 22px 0;
}
#location {
float:right;
text-align:right;
}
.numDistance {
text-align:right;
Określanie odległości za pomocą opcji lokalizacji 235
}
</style>
<script src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry"></script>
<script>
// zmienna globalna
var map;
// aktualizuj status
document.getElementById('geoStatus').innerHTML = 'Uzyskano informację o położeniu';
}
// wykonaj geokodowanie
geocoder.geocode({'latLng': geoLatLng}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
// sprawdź, czy otrzymaliśmy adres
if (results[0]) {
// utwórz znacznik na mapie
var marker = new google.maps.Marker({
position: geoLatLng,
map: map
});
// ustaw adres jako treść i otwórz okno
infowindow.setContent(results[0].formatted_address);
infowindow.open(map, marker);
}
236 Rozdział 10. Wykorzystanie Geolocation API do uzyskania informacji o położeniu geograficznym
} else {
alert('Geokodowanie nie powiodło się z powodu: ' + status);
}
});
}
// obliczenia odległości
var distFromWAR = gmapsSpherLib.computeDistanceBetween(disLatLng,WARLatLng,conEarth).toFixed(2);
var distFromWRO = gmapsSpherLib.computeDistanceBetween(disLatLng,WROLatLng,conEarth).toFixed(2);
var distFromKRA = gmapsSpherLib.computeDistanceBetween(disLatLng,KRALatLng,conEarth).toFixed(2);
} else {
// Geolocation API nie jest obsługiwane
divStatus.innerHTML = 'Geolocation API nie jest obsługiwane';
}
}
</script>
</head>
<body>
<div id="container">
<div id="mapCanvas"></div>
<div id="location">
<table id="status">
<tr>
<td colspan="2"><div id="geoStatus"></div></td>
</tr>
<tr>
<td>Szerokość geograficzna:</td>
<td class="numDistance"><div id="myPosLat"></div></td>
</tr>
<tr>
<td>Długość geograficzna:</td>
<td class="numDistance"><div id="myPosLng"></div></td>
</tr>
</table>
</div>
<div id="distance">
<table id="cityDistance">
<tr>
<td>Warszawa:</td>
<td class="numDistance"><div id="divDistFromWAR"></div></td>
</tr>
<tr>
<td>Wrocław:</td>
<td class="numDistance"><div id="divDistFromWRO"></div></td>
</tr>
<tr>
<td>Kraków:</td>
<td class="numDistance"><div id="divDistFromKRA"></div></td>
</tr>
</table>
</div>
</div>
</body>
</html>
238 Rozdział 10. Wykorzystanie Geolocation API do uzyskania informacji o położeniu geograficznym
Jak widzisz, możesz sterować pobieraniem informacji o położeniu geograficznym poprzez ustawienia
obiektu opcji lokalizacji w wywołaniu getCurrentPosition. Opcje pozwalają Ci wpływać na dokładność
i wydajność poprzez dostosowanie Twojej aplikacji do wymagań użytkownika. Do tego momentu
zajmowaliśmy się mapami, odwróconym geokodowaniem, a nawet obliczaniem odległości, ale prawdopodobnie
przyszła Ci do głowy myśl o urządzeniach przenośnych i aktualizowaniu informacji prezentowanych
użytkownikowi wraz ze zmianą jego położenia geograficznego. Cóż, Geolocation API umożliwia realizację
także tego zadania, o czym przekonasz się w kolejnym przepisie.
Podążanie za poruszającym się obiektem dzięki watchPosition 239
clearWatch (watchId)
Sprawdzanie odległości od ostatniego punktu pozwala Ci utrzymywać dość przejrzysty wygląd mapy,
w miarę jak dana osoba porusza się ze swoim urządzeniem przenośnym. Jeśli jednak miałbyś do czynienia
z mniejszym obszarem ruchu, takim jak centrum miasta, różnica jednej czwartej kilometra może być zbyt
duża; jeśli z kolei poruszałbyś się szybkim środkiem transportu, jedna czwarta kilometra może nie być
dostatecznie dużą wielkością. Sprawdzanie różnicy powinno się zatem opierać na konkretnych potrzebach.
W tym przepisie nauczysz się, jak w miarę zmian położenia geograficznego filtrować kolejne punkty
reprezentujące lokalizację, wykorzystując obliczenia odległości między punktami.
1. Utwórz stronę HTML z przyciskami Rozpocznij i Zakończ, jak pokazano na listingu 10.4.
2. Dodaj skrypt dla Google Maps JavaScript API V3 Overlay z uwzględnieniem biblioteki geometry
oraz zmienne globalne, które będą przechowywać identyfikator obserwacji, mapę, linię łamaną
i ostatnie wartości szerokości oraz długości geograficznej.
3. Dodaj w znaczniku script funkcję initMap i ustaw zdarzenie load, aby ją uruchomić.
4. Dodaj funkcje startWatch i clearWatch.
5. Dodaj funkcje successCallback i errorCallback.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" />
<title>10.4. Pozostawianie śladu</title>
<style>
#container {
width:300px;
}
#mapCanvas {
width:300px;
height:200px;
border-style:solid;
border-width:2px;
margin: 22px 0;
}
#btnMap {
float:left;
}
#location {
float:right;
}
.numDistance {
text-align:right;
}
</style>
<script type="text/javascript"
´src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry"></script>
<script>
Podążanie za poruszającym się obiektem dzięki watchPosition 241
// zadeklaruj zmienne
var watchId; // identyfikator procesu śledzenia położenia geograficznego
var map; // mapa
var poly; // linia łamana ścieżki
var lastLatLng; // ostatnie wartości szerokości i długości geograficznych
// inicjalizuj mapę
function initMap() {
// aktualizuj ekran
document.getElementById('myPosLat').innerHTML = posLat.toFixed(8);
document.getElementById('myPosLng').innerHTML = posLng.toFixed(8);
document.getElementById('watchStatus').innerHTML = 'Zaktualizowane położenie (#' + path.getLength() + ')';
}
}
// aktualizuj status
divWatchStatus.innerHTML = errMessage;
}
// rozpocznij obserwację
watchId = navigator.geolocation.watchPosition(successCallback,
errorCallback,
posOptions);
// aktualizuj status
divWatchStatus.innerHTML = 'Obserwacja położenia ('+watchId+')';
}
} else {
// aktualizuj status mówiący, że uzyskiwanie informacji o położeniu geograficznym nie jest obsługiwane
divWatchStatus.innerHTML = 'Uzyskiwanie informacji o położeniu geograficznym nie jest obsługiwane';
}
}
// aktualizuj status
document.getElementById('watchStatus').innerHTML = 'Wyłączona';
}
}
// inicjalizuj stronę
window.addEventListener('load',initMap,false);
</script>
</head>
<body>
<div id="container">
<div id="mapCanvas"></div>
<div id="btnMap">
<button id="startWatch">Rozpocznij</button>
<br>
<button id="stopWatch">Zakończ</button>
</div>
<div id="location">
<table id="status">
<tr>
<td>Szerokość:</td>
<td class="numDistance"><div id="myPosLat"></div></td>
</tr>
<tr>
244 Rozdział 10. Wykorzystanie Geolocation API do uzyskania informacji o położeniu geograficznym
<td>Długość:</td>
<td class="numDistance"><div id="myPosLng"></div></td>
</tr>
<tr>
<td colspan="2"><div id="watchStatus"></div></td>
</tr>
</table>
</div>
</div>
</body>
</html>
Przyjrzyjmy się listingowi 10.4 nieco dokładniej. Po wczytaniu strony jest wywoływana funkcja initMap,
która inicjalizuje mapę na stronie. Wybraliśmy współrzędne w Poznaniu, ale mógłbyś ustawić inny
początkowy punkt środka mapy lub załadować mapę dopiero wtedy, gdy obserwacja się rozpocznie.
W initMap tworzysz warstwę linii łamanej nad mapą, co pozwoli Ci połączyć współrzędne z obserwacji
i wyświetlić „ścieżkę” poruszania się obserwatora. Kiedy mapa jest gotowa, możesz rozpocząć obserwację
położenia geograficznego.
Aby rozpocząć, należy kliknąć lub dotknąć przycisk Rozpocznij. Jak zwykle powinieneś zostać poproszony
o zgodę na udostępnienie informacji o Twojej lokalizacji. Po udzieleniu zezwolenia zostanie wywołana
funkcja startWatch, za pomocą której poprzez sprawdzenie zmiennej globalnej identyfikatora obserwacji
upewnisz się, że proces obserwacji nie został rozpoczęty wcześniej — zapobiega to uruchomieniu więcej niż
jednego takiego procesu. Następnie są ustawiane opcje lokalizacji i wywoływana jest metoda watchPosition.
Identyfikator obserwacji zwracany przez watchPosition jest następnie zapisywany w zmiennej watchId, tak
aby można jej było użyć później do zatrzymania procesu za pomocą clearWatch.
Kiedy watchPosition zwraca obiekt lokalizacji, pobierane są z niego właściwości szerokości
i długości geograficznej, tworzony jest reprezentujący je obiekt, a następnie za pomocą metody Google
computeDistanceBetween jest obliczana odległość od ostatnich znanych współrzędnych geograficznych.
W tym przypadku przekazujesz metodzie nowe współrzędne, ostatnie współrzędne i stałą wartość,
informującą funkcję, że chcesz uzyskać wynik w kilometrach. Następnie sprawdzamy, czy odległość jest
większa niż ćwierć kilometra, tak aby nie zalać mapy znacznikami. Jeśli następny punkt jest położony
w większej odległości, dodajemy nowy znacznik, a do tablicy punktów linii łamanej ścieżki dopisujemy
nowe współrzędne. Dopisanie współrzędnych do tablicy aktualizuje linię na mapie, więc z każdym nowym
punktem ścieżka będzie rosła, pokazując, gdzie byłeś, jak zaprezentowano na rysunku 10.4.
Ten przepis pokazuje, w jaki sposób uzyskać dostęp do informacji o położeniu geograficznym
przeglądarki i być powiadamianym o każdej jego zmianie. Informacja ta może być użyta w wielu
aplikacjach, wliczając integrację z bazami danych usług opartych o lokalizację, takimi jak Yahoo Query
Language i Miejsca Google, pokazujących znajdujące się w pobliżu atrakcje.
Uwaga
Podczas pracy z urządzeniami, szczególnie przenośnymi, i pozyskiwaniem informacji o położeniu
geograficznym warto pamiętać o tym, że pobieranie informacji o urządzeniu przyspiesza zużycie
jego baterii. W celu zminimalizowania tego zużycia warto zaprojektować aplikację tak, aby pobierać
informacje tylko w razie potrzeby.
Podsumowanie 245
Podsumowanie
Geolocation API udostępnia prosty interfejs do dodawania do witryn i aplikacji funkcji związanych
z miejscem położenia i wykorzystujących informacje o nim. Oto niektóre z rozwiązań, które mogą zostać
zaprojektowane:
wyświetlanie informacji związanych z lokalizacją,
wykorzystanie informacji o przebywaniu w pobliżu czegoś lub kogoś,
dynamiczne dostosowywanie ustawień lokalnych, takich jak język i waluta,
integracja z mapami i trasami,
oznaczanie danych, zdjęć i innych rzeczy informacjami o lokalizacji.
Bezpieczeństwo danych
Jeśli mówimy o przechowywaniu danych, musimy również rozważyć kwestię ich bezpieczeństwa. Tak jak
w przypadku przechowywania informacji w bazie danych serwera aplikacji internetowej, do przechowywania
w bazie danych po stronie klienta powinny być stosowane odpowiednie reguły dotyczące bezpieczeństwa.
To szczególnie istotne, ponieważ w odróżnieniu od serwera, na którym możesz kontrolować zapory ogniowe,
użytkowników, hasła czy inne aspekty związane z bezpieczeństwem, przeglądarka użytkownika znajduje
się poza kontrolowaną przez Ciebie siecią. A zatem kwestia tego, co i w jaki sposób jest przechowywane
w przeglądarce klienta, staje się o wiele ważniejsza. W zależności od danych, które zapisujesz, możesz wziąć
pod uwagę szyfrowanie, ale jeśli rozważasz szyfrowanie danych, warto w pierwszej kolejności ponownie
przyjrzeć się ich przechowywaniu.
Funkcje przechowywania danych w HTML5 realizują „opartą o pochodzenie” politykę bezpieczeństwa
poprzez ograniczanie dostępu do danych zapisanych sesyjnie, lokalnie i w bazie danych po stronie klienta
do stron pochodzących z tej samej domeny, w której zapisano dane. Dzięki temu strony z innych witryn
lub aplikacji nie mogą uzyskać dostępu do danych. Ponieważ implementacja bezpieczeństwa w oparciu
o „pochodzenie” łączy się z wykorzystaniem domeny strony jako czynnika decydującego o dostępie do sesyjnie
i lokalnie składowanych list par klucz-wartość, istnieje potencjalna możliwość narażenia przechowywanych
danych na działanie wbudowanych skryptów. Istotna jest zatem znajomość działań podejmowanych przez
zewnętrzne pliki, których używasz na swoich stronach.
Uwaga
Dane, które przechowujesz przy użyciu obiektów lokalnych, sesyjnych albo bazodanowych, są ograniczone
do konkretnej przeglądarki używanej w danej chwili przez odwiedzającego. Jeśli użytkownik powróci do
Twojej witryny lub aplikacji za pośrednictwem innej przeglądarki albo innego komputera, to przechowywane
dane nie będą dostępne. Aby dane były ogólnie dostępne, musiałbyś przechowywać je na swoim serwerze
bazy danych lub w chmurze.
mechanizmu przechowywania danych w bazie danych przez różne przeglądarki. Tabela 11.2 przedstawia
możliwości przeglądarek w zakresie przechowywania obiektów danych po stronie klienta.
Android 2.1+
Chrome 10.0+
Firefox 10.6+
Internet Explorer 8.0+
iOS Safari 3.2+
Opera 10.6+
Safari 4.0+
Aby ustawić element, wywołaj setItem i podaj klucz oraz wartość, które należy ustawić:
setItem(klucz, wartość)
Oto parametry:
klucz — klucz, pod jakim należy zapisać wartość mającą postać tekstu,
wartość — wartość, która ma być zapisana.
Wartość jest ciągiem znaków, więc jeśli zapisujesz liczbę, będzie ona traktowana w pamięci jak tekst i po
pobraniu należy ją przekształcić z powrotem do właściwego typu danych. Klucz i wartość zostaną następnie
zapisane w przypadkowej kolejności na liście par klucz-wartość w pamięci przeglądarki dla domeny, z której
strona została załadowana.
250 Rozdział 11. Przechowywanie danych po stronie klienta
Jeśli zapiszesz element z kluczem, który już istnieje, wartość dla tego klucza będzie zaktualizowana nowo
podaną wartością. A zatem metoda setItem działa zarówno jako metoda tworząca, jak i aktualizująca. Aby
zapisać w magazynie sesji wartość book pod kluczem source, mógłbyś wykonać w JavaScripcie następujące
polecenie:
sessionStorage.setItem('source', 'book');
Metoda setItem nie zwraca wartości, więc żeby ochronić się przed ewentualnymi błędami zapisu, możesz
zamknąć wywołanie w bloku try-catch.
Aby pobrać z pamięci sesji albo z pamięci lokalnej wartość dla klucza, wywołaj metodę getItem z kluczem,
który Cię interesuje. Zwrócona wartość będzie miała postać tekstu, którego możesz następnie użyć w swoim
skrypcie:
string getItem(klucz)
Klucz jest tutaj ciągiem znakowym, dla którego jest pobierana odpowiednia wartość.
Aby pobrać wartość zapisaną w poprzednim przykładzie, odwołaj się do odpowiadającej kluczowi source
wartości z pamięci sesji, wykonując następujące polecenie:
textSource = sessionStorage.getItem('source');
Ponieważ zarówno pamięć sesyjna, jak i lokalna są oparte o obiekty przechowywania danych, w celu
odwołania się do przechowywanych wartości kluczy możesz również wykorzystać notację obiektową z kropką,
używając klucza jako nazwy właściwości, tak jak poniżej:
textSource = sessionStorage.source;
Teraz, po omówieniu metod get i set pamięci sesyjnej i lokalnej, przyjrzyjmy się działaniu podstawowych
przepisów. Zaczniemy od przepisów dotyczących obiektu sesji, a następnie przejdziemy do przechowywania
lokalnego.
Wskazówka
W przeglądarkach takich jak Firefox użytkownik może wyłączyć przechowywanie danych. Aby upewnić
się, że przechowywanie danych jest dostępne, Twój kod powinien zapisać, a następnie spróbować
pobrać jakąś wartość. Jeśli wartość ta nie może zostać odzyskana, to przechowywanie danych jest
być może w danej przeglądarce wyłączone.
Listing 11.1. Wyświetlanie liczby wizyt na stronie przy użyciu sesyjnego przechowywania danych
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>11.1. Wizyty na stronie z sesyjnym przechowywaniem danych</title>
<script>
function init() {
// zwiększ visits
visits++;
} else {
// wartość domyślna dla pierwszej wizyty
visits = 1;
}
} else {
// sessionStorage nie jest dostępne
divVisits.innerHTML = 'sessionStorage nie jest dostępne';
}
}
</script>
</head>
<body>
<div id="divVisits"></div>
</body>
</html>
Kiedy załadujesz stronę utworzoną na listingu 11.1 w swojej przeglądarce, zostanie uruchomiona funkcja
init. Funkcja ta sprawdzi najpierw za pomocą window.sessionStorage, czy obiekt sessionStorage jest
dostępny w oknie. Jeśli obiekt sessionStorage nie jest dostępny, funkcja wyświetli komunikat w Twoim
elemencie div na stronie.
252 Rozdział 11. Przechowywanie danych po stronie klienta
Jeżeli obiekt sessionStorage jest dostępny, kod w JavaScripcie sprawdza następnie, czy klucz visits
istnieje, używając w tym celu notacji z kropką: sessionStorage.visits. Jeśli klucz visits istnieje, zostanie
zwrócona odpowiednia wartość, a sprawdzenie wypadnie pomyślnie. W przeciwnym razie jest to pierwsze
sprawdzenie w danej sesji i rozpoczynasz liczenie visits od wartości 1. Jeśli klucz jest przechowywany
w sesji, w celu zaprezentowania metody służącej do pobierania wartości możesz pobrać wartość dla klucza
metodą getItem. Kiedy wartość zostanie pobrana, za pomocą metody parseInt przekształć visits z wartości
tekstowej na liczbę całkowitą. Tę konwersję należy wykonać, ponieważ w pamięci przeglądarki wszystkie
wartości są przechowywane jako zwykłe ciągi znaków. Jeśli Twój skrypt zapisuje, a następnie pobiera inne
typy danych, będziesz musiał przekształcić te ciągi na ich właściwe typy.
Niezależnie od tego, czy pobrałeś liczbę odwiedzin i zwiększyłeś ją o 1, czy też ustawiłeś początkową
wartość na 1, skrypt aktualizuje następnie za pomocą metody setItem klucz visits w pamięci sesji. Jeśli
klucz nie znajdował się wcześniej w pamięci, metoda setItem doda parę klucz-wartość, w przeciwnym
wypadku wartość zostanie zastąpiona nową liczbą odwiedzin.
Rysunek 11.1. Podgląd par klucz-wartość pamięci sesji w narzędziach dla programistów przeglądarki Chrome
Na rysunku 11.1 domena, która zapisała klucze, jest pokazana po lewej stronie, pod zakładką Session
Storage (pamięć sesji), a po prawej stronie znajdują się pary klucz-wartość, które są aktualnie przechowywane.
W tym przypadku bieżąca wartość dla klucza visits to 2, a JavaScript pokazał to użytkownikowi. Narzędzia
dla programistów przeglądarki Chrome są przydatne przy pracy z pamięcią sesji i pamięcią lokalną, ponieważ
pozwalają nie tylko potwierdzić prawidłowe działanie skryptu, ale również dodawać, aktualizować i usuwać
Nadawanie stylów z pamięci sesji 253
pary klucz-wartość za pomocą interfejsu użytkownika. W obecnej wersji narzędzi dla programistów po zmianie
pary klucz-wartość należy odświeżyć panel widoku key/value (klucz/wartość) pamięci za pomocą położonego
u dołu przycisku Refresh (odśwież).
Wskazówka
Każdy obiekt, który ma wbudowaną metodę toString, może być zapisany w polu wartości pary
klucz-wartość pamięci sesji i pamięci lokalnej. Bardziej złożone obiekty o strukturze JSON mogą
być zapisane za pomocą metod JSON.stringify(twójObiekt) oraz JSON.parse(zwróconyCiąg),
przekształcających obiekt JSON na ciąg znaków, a pobrany ciąg znaków z powrotem na obiekt.
W tym przepisie w bardzo prosty sposób ustawialiśmy i pobieraliśmy pary klucz-wartość w pamięci sesji.
W kolejnym zajmiemy się obsługą błędów i dostępnością pamięci sesji pomiędzy stronami.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>11.2. Wybór motywu - strona 1</title>
<style>
254 Rozdział 11. Przechowywanie danych po stronie klienta
#themeContent {
background-color:#FFF;
border-style:solid;
border-width:2px;
}
#themeSettings {
margin:10px;
}
</style>
<script>
applyTheme(themeColor);
}
} else {
themeDiv.innerHTML = 'sessionStorage nie jest obsługiwane.';
}
}
// wyczyść wyświetlanie
document.getElementById('default').selected = true;
document.body.style.backgroundColor = '';
themeDiv.innerHTML = 'Motyw wyczyszczony.';
}
// inicjalizuj okno
window.addEventListener('load',initTheme,false);
</script>
</head>
<body>
<div id="themeContent">
<div id="themeSettings">
<H1>Strona 1</H1>
Wybierz motyw:
<select id="themeColor">
<option id="default" value="">Wybierz kolor...</option>
<option id="blue" value="blue">niebieski</option>
<option id="red" value="red">czerwony</option>
<option id="yellow" value="yellow">żółty</option>
<option id="green" value="green">zielony</option>
</select>
<button id="resetTheme">Resetuj motyw</button><br><br>
<div id="theme"></div><br><br>
<a href="listing.11.3.html">Przejdź do strony 2</a>
</div>
</div>
</body>
</html>
Po ustawieniu motywu na stronie 1 (listing 11.2) możesz sprawdzić, czy został on zapisany, ładując inną
stronę, która odczyta zawartość pamięci i użyje odczytanej informacji do ustawienia właściwego motywu.
Wykonaj poniższe kroki i za pomocą listingu 11.3 utwórz drugą stronę, służącą do pokazania koloru motywu
wybranego wcześniej:
1. W oparciu o listing 11.3 utwórz nową stronę o podobnej strukturze.
2. Zmodyfikuj znaczniki a na obu stronach, aby odpowiadały nazwom Twoich stron i abyś dzięki
temu mógł się poruszać pomiędzy stronami.
256 Rozdział 11. Przechowywanie danych po stronie klienta
<!DOCTYPE html>
<html><head>
<meta charset="UTF-8" />
<title>11.3. Pokazywanie motywu - strona 2</title>
<style>
#themeContent {
background-color:#FFF;
border-style:solid;
border-width:2px;
}
#themeSettings {
margin:10px;
}
</style>
<script>
// inicjalizuj stronę
function init() {
document.body.style.backgroundColor = themeColor;
// inicjalizuj stronę
window.addEventListener('load',init,false);
</script>
</head>
<body>
<div id="themeContent">
<div id="themeSettings">
<H1>Strona 2</H1>
<div id="theme"></div>
<br><br>
<a href="listing.11.2.html">Przejdź do strony 1</a>
</div>
</div>
</body>
</html>
Po załadowaniu strony 1 w Twojej przeglądarce pobiera ona z pamięci sesji aktualnie zapisany kolor
motywu. Kiedy strona jest uruchamiana po raz pierwszy, w pamięci nie ma motywu i zwracana jest pusta
wartość, więc motyw przyjmuje wartość domyślną przeglądarki. Przy kolejnych ładowaniach strony
pobierany jest kolor ustawiany jako kolor tła oraz jako wybrana wartość w kontrolce wyboru koloru.
Zapisywanie formularzy za pomocą lokalnego przechowywania danych 257
Uwaga
Aby wykonać przepisy korzystające z sesyjnego przechowywania danych, będziesz musiał załadować
strony na serwer, zamiast uruchamiać je w „trybie lokalnym”. Powodem jest to, że w trybie lokalnym
niektóre przeglądarki generowałyby błąd mówiący, że operacja nie jest obsługiwana. Na przykład
w Firefoxie zostałby wygenerowany błąd o kodzie 9: „Operation is not supported”.
Po załadowaniu strony odwiedzający może wybrać z listy rozwijanej opcji inny kolor, co spowoduje
zmianę koloru tła strony i zapisanie tego koloru w pamięci sesji pod kluczem themeColor. W skrypcie to
zapisujące ustawienia wywołanie setItem zostało opakowane w blok try-catch. Jeśli zostanie on aktywowany
z powodu ograniczeń rozmiaru, kod poinformuje odwiedzającego, że pamięć sesji jest pełna, ale nadal
będziesz mógł podjąć odpowiednie do potrzeb działania. Po ustawieniu koloru motywu użytkownik może
kliknąć łącze Przejdź do strony 2 i w ten sposób załadować drugą stronę. Jako że druga strona jest w tej samej
sesji co pierwsza, ona również będzie mieć dostęp do zapisanej na stronie 1 pary klucz-wartość z pamięci
sesji. Druga strona pobierze następnie odpowiednią wartość i ustawi tło na zapisany kolor.
Na listingu 11.2 dałeś odwiedzającemu również możliwość wyczyszczenia motywu. W tym przypadku
przepis usuwa parę klucz-wartość z pamięci sesji za pomocą metody removeItem. Przepis może również
aktualizować klucz, wywołując setItem z pustym ciągiem znaków.
Na początku tego rozdziału opisaliśmy, jak sesyjne przechowywanie danych pozwala Ci przechowywać
dane tylko dla konkretnej sesji. Jeśli załadujesz stronę utworzoną na listingu 11.2 do dwóch zakładek lub
okien w tej samej przeglądarce, a następnie wybierzesz różne kolory, każda zakładka będzie miała swój
własny kolor tła, ponieważ z każdą zakładką lub oknem związana jest osobna sesja. Aby wartości pozostały
dostępne w różnych oknach albo zakładkach przeglądarki, a nawet po zamknięciu i ponownym jej otwarciu,
musisz użyć obiektu lokalnego przechowywania danych. Przyjrzymy mu się w następnym przepisie.
W tym przepisie są wykorzystywane dwa nowe pojęcia związane z pamięcią sesji i lokalną: key oraz length.
Metoda key pozwala pobrać nazwę przechowywanego klucza przez dostarczenie jej wartości indeksu:
DOMString key(indeks)
Indeks jest tutaj indeksem pary klucz-wartość, dla której należy zwrócić wartość tekstową klucza.
Z kolei właściwość length pamięci sesji i pamięci lokalnej zwraca liczbę par klucz-wartość w pamięci.
Aby to uzyskać, użyj poniższego wywołania:
var numItems = localStorage.length;
W niniejszym przepisie użyjesz zarówno metody key, jak i właściwości length do przejścia w pętli przez
pary klucz-wartość localStorage. Wykonajmy następujące kroki, aby rozpocząć pracę z listingiem 11.4:
1. Utwórz pusty plik HTML i dodaj znaczniki HTML z listingu 11.4, zawierającego formularz,
którego będziesz używał, oraz jego pola. Dla uproszczenia użyliśmy w polach formularza atrybutu
onchange, ale mógłbyś go zastąpić procedurami nasłuchu zdarzeń dla każdego z pól.
2. Dodaj w sekcji head znaczniki script zawierające funkcję window.addEventListener,
która po załadowaniu strony uruchamia funkcję checkStorage.
3. Dodaj w skrypcie funkcję checkStorage, jak pokazano na listingu 11.4.
4. Dodaj w skrypcie funkcję changeField, która będzie obsługiwać zmiany pól formularza.
5. Załaduj plik w przeglądarce Chrome z narzędziami dla programistów otwartymi w obszarze
pamięci lokalnej i wpisz informacje w polach.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>11.4. Przechowywanie danych formularza w pamięci lokalnej</title>
<script>
// pobierz klucz
key = localStorage.key(i);
value = unescape(localStorage.getItem(key));
} else {
alert('localStorage nie jest obsługiwane.');
}
}
</script>
</head>
<body>
<h1>Mój formularz</h1>
<form id='myForm'>
<table>
<tr>
<td>Imię:</td>
<td><input type="text" id="firstName" onchange="changeField(this);" /></td>
</tr>
<tr>
<td>Nazwisko:</td>
<td><input type="text" id="lastName" onchange="changeField(this);" /></td>
</tr>
<tr>
<td>E-mail:</td>
<td><input type="email" id="email" onchange="changeField(this);" /></td>
</tr>
<tr>
260 Rozdział 11. Przechowywanie danych po stronie klienta
<td>Telefon:</td>
<td><input type="tel" id="phone" onchange="changeField(this);" /></td>
</tr>
</table>
</form>
</body>
</html>
Po wpisaniu w polu firstName imienia i przejściu za pomocą klawisza Tab do następnego pola przeglądarka
uruchomi zdarzenie onchange dla pola firstName i w konsekwencji wykona funkcję changeField z polem
firstName formularza przekazanym jako parametr. Funkcja changeField zapisze wtedy dane pola w parze
klucz-wartość w localStorage. Funkcja używa w parze klucz-wartość identyfikatora pola formularza jako
klucza, a wartości pola jako wartości. Jeśli zatem masz otwarte narzędzia dla programistów w Chrome,
powinieneś zobaczyć klucz firstName z wartością tekstu, który wpisałeś.
Aby poznać rzeczywiste możliwości localStorage, zamknij kartę lub okno przeglądarki, a następnie otwórz
je ponownie na tej samej stronie formularza. Po ponownym otwarciu i załadowaniu strony przeglądarka
wykona funkcję checkStorage. Funkcja ta ma za zadanie sprawdzić, czy zapisałeś wcześniej w pamięci
lokalnej jakieś informacje z formularza, i pobrać te informacje. Funkcja pobiera każdą parę klucz-wartość
poprzez przejście w pętli przez listę localStorage, sprawdzenie, czy istnieje identyfikator pola formularza,
który odpowiada nazwie klucza, oraz, jeśli taki istnieje, ustawienie wartości pola na pobraną wartość.
Dla zobrazowania możliwości localStorage korzystamy w tym przepisie z prostych pól tekstowych,
moglibyśmy jednak wykorzystać również inne rodzaje pól wejściowych formularza. Aby obsługiwać
różne działania dla różnych rodzajów pól, takich jak lista wyboru lub przycisk opcji, funkcje changeField
i checkStorage należałoby zaktualizować, ale wartość mogłaby być przechowywana w pamięci lokalnej,
tak jak zwykłe tekstowe dane wejściowe.
Kiedy strona przestaje korzystać z przechowywanych informacji formularza, a formularz zostaje
ostatecznie przesłany do serwera, powinieneś usunąć zawartość pól. Obok metody removeItem, którą
poznałeś wcześniej, API przechowywania danych udostępnia metodę służącą do czyszczenia całej pamięci
sesji lub pamięci lokalnej — clear. Metoda clear nie przyjmuje parametrów ani nie zwraca żadnego wyniku
— „czyści” z listy wszystkie pary klucz-wartość dla domeny strony. Oto przykład jej wywołania:
localStorage.clear();
Wykonanie tego wiersza usunie z lokalnej pamięci wszystkie pary klucz-wartość, bez względu na to,
która strona je dodała i kiedy zostały dodane. Ponieważ jest to metoda dość radykalna, powinieneś być
absolutnie pewny, że chcesz wyczyścić zawartość pamięci sesji albo pamięci lokalnej.
Z dotychczasowych przepisów dowiedziałeś się o podstawach dodawania, aktualizowania i usuwania
danych zapisanych dla witryny na komputerze klienta. Zakłada to, że w danym momencie odwiedzający
używa do interakcji z witryną jednej strony. Jeśli przechowywana informacja została zmieniona z innej
strony w przeglądarce, nie miałeś żadnego sposobu, aby automatycznie uwzględnić tę zmianę na wcześniej
otwartej stronie. API przechowywania danych po stronie klienta udostępnia zdarzenia aktualizacji pamięci
sesji i pamięci lokalnej, które możesz przechwycić i uwzględnić na otwartej stronie. Kolejny przepis łączy
opisane wcześniej metody ze zdarzeniami aktualizacji pamięci w celu odświeżenia zawartości Twojej strony.
Przechwytywanie zdarzeń w pamięci lokalnej 261
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>11.5. Przechowywanie notatek w pamięci lokalnej</title>
<style>
* {margin: 0; padding: 0;}
body {padding: 20px;}
h1 {font-size: 120%; margin: 0 0 .5em;}
section {width: 300px;}
#noteBoard, textarea {
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
-moz-box-shadow: 0px 0px 4px rgba(0,0,0,.4);
-webkit-box-shadow: 0px 0px 4px rgba(0,0,0,.4);
box-shadow: 0px 0px 4px rgba(0,0,0,.4);}
#noteBoard {
background: #FCFABA;
float: right;
padding: 10px 20px;}
#noteBoard div {
border-bottom: 1px dashed #CCC;
margin: 0 0 5px;
padding: 5px 0;
width: 100%;}
#noteBoard div.buttons {border: none;}
#addNote, #updateNote {float: left;}
#addNote {
border-right: 1px dashed #ccc;
margin: 0 50px 0 0;
padding: 0 50px 0 0;}
#updateNote { display:none;}
textarea {
border: none;
clear: both;
height: 150px;
margin: 0 0 10px;
padding: 10px;
width: 280px;}
input[type="text"] {margin: 0 0 10px; padding: 4px; }
button {padding: 5px;}
</style>
<script>
btnUpdateNote.addEventListener('click',updateNote,false);
// zapisz notatkę
localStorage.setItem(noteKey, noteValue);
// wyświetl notatki
function updateNoteBoard() {
// ustaw obszar wyświetlania
var noteBoard = document.getElementById('noteBoard');
// pobierz klucz
value = localStorage.getItem(key);
</script>
</head>
<body>
<h1>Plansza z notatkami</h1>
<section id="noteBoard"></section>
<section id="addNote">
<h1>Tutaj dodaj nową notatkę:</h1>
<textarea name="note" id="note"></textarea>
<button id="btnAddNote">Dodaj notatkę</button>
</section>
<section id="updateNote">
<h1>Aktualizuj notatkę</h1>
<input type="text" name="oldKey" id="oldKey" disabled />
266 Rozdział 11. Przechowywanie danych po stronie klienta
Prawdziwa siła tego przepisu leży w obsłudze zdarzeń. Jeśli załadujesz stronę w dwóch zakładkach lub
oknach przeglądarki i dodasz nową notatkę w jednym z okien, zostanie uruchomiona procedura obsługi
zdarzeń. W tym przypadku przeglądarka wywoła funkcję onStorageEvent, która wykona dwa działania.
Po pierwsze, powiadomi odwiedzającego, że wystąpiła zmiana, i wyświetli szczątkową informację na jej
temat. Po drugie, procedura obsługi zdarzeń wywoła funkcję aktualizacji wyświetlania w celu pokazania
uaktualnionej listy notatek. Jeśli poeksperymentujesz z dodawaniem, aktualizowaniem i usuwaniem notatek,
zrozumiesz działanie tej procedury.
Web SQL Database API 267
Jeśli usuniesz notatkę, procedura obsługi zdarzeń pokaże Ci cały szereg zmian zachodzących w pamięci
lokalnej. Wynika to z utrzymywania kolejności notatek. Aby zachować kolejność na liście, skrypt wykorzystuje
algorytm, w którym wpisy znajdujące się po tym przeznaczonym do usunięcia są przesuwane w dół listy,
a ostatni wpis jest usuwany. Pamiętaj, że metoda clear usuwa nie tylko pary klucz-wartość, które chciałbyś
usunąć, ale wszystkie pary klucz-wartość z pamięci.
Uwaga
Zdarzenia pamięci nie są wyzwalane na tej samej stronie, która dokonała modyfikacji pamięci sesji
albo pamięci lokalnej. Są one powiadomieniami propagowanymi do innych okien i zakładek przeglądarki.
Aby przechwycić zmianę na stronie, która jej dokonała, musisz wywołać odpowiednie działania
bezpośrednio po dokonaniu tej zmiany.
Android 2.1+
Chrome 9.0+
Firefox -
Internet Explorer -
iOS Safari 3.2+
Opera 10.6+
Safari 3.2+
268 Rozdział 11. Przechowywanie danych po stronie klienta
Specyfikacja Web SQL Database API jest oparta na SQLite, więc jeśli używałeś już SQLite w środowisku
mobilnym, takim jak środowisko iOS, będzie Ci ona znana. A jeśli używałeś bazy danych po stronie serwera,
takiej jak MySQL, rozpoznawalne będą podstawowe polecenia oraz struktura. Jeśli nie masz doświadczenia
z SQL-em, możesz poszukać jakichś zasobów dotyczących poleceń SQL, ale podstawowe polecenia do tworzenia,
odczytu, modyfikacji i usuwania danych omówimy w następnym przepisie. Tak jak w przypadku pamięci
sesji i pamięci lokalnej, dla celów bezpieczeństwa sieciowa baza danych jest dostępna tylko dla stron z tej
samej domeny, dla której została utworzona. Jednak w przeciwieństwie do pamięci sesji i pamięci lokalnej,
sieciowa baza danych może przechowywać złożone dane z informacjami o powiązaniach między nimi.
Uwaga
Trwa debata na temat formatu bazy danych, który ma być używany w Web SQL Database API.
W specyfikacji Web Database organizacji W3C został wymieniony SQLite, wdrożony zresztą w kilku
przeglądarkach, jednak dopóki nie ma jednogłośnej akceptacji, grupa sieciowej bazy danych z W3C
zawiesiła pracę nad tą funkcjonalnością. Spowodowało to promocję specyfikacji Indexed Database,
która zyskuje na atrakcyjności.
Do interakcji z sieciową bazą danych API udostępnia trzy podstawowe asynchroniczne polecenia. Są one
asynchroniczne, aby renderowanie strony w przeglądarce nie było „blokowane” podczas oczekiwania na
wynik wywołania bazodanowego. W zależności od ilości żądanych w transakcji danych strona może czekać
nawet kilka sekund na zwrócenie zbioru danych. Trzy podstawowe polecenia pozwalają Ci otworzyć (lub
utworzyć) bazę danych, utworzyć transakcję i wykonać polecenia SQL w ramach transakcji:
openDatabase — otwiera istniejącą bazę danych lub tworzy na podstawie przekazanych
parametrów nową, jeśli baza danych nie istnieje.
transaction — grupuje polecenia dla bazy danych, dzięki czemu możesz w razie potrzeby
wycofać całą transakcję. Dostępna jest wersja tylko do odczytu — readTransaction.
executeSql — metoda uruchamiająca właściwe polecenie SQL na otwartej bazie danych.
Metoda openDatabase otwiera bazę danych, a jeśli baza danych nie istnieje, najpierw automatycznie
tworzy ją na podstawie zadanych parametrów:
WindowDatabase openDatabase(name, version, displayName, estimatedSize [, creationCallBack])
Wskazówka
Numer wersji w metodzie openDatabase przydaje się do śledzenia wersji struktury i danych Twojej bazy
danych. Może to być użyteczne, jeśli używasz bazy danych w celu szybkiego, lokalnego wyszukiwania
danych. Kiedy wyszukane przez Ciebie dane ulegają zmianie, a użytkownik powraca na Twoją witrynę,
możesz sprawdzić wersję dzięki odpowiedniej wartości zapisanej w tabeli w głównej bazie danych.
Jeśli baza danych do wyszukiwania nie została zaktualizowana, możesz umieścić na serwerze nową.
Podczas otwierania możesz także użyć kodu błędu do potwierdzenia, że jest to zła wersja.
Aby zrealizować zapytanie w bazie danych, należy wykonać żądanie transakcji. Metoda transaction
opakowuje jedno lub większą liczbę poleceń executeSql, aby utworzyć pojedynczą transakcję, w ramach
której baza danych ma działać. Transakcja może być wycofana, jeśli którekolwiek z zapytań SQL skończy
się niepowodzeniem. Parametr callback transakcji zawiera funkcję dla metod executeSql:
transaction(callback [, errorCallback] [, successCallback])
Metoda executeSql przyjmuje jako pierwszy parametr Twoje polecenie zapytania SQL i może zawierać
tablicę opcjonalnych argumentów, funkcję wywołania zwrotnego w przypadku prawidłowego wykonania
zapytania i opcjonalną funkcję wywołania zwrotnego w przypadku błędu, jak pokazano poniżej:
executeSql(sqlStatement [, arguments] [, callback] [, errorCallback])
Parametr opcji procedury obsługi błędów dla metod API baz danych jest używany do przechwytywania
wszelkich błędów, które mogłyby wystąpić podczas pracy z bazą danych. Może to obejmować różne przypadki,
takie jak błąd w Twoim SQL-u lub problem z dostępem do bazy danych:
UNKNOWN_ERR (0) — wystąpił nieznany błąd, spoza wymienionych tutaj.
DATABASE_ERR (1) — w bazie danych wystąpił błąd, który nie jest objęty kategorią błędu.
VERSION_ERR (2) — wersja podana w poleceniu nie pasuje do wersji bazy danych.
TOO_LARGE_ERR (3) — zbiór danych uzyskanych w wyniku Twojego zapytania z bazy danych
był zbyt duży, aby mógł zostać zwrócony.
270 Rozdział 11. Przechowywanie danych po stronie klienta
Często spotykanym błędem jest SYNTAX_ERR, który oznacza problem ze składnią Twojego polecenia SQL.
Komunikat będący częścią obiektu błędu przekazanego do Twojej procedury obsługi błędu będzie miał
decydujące znaczenie w przekazaniu Ci większej ilości szczegółów na temat danego kodu błędu.
W tym momencie mógłbyś zapytać, w jaki sposób wszystko to działa wspólnie, realizując przechowywanie
i wyszukiwanie informacji w bazie danych. Pokazuje to kolejny przepis, prezentujący łączne wykorzystanie
tych podstawowych metod.
Tabela departments będzie miała dwa pola, jak określono w tabeli 11.6.
Użycie sieciowej bazy danych do stworzenia listy zakupów 271
Kiedy tabela departments zostanie utworzona, skrypt doda do niej domyślnie cztery działy.
Wskazówka
Podobnie jak w przypadku pamięci sesji i pamięci lokalnej, narzędzia dla programistów przeglądarki
Chrome pozwalają w prosty sposób przeglądać bazę danych SQLite w czasie wykonywania skryptu.
W oknie Narzędzia dla programistów w zakładce Resources (zasoby) możesz odnaleźć punkt Database
(baza danych). W przeciwieństwie do elementów pamięci sesji i pamięci lokalnej, tabel i danych bazy
danych nie da się edytować w narzędziach dla programistów, ale ta funkcjonalność zostanie być może
w przyszłości dodana.
Dla celów diagnostycznych na stronie umieszczono także przycisk Usuń bazę danych, pozwalający
na usunięcie bazy danych. Aktualna specyfikacja sieciowej bazy danych nie zawiera metody usuwania
bazy danych. Aby obejść ten problem i usunąć bazę danych, należy najpierw ją opróżnić, pozbywając się
utworzonych w niej tabel. W trakcie programowania przy użyciu Web SQL Database API użyteczne może
być ponowne tworzenie bazy danych, dzięki któremu możesz weryfikować odpowiedzialne za to zadanie
SQL-owe transakcje.
Aby utworzyć stronę z listą zakupów, wykonaj następujące kroki:
1. Utwórz podstawową strukturę strony ze znacznikami HTML, deklaracją zmiennej shoppingdb,
procedurą obsługi zdarzenia załadowania strony window.addEventListener oraz javascriptową
funkcją init.
2. Dodaj wywoływane z funkcji init funkcje javascriptowe: openShoppingDb i dbPresent.
3. Aby zakończyć inicjalizację bazy danych, dodaj funkcje initShoppingDb, onDbError i nullHandler.
Funkcja initShoppingDb tworzy tabele i wpisuje do nich działy. Funkcja onDbError jest ogólną
funkcją obsługującą błędy bazy danych, które mogą się pojawić, kiedy skrypt wykonuje transakcje,
natomiast funkcja nullHandler jest używana do przechwytywania zdarzeń udanych transakcji.
Jeśli załadujesz teraz stronę, powinieneś zobaczyć bazę danych utworzoną przez Twój skrypt
w narzędziach dla programistów Chrome.
4. Aby produkty spożywcze były prezentowane, gdy wyświetlana jest strona, dodaj funkcje initPage,
getDepartments i showDepartments. Technika „pobierz i pokaż” jest tu szczególnie przydatna,
ponieważ żądania transakcyjne są asynchroniczne i wymagają funkcji zwrotnej do obsługi
wyników. W tym przypadku funkcja getDepartments wykonuje żądanie pobrania działów z tabeli
departments, a funkcja showDepartments je wyświetla. Dzięki funkcji showDepartments utworzysz
opcje dla listy wyboru.
5. Aby zakończyć wyświetlanie w czasie inicjalizacji, powinieneś pokazać wszelkie produkty
spożywcze, które zostały wcześniej zapisane w bazie danych — w tym celu dodaj getGroceryItems
i showGroceryItems z listingu 11.6.
272 Rozdział 11. Przechowywanie danych po stronie klienta
6. Aby dodać produkt, wywoływana jest funkcja addGroceryItem, która pobiera dane wejściowe z pól
wejściowych i wstawia je do tabeli groceryitems. Jeśli operacja się powiedzie, strona wyświetli
nową listę produktów. Aby móc usunąć produkt, dodaj metodę deleteItem.
7. Aby dokończyć tworzenie strony, dodaj metodę resetGroceryList, która czyści listę zakupów,
oraz diagnostyczną funkcję removeDatabase, która usuwa tabele bazy danych.
Listing 11.6. Tworzenie listy zakupów przy użyciu bazy danych po stronie klienta
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>11.6. Bazodanowa lista zakupów</title>
<style>
section {
margin-bottom:20px;
}
</style>
<script>
</script>
</head>
<body>
<section>
Ilość:<input type="number" id="quantity" />
Produkt:<input type="text" id="item" placeholder="mleko" />
Dział:<select id="department"></select>
<button id="addGroceryItem">Dodaj produkt</button>
</section>
<section id="myShoppingList"></section>
<section>
276 Rozdział 11. Przechowywanie danych po stronie klienta
Po załadowaniu strony w przeglądarce funkcja init weryfikuje dostępność Web SQL Database poprzez
sprawdzenie, czy dostępna jest metoda window.openDatabase. Jeśli tak, wywoływana jest metoda openDatabase
z parametrami bazy danych. W tym przypadku skrypt określa wielkość bazy na 5 MB, ponieważ przeglądarki
wydają się nie współpracować z użytkownikiem przy tworzeniu baz danych tej wielkości lub mniejszych. Jeśli
Twoja baza danych przekroczy ten rozmiar podczas tworzenia lub później, przeglądarka zapyta użytkownika,
czy może przeznaczyć na bazę więcej miejsca. Jeśli uruchamiasz kod po raz pierwszy, baza danych zostanie
utworzona, a jej uchwyt zwrócony; w przeciwnym wypadku baza danych zostanie otwarta.
Ponieważ przepis ten używa do załadowania listy wyboru działów wartości z tabeli departments,
w przypadku nowej instancji bazy danych musisz wypełnić tę tabelę swoimi danymi referencyjnymi. Aby
sprawdzić, czy należy utworzyć tabele, w metodzie dbPresent skrypt weryfikuje ich obecność, wykonując
proste zapytanie, i stara się wybrać pierwszy wiersz w tabeli działów. Jeśli tabela nie istnieje, w metodzie
initShoppingDb skrypt zarówno tworzy tabele, jak i wstawia wartości działów do tabeli departments. Gdy
baza danych jest załadowana, skrypt wczytuje działy na listę wyboru i wyświetla wszelkie zapisane produkty
spożywcze poprzez wywołanie metody getGroceryItems. Metoda getGroceryItems otwiera kolejną transakcję,
aby zwrócić wszystkie pozycje z tabeli groceryitems, oraz, w przypadku sukcesu, przekazuje wynikowy zbiór
wierszy metodzie showGroceryItems. Metoda show przejdzie następnie w pętli przez produkty spożywcze
i wyświetli je we fragmencie strony o identyfikatorze myShoppingList.
Uwaga
Metoda transaction służy do poleceń SQL, które wymagają uprawnień do zapisu w bazie danych.
Poprzez wymaganie prawa zapisu transakcja ustawia blokadę zapisu w bazie danych, co uniemożliwia
innym pisanie do bazy danych w tym czasie. Jeśli wykonujesz w bazie danych wyłącznie polecenia
odczytu, powinieneś użyć metody readTransaction. Metoda readTransaction jest wywoływana tak
samo jak metoda transaction.
Gdy strona jest zainicjowana, a baza danych i działy są wypełnione, odwiedzający może wprowadzić
produkt i ilość oraz wybrać dział dla nowej pozycji na swojej liście zakupów. addGroceryItem uwzględni
następnie te informacje, utworzy transakcję bazy danych i wykona SQL-owe polecenie wstawiania danych,
aby dodać element w wywołaniu funkcji. Jeżeli wstawienie danych nie powiedzie się, zostanie wywołana
procedura obsługi błędów, onDbError, która wyświetli błąd. Jeśli addGroceryItem powiedzie się, skrypt
wywoła getGroceryItems, a następnie showGroceryItems, by wyświetlić wynikowy zbiór wierszy zwróconych
z tabeli groceryitems. Rysunek 11.3 pokazuje wyniki po wprowadzeniu kilku elementów.
W funkcji getGroceryItems skrypt wykona metodę readTransaction, przekazując jej funkcję wywołującą
executeSql. Polecenie SQL wybiera z tabeli groceryitems wszystkie rekordy, grupuje je i sortuje alfabetycznie
według działów. Dodatkowo polecenie SQL łączy tabele groceryitems i departments na podstawie
identyfikatora deptid, tak że zwracany zbiór będzie również zawierać nazwę działu do wyświetlenia. Jeśli
wywołanie executeSql powiedzie się, skrypt zwróci wyniki do showGroceryItems, przekazując odwołanie
do transakcji i zbiór rekordów z wynikami. Aby wyświetlić wyniki ze zbioru rekordów, skrypt przechodzi
w pętli przez wiersze tego zbioru. Każdy wiersz reprezentuje rekord zwracany przez zapytanie SQL,
a do każdego pola możesz się odwołać przez notację z kropką.
Podsumowanie 277
Aby usunąć produkt, odwiedzający klika przycisk X, który wywołuje metodę deleteItem, przekazując
jej identyfikator produktu. Następnie strona używa przekazanego identyfikatora produktu, aby usunąć
produkt z tablicy groceryitems. Jeśli wywołanie się powiedzie, lista jest odświeżana poprzez funkcje
pobierania i pokazywania produktów spożywczych. Odwiedzający może także wyczyścić listę zakupów,
co spowoduje usunięcie wszystkich produktów z tabeli groceryitems.
Ten przepis jest prostą próbą integracji sieciowych baz danych klienta z Twoimi witrynami i aplikacjami.
Przepis obejmuje pisanie, wyszukiwanie, aktualizowanie i usuwanie rekordów wraz z początkowym
tworzeniem bazy danych. Ta funkcjonalność zapewnia elementy potrzebne do zarządzania przechowywaniem
danych w bazie danych.
Podsumowanie
W tym rozdziale poznałeś kilka nowych interesujących sposobów przechowywania danych w przeglądarce
klienta. Wcześniej technologia przeglądarek klienckich była ograniczona do korzystania z ciasteczek. Dzięki
dodaniu pamięci sesji, pamięci lokalnej oraz sieciowych baz danych masz teraz kilka możliwości zapisu
prostych par klucz-wartość albo złożonych struktur danych. Przy użyciu tych sposobów przechowywania
danych możesz tworzyć zaawansowane aplikacje w trybie offline, zmniejszać liczbę żądań pobrania danych
z serwera oraz przechowywać informacje pomiędzy sesjami przeglądarki po stronie klienta.
278 Rozdział 11. Przechowywanie danych po stronie klienta
12
Komunikacja i wątki
API HTML5 obejmują wiele nowych obszarów i ulepszeń, a dwa spośród najnowszych to WebSocket
API i Web Workers API, które wypełniają istotne luki dotyczące tworzenia witryn i aplikacji
internetowych. Przed ich pojawieniem się otwarcie dwukierunkowego kanału komunikacji z serwerami
stanowiło problem, a intensywne przetwarzanie na stronie dodatkowo uniemożliwiało interfejsowi interakcję
z użytkownikiem. WebSocket API dzięki połączeniu poprzez gniazdo sieciowe udostępnia dwukierunkową
komunikację między JavaScriptem klienta a serwerem, natomiast Web Workers API udostępnia JavaScriptowi
klienta podstawowe możliwości pracy wielowątkowej. W tym rozdziale dowiesz się, jak wdrożyć oba te
interfejsy — w tym celu zrealizujesz kilka prostych, ale skutecznych przepisów, na których możesz oprzeć
własne rozwiązania.
Po utworzeniu instancji obiekt WebSocket próbuje otworzyć połączenie z usługą nasłuchującą pod adresem
URL podanym w konstruktorze obiektu. Jeżeli połączenie zostanie pomyślnie otwarte, instancja obiektu
WebSocket udostępni następujące metody do wysyłania wiadomości i zamykania połączeń:
send(DOMString) — wysyła dane w postaci ciągu znaków,
send(ArrayBuffer) — wysyła dane w postaci ArrayBuffer,
280 Rozdział 12. Komunikacja i wątki
W celu ustalenia, czy połączenie z gniazdem umożliwia wysłanie danych, możesz wykorzystać zdarzenie
onopen albo atrybut połączenia readyState, który może przyjąć jedną z czterech wymienionych w tabeli 12.1
wartości.
Tabela 12.2 przedstawia wersje najpopularniejszych przeglądarek obsługujące WebSocket API. Należy
zauważyć, że w różnych przeglądarkach obsługa ta wygląda odmiennie, do czego wrócimy przy okazji
omawiania konkretnych przepisów w dalszej części tego rozdziału.
Android -
Chrome 10.0+
Firefox 4.0+
Internet Explorer -
iOS Safari 4.2+
Opera 11.0+
Safari 5.0+
połączenie oraz wysyłać i odbierać wiadomości. Aby utworzyć połączenie, wykonaj następujące kroki,
co pozwoli Ci utworzyć kod z listingu 12.1:
1. Utwórz pustą stronę HTML z elementem input dla wiadomości, przyciskami do wysyłania
wiadomości i zamykania połączenia oraz elementami div dla statusu i otrzymanej wiadomości.
2. Dodaj zmienne globalne dotyczące gniazda sieciowego i adresu URL.
3. Dodaj funkcję init i procedurę nasłuchu zdarzenia załadowania strony.
4. Dodaj funkcje onOpen, onClose, onMessage i onError do obsługi zdarzeń połączenia poprzez
gniazdo sieciowe.
5. Dodaj funkcje postMessage, closeWS i updateStatus do obsługi wysyłania wiadomości, zamknięcia
połączenia i aktualizowania statusu połączenia.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>12.1. Komunikacja poprzez gniazdo sieciowe</title>
<script>
// ustaw globalne odwołanie do gniazda sieciowego
var directorWebSocket = null;
btnSend.addEventListener('click',postMessage,false);
btnClose.addEventListener('click',closeWS,false);
// FUNKCJE KLIENTA
// dodaj procedurę nasłuchu zdarzenia załadowania strony, aby uruchomić funkcję init
window.addEventListener('load', init, false);
</script>
</head>
<body>
<div id="btnTryCall">
<input type="text" id="msg" />
<button id="btnSend">Wyślij wiadomość</button>
<button id="btnClose">Zamknij gniazdo sieciowe</button>
</div>
Realizacja wielowątkowości za pomocą wątków roboczych 283
<div id="wsState"></div>
<div id="messages"></div>
</body>
</html>
Po załadowaniu strony z listingu 12.1 jest wywoływana funkcja init. W tej funkcji skrypt dodaje procedury
nasłuchu zdarzeń przycisków, a następnie buduje połączenie przez gniazdo sieciowe, tworząc instancję
interfejsu WebSocket i przekazując jej adres. Po utworzeniu tej instancji zdarzenia onopen, onclose, onmessage
i onerror są rejestrowane — każdemu z nich zostaje przyporządkowana odpowiednia funkcja. Po pomyślnym
otwarciu połączenia przez gniazdo sieciowe zostaje wywołane zdarzenie onopen, a w konsekwencji funkcja
onOpen, która zmienia wyświetlany na stronie status połączenia. W tym przepisie wpisaliśmy do dziennika
konsoli komunikaty, tak abyś w dzienniku swojej przeglądarki mógł śledzić przepływ danych poprzez
połączenie używające gniazda sieciowego.
Strona wyświetla użytkownikowi pole wprowadzania danych, w którym może on wpisać wiadomość
i wysłać ją za pośrednictwem gniazda sieciowego do serwera. Wiadomość jest wysyłana w funkcji postMessage,
w której na gnieździe sieciowym zostaje wywołana metoda send z komunikatem przekazanym w parametrze.
Po odebraniu komunikatu usługa gniazda sieciowego przepakowuje ciąg znaków i wysyła go z powrotem
do strony w postaci wiadomości. Procedura obsługi zdarzenia onmessage przechwytuje przychodzącą
wiadomość, a odpowiadająca mu funkcja onMessage pobiera przychodzący komunikat i aktualizuje zawartość
ekranu częścią komunikatu zawierającą dane.
W przypadku wystąpienia błędu podczas otwierania, używania albo zamykania połączenia przez gniazdo
sieciowe procedura obsługi zdarzenia onerror przechwyci błąd i wyświetli użytkownikowi fragment błędu
zawierający dane. Po zakończeniu konwersacji pomiędzy stroną a usługą gniazda sieciowego można zamknąć
połączenie poprzez gniazdo sieciowe, wywołując na nim metodę close.
Uwaga
Usługa, której Twoja instancja gniazda sieciowego wysyła wiadomości i od której je odbiera, będzie
zwykle zaprojektowana przez Ciebie i będzie działać na Twoim serwerze sieciowym. W sieci możesz
znaleźć kilka przykładów napisanych w różnych językach programowania i mających otwarte źródła,
dotyczących konfigurowania procesu serwera gniazda sieciowego.
W tym przepisie otworzyliśmy połączenie ze zdalnym serwerem i wysłaliśmy w obie strony wiadomości
przy wykorzystaniu połączenia przez gniazdo sieciowe. Może to być niezwykle przydatne do
dwukierunkowej komunikacji, a zwłaszcza inicjowanego przez usługę przesyłu wiadomości do podłączonej
z nią strony przeglądarki.
Realizacja wielowątkowości
za pomocą wątków roboczych
Powszechnym problemem związanym z działającym na stronie HTML JavaScriptem jest ograniczenie
jego działania do jednego wątku, co może skutkować zablokowaniem interfejsu podczas intensywnego
przetwarzania. Dzięki Web Workers API w HTML5 programiści mają możliwość odseparowania
przetwarzania i wykonywania zadań w działającym w tle wątku mającym niższy priorytet od głównego
wątku na stronie HTML. W ten sposób główny wątek pozostaje odblokowany i umożliwia użytkownikowi
interakcję ze stroną z zerowym lub minimalnym negatywnym wpływem na stronę.
284 Rozdział 12. Komunikacja i wątki
Implementacja Web Workers API w HTML5 udostępnia dwa rodzaje wątków roboczych: dedykowane
i współdzielone. Dedykowany wątek roboczy jest przypisany do strony, która go uruchamia, i nie jest
dostępny z poziomu innych stron. Współdzielony wątek roboczy może być z kolei dzielony przez wiele
stron z tej samej domeny w tej samej instancji przeglądarki. Wątek współdzielony ma swój własny stan,
również dzielony przez strony, co zobaczysz w ostatnim przepisie tego rozdziału.
Czym więc konkretnie jest wątek roboczy? Wątek roboczy jest wyodrębnionym w formie pliku
javascriptowym skryptem uruchamianym w nowym wątku. Zamiast przechowywać taki skrypt w osobnym
pliku .js, można go również przekazać wątkowi roboczemu bezpośrednio w parametrze. Wątek roboczy
może się dwukierunkowo komunikować z uruchamiającą go stroną oraz głównym skryptem za pomocą
wiadomości, które są przesyłane pomiędzy wątkami.
Wskazówka
Wątki robocze nie mogą manipulować elementami DOM strony, ponieważ wątek roboczy działa w wątku
oddzielonym od samej strony. DOM może być modyfikowany tylko z poziomu głównego wątku strony.
Aby powstała instancja wątku roboczego, należy utworzyć nową instancję interfejsu Worker, przekazując
jej skrypt lub plik do wykonania, co pokazano poniżej na przykładzie dedykowanego wątku roboczego:
var myWorker = new Worker('worker_script.js');
Dedykowany wątek roboczy z powyższego kodu po utworzeniu wykona kod javascriptowy. Wątek może
nieustannie działać i być dostępny, dopóki nie zostanie zakończony lub nie zostanie zamknięta strona, która
go utworzyła. Oto metody dostępne dla dedykowanego wątku roboczego:
postMessage(wiadomość [, portWiadomości]) — wysyła wiadomość do wątku roboczego.
Wątek roboczy „przechwytuje” komunikat za pomocą opisanego niżej zdarzenia onmessage.
terminate() — kończy wątek roboczy, na którym ta metoda jest wywoływana.
W dedykowanym wątku roboczym dostępne są dwa zdarzenia: onmessage i onerror. Zdarzenie onmessage
pozwala głównemu skryptowi strony otrzymywać wiadomości z wątku roboczego, a wątkowi roboczemu
— wiadomości z głównego wątku. W tabeli 12.3 przedstawiono zdarzenia onmessage i onerror dostępne
w interfejsie Worker.
Uwaga
Jeśli programowałeś kiedyś w języku, który obsługuje korzystanie z wielu wątków, prawdopodobnie jesteś
świadomy zagadnień bezpieczeństwa wątków i problemów, które mogą powstać przy równoczesnym
ich uruchamianiu. W Web Workers API w większości przypadków przed problemami chroni przesyłanie
wiadomości między wątkami za pomocą serializowanych obiektów. W celu uniknięcia pozostałych
problemów wątki robocze nie mają dostępu do struktury DOM strony ani do składników, które nie są
bezpieczne dla wątków. Tylko wątek głównej strony może zaktualizować elementy DOM strony.
Tworzenie wątku roboczego 285
W tabeli 12.4 przedstawiono obsługę Web Workers API przez najpopularniejsze przeglądarki.
Android 2.1
Chrome 10.0+
Firefox 3.6+
Internet Explorer 10.0+
iOS Safari -
Opera 10.6+
Safari 4.0+
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>12.2. Prosta komunikacja z wątkiem roboczym</title>
<script>
// globalne odwołanie do wątku roboczego
var worker = null;
286 Rozdział 12. Komunikacja i wątki
btnTimeStamp.addEventListener('click',getTimeStamp,false);
btnTerminateWorker.addEventListener('click',terminateWorker,false);
// lokalny wątek, mający zapisać znacznik czasowy i pokazać, że główny wątek nie jest blokowany przez trwające obliczenia
function getTimeStamp() {
</script>
</head>
<body>
<p>Obliczanie <output id="result"></output></p>
<button id="btnTimeStamp">Znacznik czasowy</button>
<button id="btnTerminateWorker">Zakończ wątek roboczy</button>
<section id="timeLog"></section>
</body>
</html>
Kiedy strona tworzy wątek roboczy, nowej instancji jest przekazywana nazwa javascriptowego pliku do
wykonania w nowym wątku. W tym przypadku plik nazywa się listing.12.3.js. Listing 12.3 przedstawia kod tego
pliku, który przechodzi w pętli przez zakres liczb, dodaje je i wysyła wiadomości do głównego skryptu strony.
1. Utwórz javascriptowy plik z listingu 12.3, dodając zmienne na jego początku.
2. Dodaj instrukcję for przechodzącą w pętli od 1 do maxLimit, sumującą liczby i zwracającą
procentowy status ukończenia.
3. Dodaj końcowe wywołanie postMessage, aby przesłać całkowitą sumę do głównego skryptu strony.
Tworzenie wątku roboczego 287
// inicjalizuj zmienne
var sum = 0;
var currentPercentageComplete = 0;
var maxLimit = 100000000;
// w pętli
for (var j=0; j<=maxLimit; j++) {
// minimalizuj liczbę wysyłanych wiadomości poprzez wysyłanie ich tylko wtedy, gdy procent się zmienił
if (newPercentageComplete > currentPercentageComplete) {
Po załadowaniu do przeglądarki głównej strony zdarzenie load okna wywołuje funkcję startWorkerThread.
Funkcja dodaje procedury nasłuchu przycisków, tworzy nową instancję Worker, przekazuje jej nazwę pliku
javascriptowego oraz przypisuje odwołanie do niej zmiennej wątku. Po utworzeniu tej instancji skrypt definiuje
funkcję, która po wystąpieniu zdarzenia onmessage otrzyma dane i w elemencie output o identyfikatorze result
wyświetli komunikat pochodzący z wątku roboczego. Kiedy wątek roboczy wyśle za pomocą postMessage
wiadomość do głównego skryptu, zostaje uruchomiona procedura obsługi zdarzenia onmessage.
Kiedy instancja wątku zostaje utworzona, skrypt jest ładowany do wątku i automatycznie uruchamiany.
Z kolejnego przepisu dowiesz się, jak ręcznie poinstruować skrypt, kiedy ma rozpocząć działanie po
utworzeniu wątku.
W miarę wykonywania skryptu w wątku roboczym poprzez przechodzenie w pętli przez kolejne liczby
obliczana jest całkowita suma (dla celów demonstracyjnych w bardzo długi i żmudny sposób). W miarę
postępów pętli skrypt sprawdza, czy zwiększył się procent ukończenia. Jeżeli procent ukończenia uległ zmianie,
skrypt za pomocą polecenia postMessage wysyła głównej stronie wiadomość zawierającą nową wartość
procentowego zakończenia. Wiadomość ta uruchamia procedurę obsługi zdarzenia onmessage w głównym
skrypcie strony. Główny skrypt pobiera wówczas zdarzenie i uzyskuje dostęp do składowej data, która
zawiera właściwy tekst. Zostaje wyświetlony procent ukończenia, jak pokazano na rysunku 12.1.
Jeśli użytkownik chciałby zakończyć wątek roboczy bez zamykania strony, może użyć przycisku
zakończenia wątku i w ten sposób poinformować wątek za pomocą metody terminate, żeby natychmiast
zakończył pracę.
288 Rozdział 12. Komunikacja i wątki
Rysunek 12.1. Przykładowe znaczniki czasowe wyświetlone podczas wykonywania przez wątek obliczeń
Wskazówka
Tworzenie i wykonywanie wątków roboczych może stanowić spore obciążenie. Najlepiej korzystać
z nich, gdy są „długowieczne” i wykonują operacje, które nie powinny blokować głównego wątku
interfejsu. Powinieneś się zastanowić, kiedy używać wątku roboczego i jak wiele wątków należy
utworzyć. Nie istnieje odgórny limit liczby wątków roboczych, które możesz utworzyć, a wątki mogą
nawet tworzyć instancje innych wątków, jednak z każdym kolejnym wątkiem roboczym związane jest
pewne obciążenie, zatem najlepiej jest ograniczać ich liczbę do minimum.
<!DOCTYPE html>
<html>
<head>
Dodawanie dwukierunkowej komunikacji 289
case 'MSG':
// wiadomość wysłana z wątku roboczego — wyświetlanie
var workerResponse = document.getElementById('workerResponse');
workerResponse.innerHTML = 'Wątek roboczy przesłał: ' + msgFromWorker.msg;
break;
case 'ERR':
// błąd wysłany z wątku roboczego — powiadom użytkownika
alert('Błąd z wątku roboczego: ' + msgFromWorker.msg);
break;
}
};
window.addEventListener('load',init,false);
</script>
</head>
<body>
<p>Wpisz wiadomość tekstową dla wątku roboczego (pozostaw puste pole, aby wątek zwrócił wiadomość
´zawierającą błąd):</p>
<input id='inputForWorker' />
<button id="btnPostToWorker">Wyślij do wątku roboczego</button>
<section id="workerResponse"></section>
</body>
</html>
290 Rozdział 12. Komunikacja i wątki
Tak jak poprzednio, do przechowywania kodu wątku roboczego wykorzystamy zewnętrzny plik
javascriptowy. Treść tego pliku jest przedstawiona na listingu 12.5, który można odtworzyć, wykonując
poniższe kroki. Upewnij się, że użyta nazwa pliku jest taka sama jak na listingu 12.4, w poleceniu tworzenia
wątku roboczego w Twoim głównym skrypcie.
1. Utwórz pusty plik i dodaj procedurę obsługi zdarzenia onmessage z listingu 12.5.
2. Dodaj polecenie warunkowe if i jego gałęzie w celu sprawdzenia prawidłowości danych
wejściowych oraz odesłania do głównego skryptu błędu albo wiadomości z potwierdzeniem.
Poprzez załadowanie javascriptowego pliku wątku po wczytaniu strony HTML zostaje utworzony wątek
roboczy. Zauważ jednak, że w odróżnieniu od wcześniejszego przepisu, bezpośrednio po utworzeniu wątek
ten nie wykonuje żadnych działań. Wątek roboczy czeka, dopóki przychodząca wiadomość nie uruchomi
procedury obsługi wiadomości. Na stronie HTML użytkownik jest proszony o wpisanie w polu tekstowym
ciągu tekstowego i kliknięcie przycisku Wyślij do wątku roboczego. Przycisk ten uruchamia funkcję postMessage,
wysyłającą łańcuch tekstowy do wątku roboczego. Wątek roboczy sprawdza następnie, czy łańcuch jest
pusty — jeśli tak, wysyła z powrotem do głównego skryptu wiadomość przy użyciu łańcucha JSON z typem
komunikatu ERR i odpowiednią informacją. Jeśli otrzymana przez wątek roboczy wiadomość nie jest pusta,
wątek roboczy opakowuje łańcuch innym łańcuchem, aby oznaczyć to, co zostało odebrane, i wysłać
z powrotem zaktualizowaną wiadomość w tym samym formacie JSON, ale z typem komunikatu MSG.
Po odesłaniu błędu lub wiadomości do głównego skryptu jest w nim uruchamiana procedura obsługi
zdarzenia onmessage. Funkcja sprawdza typ przesłanego komunikatu, a następnie albo informuje użytkownika
o błędzie, albo aktualizuje pole wiadomością z wątku roboczego (co pokazano na rysunku 12.2), po czym
kończy pętlę komunikacyjną.
Rysunek 12.2. Przykładowy rezultat pokazujący wiadomość wysłaną i zwróconą przez wątek roboczy
Aby uzyskać komunikat o błędzie, pozostaw pole wejściowe puste i kliknij przycisk Wyślij do wątku
roboczego, co spowoduje wysłanie do wątku roboczego pustego ciągu znaków.
Wykorzystywanie współdzielonych wątków roboczych 291
W tym oraz poprzednim przepisie wykorzystaliśmy dedykowane wątki robocze, które są dostępne
wyłącznie dla tworzących je stron. W niektórych przypadkach możesz jednak potrzebować wątku roboczego
współdzielonego przez strony. W kolejnym przepisie wyjaśnimy strukturę współdzielonego wątku roboczego
oraz sposób zaimplementowania połączenia z takim wątkiem.
Współdzielony wątek roboczy ma jedno zdarzenie, connect, które jest generowane, gdy wątek klienta
łączy się ze współdzielonym wątkiem roboczym, co opisano w tabeli 12.5.
Jak wspomniano, każde połączenie klienta jest jednoznacznie identyfikowane poprzez port. Metoda
postMessage i zdarzenia wiadomości wykorzystują ten port, tak aby przesyłanie wiadomości odbywało się
na poziomie połączenia.
W tym przepisie wyjaśnimy, jak wykorzystać współdzielenie wątku roboczego na wielu stronach do
obliczenia średniej wartości liczbowej na podstawie wartości podanych poprzez dowolną z podłączonych
stron. Kiedy średnia ulega zmianie, wynik — w celu wyświetlenia — jest wysyłany do każdej strony. Dla celów
testowych strony klienta są wyświetlane przy użyciu iframe w jednym dokumencie, który pełni funkcję
pojemnika, zamiast w osobnych oknach przeglądarki. Mamy zatem trzy pliki do utworzenia: główną stronę
(pojemnik), która przechowa ramkę iframe dla każdej strony klienta, stronę klienta, która będzie przyjmować
dane wejściowe od użytkownika i łączyć się ze współdzielonym wątkiem roboczym, oraz właściwy plik
javascriptowy współdzielonego wątku roboczego. Zacznijmy od pliku pojemnika pokazanego na listingu 12.6:
292 Rozdział 12. Komunikacja i wątki
1. Utwórz pustą stronę HTML ze znacznikiem body oraz trzema elementami iframe w znacznikach div.
2. Następnie dodaj znaczniki style i zdefiniuj styl znaczników div i iframe, aby pokazać ramki iframe
w trzech równych kolumnach w poprzek strony. Na tym kończy się plik pojemnika.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>12.6. SharedWorker na wielu stronach</title>
<!-- To jest tylko strona pojemnika do pokazywania wielu stron
używających współdzielonego wątku roboczego. Rezultat byłby taki sam,
gdyby strony były w osobnych zakładkach albo oknach w tej samej
przeglądarce. -->
<style>
div{float:left;width:33.3%;height:500px}
iframe{width:100%;height:100%}
</style>
</head>
<body>
<div>Strona klienta 1<iframe src="listing.12.7.html"></iframe></div>
<div>Strona klienta 2<iframe src="listing.12.7.html"></iframe></div>
<div>Strona klienta 3<iframe src="listing.12.7.html"></iframe></div>
</body>
</html>
Następnie, wykorzystując kod pokazany na listingu 12.7, należy utworzyć stronę klienta, która będzie
załadowana do każdego elementu iframe pojemnika i wyświetli użytkownikowi pole wejściowe do
wprowadzania liczby.
1. Utwórz pustą stronę HTML z pokazaną na listingu 12.7 zawartością znacznika body, obejmującą
znacznik input, przycisk Wyślij liczbę do współdzielonego wątku roboczego i element zawierający
dziennik.
2. Dodaj znaczniki script i globalne odwołanie do instancji współdzielonego wątku o nazwie
sworker. Będzie to instancja SharedWorker, służąca do wysyłania i otrzymywania wiadomości.
3. Dodaj wiersz window.addEventListener i funkcję init, która tworzy połączenie ze współdzielonym
wątkiem roboczym oraz rejestruje procedurę obsługi onmessage dotyczącą portu tego klienta
współdzielonego wątku roboczego.
4. Dodaj funkcję sendNumber, która pobiera wartość wejściową i przesyła liczbę do współdzielonego
wątku roboczego w celu przetwarzania.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>12.7. Klient współdzielonego wątku roboczego</title>
<script>
// pobierz liczbę
var numToSend = document.getElementById('numberToSend').value;
</script>
</head>
<body>
Bieżąca średnia: <output id="average"></output>
<br><br>
<input id="numberToSend" />
<button id="btnSendNumber">Wyślij liczbę do współdzielonego wątku roboczego</button>
<hr width="100%">
Komunikaty ze współdzielonego wątku roboczego:<br>
294 Rozdział 12. Komunikacja i wątki
<section id="log"></section>
</body>
</html>
Po załadowaniu strony klienta z listingu 12.7 inicjalizowane jest połączenie ze współdzielonym wątkiem
roboczym, utworzonym na podstawie pliku listing.12.8.js. Ostatnim krokiem jest utworzenie javascriptowego
pliku współdzielonego wątku roboczego. Plik ten, pokazany na listingu 12.8, został utworzony za pomocą
następujących kroków:
1. Utwórz javascriptowy plik o nazwie listing.12.8.js (lub jakiejkolwiek nazwie, którą wybierzesz,
pod warunkiem że odpowiada ona parametrowi pliku wątku współdzielonego na Twojej stronie
klienta HTML-owego).
2. Dodaj zmienne globalne w początkowej części skryptu oraz procedurę obsługi zdarzenia onconnect.
3. Dodaj do skryptu funkcje sendAllConnections i updateAverage.
// 12.8. Plik js współdzielonego przez różne strony wątku roboczego do obliczania średniej liczb
// wyślij wiadomość do klienta z komunikatem w formacie JSON zawierającym rodzaj wiadomości, tekst i aktualną średnią
connections[i].postMessage({msgType:msgTypeVal,
msgText:msgVal,
aveValue:average});
}
}
Aby wykonać ten kod, załaduj w przeglądarce stronę pojemnika HTML, która wczyta instancje stron
klientów do trzech ramek iframe, co pokazano na rysunku 12.3. Każda strona klienta utworzy instancję
interfejsu współdzielonego wątku roboczego, co spowoduje utworzenie jednego współdzielonego wątku
roboczego i połączenie z nim strony klienta. Po połączeniu się strony klienta we współdzielonym wątku
roboczym zostanie uruchomiona procedura obsługi zdarzenia onconnect. Procedura obsługi zdarzenia
pobierze odwołanie do portu, z którym to połączenie jest powiązane, tak abyś mógł później wysyłać
wiadomości do tego połączenia klienta, oraz zapisze ten port w tablicy connections. W procedurze obsługi
zdarzenia onconnect współdzielony wątek roboczy wysyła następnie do portu, z którym właśnie się połączył,
komunikat dziennika zawierający informację o podłączeniu się klienta oraz numerze połączenia. Ponadto
w celu inicjalizacji ekranu na jego stronie współdzielony wątek roboczy wysyła do klienta aktualną średnią.
Wreszcie, na potrzeby zdarzenia onmessage jest rejestrowana procedura obsługi zdarzenia dla portu, tak aby po
wysłaniu przez stronę klienta komunikatu do współdzielonego wątku roboczego mógł on zostać przechwycony.
Kiedy współdzielony wątek roboczy wysyła komunikaty dziennika albo ze średnią z powrotem do strony
klienta, procedura obsługi onmessage po stronie klienta przechwyci je. Strona klienta określi na podstawie
wartości pól przekazanego łańcucha JSON rodzaj wiadomości i zaktualizuje zawartość ekranu przez dodanie
wiadomości do dziennika i ustawienie w razie potrzeby wartości średniej.
Gdy wszystkie strony klienta są połączone ze współdzielonym wątkiem roboczym, użytkownik może
wprowadzić dowolną liczbę na którejkolwiek z nich i kliknąć przycisk Wyślij liczbę do współdzielonego wątku
roboczego. Spowoduje to wywołanie w JavaScripcie klienta funkcji sendNumber, co będzie skutkować wysłaniem
zawierającego wprowadzoną liczbę komunikatu do współdzielonego wątku roboczego. Zauważ, że używamy
tutaj atrybutu port obiektu sworker, na którym jest wywoływana funkcja postMessage, co różni się nieznacznie
od przesyłania komunikatów do dedykowanych wątków roboczych:
sworker.port.postMessage(numToSend);
296 Rozdział 12. Komunikacja i wątki
Polecenie wysłania wiadomości wyśle dane wprowadzone przez użytkownika do współdzielonego wątku
roboczego, który następnie pobierze liczbę z zarejestrowanego zdarzenia onmessage i zwróci na port klienta
komunikat dziennika informujący o tym, że liczbę pobrano. Po wysłaniu wiadomości potwierdzającej odbiór
współdzielony wątek roboczy zaktualizuje wartość średniej w zmiennych globalnych i prześle ją do wszystkich
podłączonych klientów. Przekazująca komunikat do wszystkich klientów funkcja pomocnicza przechodzi
w pętli przez tablicę connections i wysyła w komunikacie nową średnią. Rysunek 12.3 przedstawia wyniki
przykładowego uruchomienia przepisu.
Rysunek 12.3. Przykładowe rezultaty pokazujące komunikaty otrzymane przez każdą ze stron klienta
Interfejs SharedWorker należy postrzegać jako młodą technologię, obsługiwaną w niewielu przeglądarkach
i posiadającą niewielką funkcjonalność. Pewne ograniczenia dotyczące tego interfejsu, takie jak brak obsługi
rozłączeń klientów, nie zostały jeszcze przezwyciężone, lecz sam interfejs wydaje się obiecujący i zapewne
w przyszłości znacząco ułatwi programistom pracę.
Wskazówka
Programując własną strukturę wątków roboczych, w celu umożliwienia łatwego debugowania warto
najpierw zaimplementować funkcję wątku na głównej stronie i dopiero wtedy, mając działającą
podstawową funkcjonalność wątku roboczego, umieścić kod w osobnym pliku i wątku.
Podsumowanie
W przepisach zawartych w tym rozdziale pokazano zastosowanie WebSocket API oraz Web Workers API
w niektórych kluczowych wzorcach komunikacji. WebSocket API udostępnia łatwy w użyciu sposób
komunikacji między serwerami i stronami klienta, natomiast wątki robocze — możliwość efektywnej
minimalizacji wpływu intensywnego przetwarzania na główny wątek witryny lub aplikacji. Wykorzystując
te nowe API w swoich aplikacjach, możesz poprawić ogólne działanie i funkcjonalność stron. Przepisy
z tego rozdziału stanowią jedynie zarys możliwości obu API, ale dostarczyły Ci niewątpliwie podstaw do
implementowania swoich własnych rozwiązań.
13
Zachowanie przeglądarek
w HTML5
W tym rozdziale przyjrzymy się tym API, mimo że są one młodsze od innych omawianych w książce
i wciąż wymagają poprawek. Zaczniemy od jednego z bardziej dopracowanych, a zakończymy na API, które
nie jest jeszcze obsługiwane przez większość przeglądarek, ale świetnie rokuje na przyszłość. Przejdźmy zatem
do omawiania Drag and Drop API.
Przeciąganie obiektu w przeglądarce jest możliwe dzięki pojawieniu się nowego atrybutu o nazwie
draggable. Atrybut ten może przyjmować jedną z trzech wartości: true, false i auto. Jeśli jest ustawiony na
true, obiekt może zostać przeciągnięty przez użytkownika. Jeśli atrybut ma wartość false, obiekt nie może
298 Rozdział 13. Zachowanie przeglądarek w HTML5
być przeciągany. Jeśli wartość jest ustawiona na auto, przeglądarka użyje domyślnej wartości atrybutu dla
danego typu obiektu, tak jakby atrybut draggable nie był ustawiony. Zauważyłeś już zapewne, że możesz
chwycić obrazek na stronie i go przeciągnąć. Oczywiście po upuszczeniu go nic się nie dzieje, ponieważ
strefa upuszczania nie została zdefiniowana; mimo to może on być przeciągany. Kod, za pomocą którego
utworzysz div z atrybutem draggable, powinien wyglądać następująco:
<div class="divClass" id="myDiv" draggable="true">Mój div</div>
Do utworzenia obszaru, na który przeciągany obiekt może być upuszczony, służy dodany w HTML5
atrybut o nazwie dropzone. Atrybut ten może przyjmować jedną z trzech wartości: copy, move albo link.
Wartość, którą wybierzesz, zależy od rodzaju działania, które ma być wykonane po upuszczeniu obiektu.
Przykładowa deklaracja dropzone na elemencie div wygląda następująco:
<div id="divDropzone" dropzone="copy" …
Atrybut dropzone może również wykorzystać filtr, taki jak lista formatów, na przykład f:/image/png
oznacza, że na strefie upuszczania mogą być upuszczone tylko pliki obrazków typu PNG.
Następnie należy obsłużyć rozpoczęcie przeciągania i upuszczania obiektu poprzez przechwycenie
zdarzeń wyzwalanych przez elementy określone wcześniej jako obiekt możliwy do przeciągnięcia oraz strefa
upuszczania. Gdy użytkownik zacznie przeciągać obiekt, zostanie wykonany kod z atrybutu ondragstart
albo zdarzenie dragstart. Dzięki wywołaniu funkcji obsługi tego zdarzenia będzie możliwe zapisanie danych
dotyczących przeciąganego obiektu. Kiedy obiekt zostanie następnie upuszczony w strefie upuszczania,
strefa ta uruchomi kod atrybutu o nazwie ondrop albo zdarzenie drop. Po wywołaniu tego zdarzenia skrypt
może pobrać dane zapisane w przeciąganym obiekcie i użyć ich do wykonania dowolnie wybranej funkcji.
Do przekazania tej informacji wykorzystujemy nowy interfejs o nazwie DataTransfer.
Interfejs DataTransfer posiada różne atrybuty i metody. Oto dostępne metody:
setData(format, dane) — ustawia dane typu format (na przykład text) w pamięci danych.
getData(format) — pobiera dane typu format z pamięci danych.
clearData([format]) — czyści pamięć danych z danych wskazanego opcjonalnie formatu;
jeśli format nie zostanie podany, czyszczona jest cała pamięć.
void setDragImage(obraz, x, y) — określa obraz wyświetlany podczas przeciągania oraz
położony w jego obrębie punkt, na który powinien wskazywać kursor.
void addElement(element) — dodaje przekazany element do pamięci danych.
Android 2.1+
Chrome 10.0+
Firefox 3.6+
Internet Explorer 6.0+
iOS Safari -
Opera -
Safari 3.2+
Przeciąganie i upuszczanie pomiędzy elementami div 299
Rysunek 13.1. Przykładowy wynik obrazujący prostą funkcjonalność przeciągania i upuszczania przy użyciu
znajdujących się z lewej strony notatek, które można przeciągać, oraz strefy upuszczania z prawej
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>13.1. Proste przeciąganie i upuszczanie</title>
<style>
#notes {
float:left;
height:220px;
width:50px;
text-align:center;
border-style:solid;
border-width:2px;
border-color:#333;
background-color:#666;
}
#board {
300 Rozdział 13. Zachowanie przeglądarek w HTML5
float:left;
width:300px;
height:220px;
border-style:solid;
border-width:2px;
border-color:#333;
background-image:url('corkboard.png');
}
</style>
<script>
// obsługa upuszczania
function dropHandler(tgt, evt) {
// spozycjonuj obrazek
oImg.style.left = newX+'px';
oImg.style.top = newY+'px';
</script>
</head>
<body>
<h1>Przeciągnij notatki na tablicę korkową:</h1>
<div id="notes">
<img id="note_blue" src="post_blue.png" onmousedown="getStartPos(event);"
´ondragstart="dragStartHandler(this, event);"><br>
<img id="note_green" src="post_green.png" onmousedown="getStartPos(event);"
´ondragstart="dragStartHandler(this, event);"><br>
<img id="note_pink" src="post_pink.png" onmousedown="getStartPos(event);"
´ondragstart="dragStartHandler(this, event);"><br>
<img id="note_yellow" src="post_yellow.png" onmousedown="getStartPos(event);"
´ondragstart="dragStartHandler(this, event);">
</div>
<div id="board" dropzone="copy" ondrop="dropHandler(this, event);" ondragover="return false;"></div>
</body>
</html>
W tym przepisie wykorzystamy Drag and Drop API do utworzenia matematycznej gry znanej jako
Wieże Hanoi, opracowanej przez matematyka Édouarda Lucasa w 1883 roku (więcej na temat historii gry
w Wikipedii, pod adresem http://pl.wikipedia.org/wiki/Wieże_Hanoi). Gracz ma do dyspozycji trzy słupki,
z których ten znajdujący się po lewej stronie zawiera początkowo szereg krążków (ich liczba może się
różnić w zależności od wersji gry), ułożonych od największego (na dole) do najmniejszego. Celem gry jest
przeniesienie stosu na słupek znajdujący się po prawej stronie, a pojedynczy ruch polega na przesunięciu
jednego i tylko jednego krążka albo na pusty słupek, albo na słupek zawierający krążek większy od przesuwanego.
Do utworzenia gry z zachowaniem jej reguł użyjemy zdarzeń Drag and Drop API i obiektu dataTransfer.
Jeśli podczas gry gracz spróbuje umieścić krążek na stosie, na którym znajduje się już mniejszy krążek,
program nie pozwoli go upuścić. Jeśli jednak ruch jest prawidłowy, krążek zostanie przesunięty i dodany
do stosu. Jeżeli użytkownik spróbuje przesunąć krążek, który nie znajduje się na szczycie danego stosu, nie
będzie mógł tego zrobić. Po każdym przesunięciu krążka na stos znajdujący się skrajnie po prawej stronie
program sprawdzi, czy użytkownik przesunął już cały stos — a jeśli tak, pojawią się gratulacje. Sprawdźmy,
w jaki sposób wykorzystać różne zdarzenia do egzekwowania reguł gry:
Wykorzystanie zdarzeń i obiektu dataTransfer 303
1. Utwórz stronę pokazaną na listingu 13.2, zawierającą znaczniki style oraz znaczniki krążków
i trzech stref upuszczania. Zauważ, że przy krążkach jest dodawany atrybut draggable, tak aby
przeglądarka wiedziała, że odpowiednie elementy div mogą być przeciągane.
2. Dodaj znaczniki script, funkcję initTowers oraz window.addEventListener do uruchamiania
inicjalizacji.
3. Dodaj do krążków procedury obsługi zdarzeń. Funkcje blockHandleDragStart oraz
blockHandleDragEnd pozwolą na przeciąganie tylko górnych krążków wież. Te funkcje zmienią
również styl na początku i końcu procesu przeciągania.
4. Oprócz obsługi krążków potrzebujemy obsługi strefy upuszczania. Dodaj funkcje obsługi stref
upuszczania wież, towerHandleDragOver, towerHandleDragLeave i towerHandleDragDrop, aby móc
zmieniać styl stref podczas przesuwania się nad nimi krążka oraz obsługiwać upuszczanie krążka
na wieżę.
5. Na początku skryptu dodaj deklaracje zmiennych globalnych przechowujących mapę krążków
należących do wież w grze (dwuwymiarową tablicę o nazwie towers), liczbę krążków w grze oraz
licznik ruchów wykonanych przez gracza.
Uwaga
Obrazki do tego przepisu możesz pobrać razem z listingami kodu (informacje o nich znajdziesz
we wstępie).
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>13.2. Gra w Wieże Hanoi</title>
<style>
.towerDropZone {
float: left;
height: 200px;
width: 200px;
margin: 5px;
padding: 15px;
position:relative;
background-color:#fff;
background-image:url(tower.png);
background-repeat:no-repeat;
background-position:bottom;
}
.towerDropZone.over {
border-radius: 15px 15px;
background-color:#EEE;
}
.tower {
width: 200px;
position:absolute;
bottom:1px;
border:none;
}
.block {
height: 25px;
304 Rozdział 13. Zachowanie przeglądarek w HTML5
margin:1px auto;
border: 1px solid #ccc;
border-radius: 15px 15px;
background-color:#00F;
color:#FFF;
text-align:center;
font-size:19px;
font-weight:bold;
}
</style>
<script>
// zadeklaruj mapę wież, liczbę krążków i licznik kroków
var towers = [[],[],[]];
var numblocks = 4;
var numMoves = 0;
return false;
}
Wykorzystanie zdarzeń i obiektu dataTransfer 305
// sprawdź, czy gra się zakończyła — czy wszystkie krążki znajdują się w dobrej kolejności na trzeciej wieży
if (towers[2].length==numblocks) {
306 Rozdział 13. Zachowanie przeglądarek w HTML5
// powiadom użytkownika
alert("Gratulacje — przesunąłeś wieżę.");
}
}
// pobierz identyfikator krążka oraz identyfikator wieży, na której się on początkowo znajduje
var blockId = this.id;
var fromTowerId = this.parentNode.parentNode.id;
// ustaw wartości zmiennych, żeby móc sprawdzić, czy uchwycony krążek znajduje się na górze wieży
var towerheight = towers[fromTowerId].length;
var topBlock = towers[fromTowerId][towerheight-1];
var thisBlock = parseInt(blockId);
} else {
// zwróć false
return false;
}
}
</script>
</head>
<body>
Wykorzystanie zdarzeń i obiektu dataTransfer 307
Teraz, kiedy zbudowałeś już stronę albo nawet zagrałeś kilka rund, przeciągając krążki w tę i z powrotem,
masz wyobrażenie tego, w jaki sposób działania gracza aktywują zdarzenia. Poświęćmy jednak kilka minut,
by omówić pewne szczegóły gry związane z funkcjonalnością przeciągania i upuszczania.
Po załadowaniu strony deklarowane są zmienne globalne: mapa gry (jeśli projektowałeś gry, w których
należy w jakiś sposób rejestrować położenie — gracza, figury itp. — mapa gry będzie Twoją drugą naturą;
w przeciwnym razie traktuj mapę jako reprezentację informacji o położeniu pionków gry w określonym
momencie), liczba krążków w grze oraz licznik ruchów gracza. Mapa gry jest w naszym przypadku
dwuwymiarową tablicą przechowującą informacje o tym, które krążki znajdują się na każdej z trzech wież.
Strefy upuszczania oraz wieże mają identyfikatory w postaci liczb całkowitych rozpoczynających się od zera
— identyfikatory te odpowiadają indeksom tablicy towers. Po uruchomieniu funkcji initTowers strona
rejestruje procedury obsługi strefy upuszczania dla każdej strefy upuszczania wieży, a następnie procedury
obsługi zdarzeń dla krążków, które będą przesuwane. Skrypt ustawia następnie aktualną mapę gry, przypisując
w tablicy wieży o numerze zero (pierwsza wieża) cztery krążki: 3, 2, 1, 0. Zauważ, iż dodawane są one
w odwróconej kolejności, co odzwierciedla fakt, że trzeci krążek jest większy od drugiego i tak dalej.
Kolejność ta jest dodatkowo wykorzystywana do dynamicznego ustawienia rozmiaru krążków poprzez
nadanie odpowiedniego stylu. Na tym kończy się inicjalizacja planszy gry.
Przyjrzyjmy się teraz przebiegowi gry. Kiedy użytkownik przeciąga krążek z jednej wieży na inną, w miarę
generowania zdarzeń dzieje się kilka rzeczy. Najpierw generowane jest zdarzenie rozpoczęcia przeciągania
i uruchamiana jest funkcja blockHandleDragStart. Funkcja ta sprawdza, czy wybrany krążek jest pierwszym
od góry krążkiem swojej wieży. Jeśli nie, funkcja zwraca false. Jeśli jest on górnym krążkiem, zostaje
utworzony łańcuch JSON zawierający pewne kluczowe informacje: identyfikator krążka oraz wieży, z której
jest on przeciągany. Dane te są następnie zapamiętywane za pomocą interfejsu DataTransfer, tak aby
informacja o wybranym krążku i wieży, z której jest przenoszony, była dostępna, kiedy krążek ten zostanie
upuszczony na nową wieżę. Aby pokazać użytkownikowi, że określony krążek jest przeciągany, w funkcji
zmieniana jest także przezroczystość krążka.
308 Rozdział 13. Zachowanie przeglądarek w HTML5
Kiedy krążek jest następnie przeciągany nad strefę upuszczania albo wieżę, generowane jest zdarzenie
dragover i uruchamiana funkcja towerHandleDragOver. Zadaniem tej funkcji jest powiadomienie gracza,
że krążek znalazł się nad strefą upuszczania. Jest to realizowane poprzez zmianę stylu obszaru div wieży,
nad którą przeciągany jest krążek, co pokazano na rysunku 13.2.
Rysunek 13 2. Przykładowy zrzut ekranu pokazujący trzeci krążek przeciągany z pierwszej do ostatniej wieży.
Zdarzenie dragstart było wygenerowane już wcześniej, a zdarzenie dragover zostało wygenerowane
po przeciągnięciu krążka nad strefę upuszczania
Kluczową sprawą jest jednak uwzględnienie polecenia preventDefault. Jak dowiedziałeś się z poprzedniego
przepisu, polecenia tego należy użyć, aby zapobiec wykonaniu przez przeglądarkę domyślnego działania,
które może spowodować problemy związane z przebiegiem gry.
Jeśli krążek jedynie „przechodzi” nad strefą upuszczania wieży, zostanie wygenerowane zdarzenie
dragleave i wykonana funkcja towerHandleDragLeave. Funkcja ta zmienia styl opuszczanej strefy upuszczania
wieży z powrotem na normalny.
Kiedy krążek zostaje upuszczony na strefę upuszczania wieży, wykonywane jest zasadnicze posunięcie
zawarte w funkcji towerHandleDragDrop. Po uruchomieniu tej funkcji najpierw zostaje wywołana metoda
preventDefault, która ponownie ma za zadanie zapobiec wykonaniu przez przeglądarkę domyślnego
działania. Następnie z obiektu dataTransfer są pobierane informacje, które zostaną wykorzystane przez
algorytm gry. Kluczem jest tutaj określenie, czy krążek może być upuszczony, co odbywa się poprzez
sprawdzenie na mapie gry, czy wieża, na którą krążek jest upuszczany, jest pusta lub czy znajduje się już na
niej większy krążek. Jeśli wieża zawiera mniejszy krążek, przesuwany krążek nie może być do niej dodany,
a jako rezultat funkcji zwracana jest wartość false. Jeśli krążek może być dodany, zostaje „wstawiony” albo
przed górnym krążkiem (po to, aby wyświetlać krążki prawidłowo), albo przed znacznikiem znajdującym
się na wieży, którym jest znacznik akapitu (<p id="px">). Po dodaniu krążka kod aktualizuje mapę gry,
zwiększa licznik ruchów i na końcu sprawdza, czy gracz ukończył grę.
W celu sprawdzenia, czy gracz ukończył grę, wykonywany jest bardzo prosty test — sprawdzenie, czy
ostatnia wieża zawiera wszystkie cztery krążki. Ponieważ reguły gry zapobiegają umieszczeniu na wieży
mniejszego krążka poniżej większego, nie ma potrzeby weryfikowania kolejności krążków. Kiedy zadanie jest
rozwiązane, gracz nie powinien mieć możliwości dalszego przesuwania krążków, co realizujemy poprzez
ustawienie atrybutu draggable każdego krążka na false. To kończy grę — aby zagrać ponownie, gracz musi
jeszcze raz załadować stronę.
Grę można znacznie ulepszyć na przykład poprzez zmianę koloru strefy zrzutu na podstawie informacji,
czy krążek może być upuszczony na jakimś obszarze. Teraz, gdy rozumiesz już przepływ zdarzeń w Drag
and Drop API oraz wiesz, jak dodawać reguły postępowania zależne od zdarzeń, pozostawiamy Ci dodanie
podobnych funkcji do samodzielnego wykonania, o ile tylko masz na to ochotę.
Pamięć podręczna aplikacji i jej API 309
W obu przykładach dotyczących Drag and Drop API za pomocą interfejsu DataTransfer przekazywaliśmy
proste ciągi tekstowe. Jak wspomniano wcześniej, interfejs DataTransfer może obsługiwać wiele rodzajów
przenoszonej informacji, w tym obrazki, pliki itp. Przykłady obejmują jedynie podstawy uzyskiwania
poprawnego rozwiązania wykorzystującego przeciąganie i upuszczanie. Istnieje wiele innych funkcji Drag
and Drop API, np. inne zachowania, przeciągane obiekty oraz przeciąganie z pulpitu do przeglądarki.
W rozdziale 14. dowiesz się, w jaki sposób przy użyciu pewnych szczególnych funkcji interfejsu DataTransfer
realizowane jest przeciąganie i upuszczanie plików spoza przeglądarki do strefy upuszczania na stronie.
Wskazówka
Przy debugowaniu swojego programu wykorzystującego przeciąganie i upuszczanie możesz czasem
napotkać pewne trudności. Jeśli za każdym razem, kiedy upuszczasz obiekt na swojej strefie
upuszczania, przeglądarka uruchamia nową kartę albo okno zawierające jedynie obiekt, który
przeciągasz, oznacza to najprawdopodobniej, że opuściłeś wywołanie na generowanym zdarzeniu
metody preventDefault. Bez wywołania tej metody przeglądarki wykonują swoje domyślne procesy
obsługi upuszczania w oparciu o rodzaj przeciąganego obiektu. Na przykład w Firefoksie przy próbie
przeciągnięcia i upuszczenia obrazka na strefę upuszczania, która nie wykonuje metody preventDefault,
zostanie otwarta nowa karta pokazująca jedynie przeciągany obrazek.
Android 2.1+
Chrome 10.0+
Firefox 3.6+
Internet Explorer -
iOS Safari 3.2+
Opera 10.6+
Safari 4.0+
Pamięć podręczna aplikacji to lista plików przechowywanych w pliku manifestu, które przeglądarka
powinna pobrać i zapisać, tak aby kiedy użytkownik powróci na stronę, pliki te były gotowe do wykorzystania.
Jest to niezwykle przydatne, jeśli nie masz połączenia z siecią, lecz nawet jeśli je masz, przyspiesza czas
wczytywania strony, ponieważ odpowiednie pliki zostały już załadowane.
Każda strona, która odwołuje się do manifestu, będzie automatycznie zapisywana, ale nie oznacza to,
że powinieneś przechowywać wszystko offline (weź pod uwagę ograniczenia związane z rozmiarem i typem
pliku). Powinieneś dbać o odpowiednie z punktu widzenia użytkownika działanie aplikacji. Offline powinny
być zapisywane kluczowe pliki aplikacji.
310 Rozdział 13. Zachowanie przeglądarek w HTML5
Uwaga
Każdy plik (.html, .php itp.), do którego odwołanie znajduje się w pliku manifestu, zostanie pobrany
i zapisany w pamięci podręcznej.
Twój serwer może nie wiedzieć, co zrobić z plikiem .appcache, więc prawdopodobnie będziesz musiał
dodać do jego konfiguracji nowy typ MIME. Możesz tak zrobić w przypadku IIS, a jeśli używasz serwera
Apache, wykorzystaj następujący kod:
AddType text/cache-manifest .appcache
Uwaga
Nie polecamy tworzenia pliku manifestu przed zakończeniem pracy nad aplikacją lub witryną
— w przeciwnym razie w pamięci podręcznej buforowane będą nieukończone pliki, które mogą
zakłócać tworzenie tej aplikacji lub witryny. Czyszczenie albo odświeżanie pamięci podręcznej
nie jest tak proste jak odświeżanie strony (omówimy to w dalszej części tego rozdziału).
Tworzenie pliku manifestu 311
Następnie możesz po prostu zacząć wymieniać pliki, które mają być zapisywane, jak pokazano poniżej:
CACHE MANIFEST
css/main.css
js/jquery.js
images/logo.png
I to w zasadzie wszystko. Jeśli utworzyłeś stronę HTML i dołączyłeś plik manifestu z listą plików,
po odwiedzeniu strony online zostanie ona zapisana wraz z dodatkowymi plikami w pamięci podręcznej.
Jeśli następnie zamkniesz połączenie internetowe i odświeżysz stronę, wyświetli się ona jak zwykle. Pliki
wymienione w manifeście mogą mieć ścieżki względne albo bezwzględne.
Możesz jednak zrobić więcej. Możesz podzielić plik manifestu na trzy części, tj. CACHE, FALLBACK oraz
NETWORK:
CACHE — lista plików, które należy przechowywać w urządzeniu przeglądarki klienta w celu ich
wykorzystania, gdy połączenie internetowe jest niedostępne.
FALLBACK — lista plików i ich zamienników, na wypadek gdyby połączenie z internetem nie było
dostępne.
NETWORK — pliki, które wymagają połączenia i nie powinny być buforowane.
CACHE
Fragment z nagłówkiem CACHE jest zasadniczo taki sam jak w poprzednim przykładzie. Pliki wymienione tak
jak powyżej albo w części z nagłówkiem CACHE są „jawnie wyspecyfikowane” i zostaną pobrane i zbuforowane
w przeglądarce. Podczas buforowania plików nie możesz używać symboli wieloznacznych, więc maska css/*
nie zadziała.
Ostrzeżenie
Samego manifestu nie zapisuj w pamięci podręcznej, ponieważ poinformowanie przeglądarki o nowym
manifeście stanie się wówczas prawie niemożliwe.
FALLBACK
Część manifestu mająca nagłówek FALLBACK może być wykorzystana do wykrywania, czy użytkownik jest
online, czy offline. Za pomocą sekcji FALLBACK można poinstruować przeglądarkę, którego pliku użyć, jeśli
określony plik jest niedostępny, ponieważ użytkownik pracuje offline. Na przykład duży plik CSS, który
nie jest konieczny do pracy offline, można pominąć w części CACHE i zamiast tego wykorzystać FALLBACK,
jak pokazano poniżej:
CACHE MANIFEST
FALLBACK:
online.css offline.css
W tym przykładzie plik manifestu mówi przeglądarce, aby w przypadku gdy strona HTML odwołuje się
do pliku online.css, a użytkownik pracuje offline, użyła pliku offline.css (zauważ, że nazwy plików są oddzielone
spacją), który został zbuforowany. Przydatne, prawda? Ten sposób nie jest ograniczony jedynie do plików
CSS, lecz może być także użyty w przypadku obrazków, plików javascriptowych, stron HTML, a nawet filmów.
Wystarczy umieścić każdą deklarację w nowym wierszu.
312 Rozdział 13. Zachowanie przeglądarek w HTML5
Używając nagłówka FALLBACK, możesz wykorzystać symbole wieloznaczne. Gdybyś chciał, aby wszystkie
pliki miały określony plik zastępczy, mógłbyś wykorzystać następujący zapis:
/ /offline.html
Jeśli zatem użytkownik pracuje offline, zamiast każdego pliku, którego nazwa znajduje się po ukośniku
(np. /page1.html), zostanie wyświetlony plik offline.html. Pamiętaj jednak, że po otwarciu dowolnego pliku,
do którego odwołanie znajduje się w pliku manifestu, plik ten zostanie pobrany i będzie dostępny offline.
Można pójść dalej i wskazać w sekcji FALLBACK zastępcze pliki dla zawartości określonych katalogów
lub po prostu dla wszystkich stron HTML:
/js/ /offline.js
*.html /offline.html
NETWORK
Fragment z nagłówkiem NETWORK mówi przeglądarce, które pliki mają być dostępne tylko przy aktywnym
połączeniu, i zapobiega pobieraniu tych plików. Zasoby znajdujące się w tej części wymagają zwykle połączenia
z bazą danych albo serwerem — przykładem może być strona logowania:
CACHE MANIFEST
NETWORK:
login.aspx
CACHE:
magic.js
style.css
Odświeżenie pamięci podręcznej może być wykonane po prostu przez zmianę komentarza.
pracuje on online. Jeśli użytkownik pracuje offline, zostanie wyświetlona statyczna strona z informacjami
kontaktowymi. W tym przepisie wykorzystamy:
trzy strony HTML,
jeden plik javascriptowy,
dwa pliki CSS,
dwa obrazki.
Na pierwszej stronie HTML znajdują się dwa pola wejściowe (oczywiście z nowymi typami pól wejściowych
HTML5), a kliknięcie przycisku spowoduje pomnożenie obu liczb (w tym przykładzie nie ma walidacji).
Po kliknięciu przycisku Wyślij zostanie wyświetlony wynik oraz pojawi się możliwość zgłoszenia za pomocą
formularza kontaktowego obliczonego wyniku do fikcyjnej firmy. Formularz kontaktowy zostanie jednak
wyświetlony jedynie wówczas, gdy aktywne jest połączenie internetowe, w związku z czym zadeklarowana
zostanie rezerwowa możliwość (FALLBACK), którą jest strona zawierająca wyłącznie numer telefonu.
W tej aplikacji wykorzystujemy również plik CSS, który określa pewne style układu, oraz inny plik CSS,
który ładuje ładną czcionkę (mogłaby ona pochodzić na przykład z usługi oferującej czcionki internetowe).
Chcemy, aby ta ładna czcionka była wyświetlana tylko wówczas, gdy użytkownik pracuje online.
Ponadto strona będzie zawierać obrazek, który pokaże Ci, czy użytkownik jest online (znak wyboru),
czy offline (znak X).
Listing 13.3 pokazuje kod HTML dla tej aplikacji, a listing 13.4 prezentuje plik manifestu. Na rysunku
13.4 możesz zobaczyć aplikację w akcji.
<!DOCTYPE html>
<html manifest="listing.13.4.appcache">
<head>
<meta charset="utf-8">
<title>Offline</title>
<script src="13_3_calculation.js"></script>
<link rel="stylesheet" href="13_3_offline_style.css" />
<link rel="stylesheet" href="13_3_offline_style2.css" />
</head>
<body>
<h1>Oblicz</h1>
<label for="first">Pierwsza liczba</label>
<input required type="number" id="first" name="first" />
<label for="second">Druga liczba</label>
<input required type="number" id="second" name="second" />
<input type="submit" onclick="calculate()" value="Oblicz" />
<div id="resultHolder">
<h2>Otrzymany wynik to <span id="result"></span></h2>
<p>Znając już wynik, możesz <a href="13_3_offline_contact.html">się z nami skontaktować</a></p>
</div>
<img src="connection-tick.gif" width="50" height="50" alt="">
</body>
</html>
314 Rozdział 13. Zachowanie przeglądarek w HTML5
CACHE MANIFEST
# Version 1.0
CACHE:
13_3_calculation.js
13_3_offline_style.css
NETWORK:
13_3_offline_style2.css
FALLBACK:
connection-tick.gif connection-cross.gif
13_3_offline_contact.html 13_3_offline_offline.html
Jeśli wypróbujesz ten przykład podczas pracy online, zobaczysz ładną czcionkę Comic Sans oraz graficzny
znak wyboru (patrz rysunek 13.4); jeśli jednak przejdziesz do pracy offline i odświeżysz stronę, aplikacja nie
będzie miała przypisanej czcionki i będzie pokazywać znak X (patrz rysunek 13.5). Jeśli zakończysz obliczenia
i spróbujesz uzyskać dostęp do formularza kontaktowego, zamiast niego zobaczysz stronę „Zadzwoń do nas”,
pokazaną na rysunku 13.6.
Rysunek 13.6. Strona zastępcza dla formularza kontaktowego podczas pracy offline
API pamięci podręcznej aplikacji 315
W przypadku wykorzystywania pamięci podręcznej aplikacji dostępnych jest wiele możliwości. Twoja
aplikacja może się w całości znajdować na jednej stronie, ale może także być rozłożona na kilka stron, więc
musisz rozważyć, które pliki zapisać, a które wymagają dostępu do internetu.
Istnieją pewne różnice pomiędzy przeglądarkami. IE9 nie obsługuje w pełni aplikacji internetowych
offline. Choć wydaje się prawidłowo zapisywać początkową stronę, CSS oraz JavaScript w pamięci podręcznej
i działa offline, nie powinieneś polegać na poprawnym działaniu tej pamięci. Chrome, Firefox i Safari
działają zgodnie z oczekiwaniami i wyświetlają zastępczy obrazek oraz stronę HTML. Opera 11 wyświetla
zastępczy obrazek, ale przy próbie połączenia się z formularzem zamiast strony zastępczej wyświetla stronę
błędu połączenia.
W obiekcie ApplicationCache znajduje się kilka wymienionych poniżej zdarzeń. Domyślnie przeglądarka
udostępnia zdarzenie ładowania strony, ale API pamięci podręcznej aplikacji pozwala wykorzystać inne
zdarzenia:
checking — przeglądarka pobiera manifest po raz pierwszy albo sprawdza, czy plik był modyfikowany;
noupdate — manifest nie zmienił się;
downloading — jeśli przeglądarka pierwszy raz odwołuje się do manifestu, pobiera odpowiednie
zasoby; w przeciwnym przypadku pobiera aktualizację po jej wykryciu;
progress — przeglądarka pobiera wymieniony zasób;
cached — zasoby zostały pobrane i zapisane;
updateready — zasoby z manifestu zostały pobrane, a pamięć podręczna przeglądarki może
zostać uaktualniona; można następnie wywołać funkcję swapCache, która musi być wywołana,
aby przeglądarka używała najnowszego manifestu — inaczej przeglądarka będzie nadal korzystać
ze starej pamięci podręcznej;
316 Rozdział 13. Zachowanie przeglądarek w HTML5
Powyższe zdarzenia dają możliwość poinformowania użytkownika, kiedy aktualizacja jest gotowa,
a następnie wywołania funkcji swapCache. Możesz tego dokonać, używając metody addEventListener:
applicationCache.addEventListener('updateready', function() {
//zrób coś
alert("Aktualizacja pamięci podręcznej")
applicationCache.swapCache();
})
Notification API
Notification API, znane również jako Web Notifications API, jest nowym API, które pozwala stronom
przeglądarki przekazywać użytkownikom informacje za pomocą powiadomień na pulpicie. API to jest
stosunkowo młode i aktualnie obsługiwane jedynie przez specyfikację WebKit i oparte o nią przeglądarki.
Wywołania za pomocą interfejsu powiadomień silnika WebKit są podobne do tych zdefiniowanych w roboczej
specyfikacji W3C, więc kiedy Web Notifications API zostanie już zaimplementowane we wszystkich
przeglądarkach, zmiany powinny być minimalne.
Podczas korzystania z innych aplikacji użytkownicy często umieszczają okno przeglądarki w tle. Kiedy
strona przeglądarki powiadamia użytkownika, że jego sesja niedługo utraci ważność, kurs akcji osiągnął
poziom, przy którym należy je sprzedać, czy też zaszło inne istotne dla użytkownika zdarzenie, powiadomienie
to może być przesłonięte innymi oknami. Za pomocą Notification API strona przeglądarki może teraz poprosić
pulpit użytkownika o wyświetlenie powiadomienia w jej imieniu. Powiadomienie zostanie wyświetlone
w sposób podobny do powiadomień z innych zainstalowanych aplikacji, zwykle w rogu ekranu.
Notification API silnika WebKit udostępnia dwa interfejsy: NotificationCenter i Notification. Interfejs
NotificationCenter daje możliwość poproszenia użytkownika o pozwolenie na wyświetlanie powiadomień,
odczytania statusu tego pozwolenia oraz tworzenia powiadomień. Interfejs ten obejmuje następujące metody:
Notification createNotification(in DOMString urlIkony, in DOMString tytuł, in DOMString
treść) — tworzy instancję powiadomienia na podstawie przekazanych w parametrach danych
(ikony, tytułu i treści powiadomienia).
Notification createHTMLNotification(in DOMString url) — tworzy powiadomienie w oparciu
o stronę HTML.
Int checkPermission() — pobiera poziom uprawnień dotyczący powiadomień dla bieżącej domeny.
void requestPermission([in Function wywołanieZwrotne]) — prosi użytkownika o pozwolenie
na wyświetlanie powiadomień.
Jak łatwo zauważyć, do tworzenia powiadomień służą dwie różne metody interfejsu: createNotification
i createHTMLNotification. Metoda createNotification pobiera parametry wykorzystywane do wyświetlenia
standardowego powiadomienia pulpitu: ikonę, tytuł i treść. Nie udostępnia ona innego sposobu dostosowania
powiadomienia do własnych potrzeb oprócz podania tych wartości. W przypadku createHTMLNotification
do metody jest przekazywany adres URL strony HTML. Strona znajdująca się pod tym adresem może posiadać
znaczniki formatujące i inne elementy realizujące dodatkowe funkcje, na przykład łącza internetowe. W obu
przypadkach zwracana jest instancja interfejsu Notification.
Po utworzeniu instancji do sterowania wyświetlaniem powiadomienia można wykorzystać metody
interfejsu Notification. W interfejsie Notification z silnika WebKit znajdują się dwie metody, show i cancel,
opisane poniżej:
show — dodaje powiadomienie do kolejki pulpitu w celu wyświetlenia, jeśli uprawnienia
na to pozwalają.
cancel — usuwa powiadomienie z pulpitu lub kolejki.
Interfejs Notification posiada również funkcje obsługi zdarzeń udostępniających szczegółowe informacje:
ondisplay — powiadomienie zostało wyświetlone na pulpicie.
onclose — powiadomienie zostało zamknięte — ręcznie lub za pomocą metody cancel().
onerror — wystąpił błąd dotyczący powiadomienia.
W ostatnim przepisie tego rozdziału zajmiemy się wykorzystaniem zdarzeń interfejsu Notification.
Weź pod uwagę, że prośba o udzielenie pozwolenia może zostać wyświetlona jedynie po celowym działaniu
użytkownika, takim jak kliknięcie przycisku albo łącza. Prośba o pozwolenie po załadowaniu strony nie da
żadnego efektu i nie zostanie wyświetlona użytkownikowi z powodów związanych z bezpieczeństwem. Ważne
jest także, aby uświadomić sobie, że pozwolenia na powiadomienia są ustawiane dla domen, a nie stron.
Jeśli więc użytkownik zablokuje powiadomienia dla jednej z Twoich stron, będą one blokowane dla całej
domeny. Jeśli użytkownik nie pozwolił na wyświetlanie powiadomień, a mimo to zostały one utworzone, próba
powiadomienia nie zakończy się niepowodzeniem, ale nie zostanie ono również wyświetlone. W ostatnim
przepisie tego rozdziału dowiesz się, jak sprawdzić aktualny „poziom uprawnień” wybrany przez użytkownika
dla domeny.
318 Rozdział 13. Zachowanie przeglądarek w HTML5
Kompatybilność przeglądarek
Tabela 13.3 przedstawia wersję każdej z przeglądarek obsługujących Notification API. Zauważ jednak,
że Notification API jest zaimplementowany w sposób specyficzny dla silnika WebKit, ponieważ ogólna
specyfikacja ma status wersji roboczej. Oczekuje się, że ogłoszona ostatecznie specyfikacja będzie opisywała
sposób pokazywania powiadomień bez wykorzystywania silnika WebKit.
Android -
Chrome 10.0+
Firefox -
Internet Explorer -
iOS Safari -
Opera -
Safari -
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
Wyświetlanie prostego powiadomienia 319
// inicjalizuj stronę
function init() {
// odwołania do przycisków
var btnSetPermission = document.getElementById('setPermission');
var btnFireNotification = document.getElementById('fireNotification');
</script>
</head>
<body>
<h1>Przepis na proste powiadomienia</h1>
<p>Kliknij „Ustaw uprawnienia”, by zezwolić na pojawianie się powiadomień, a potem utwórz powiadomienie.</p>
<section>
<button id="setPermission">Ustaw uprawnienia</button>
<button id="fireNotification">Utwórz powiadomienie</button>
</section>
</body>
</html>
320 Rozdział 13. Zachowanie przeglądarek w HTML5
Po załadowaniu kodu z listingu 13.5 do przeglądarki zostanie uruchomiona funkcja init, która doda
procedury obsługi zdarzeń kliknięcia do obu przycisków: Ustaw uprawnienia i Utwórz powiadomienie.
Najpierw kliknij przycisk Ustaw uprawnienia, aby przeglądarka wyświetliła pytanie o pozwolenie na
wyświetlanie powiadomień. (Pamiętaj, aby uruchomić przepis w Chrome albo innej przeglądarce bazującej
na silniku WebKit). Po wyświetleniu zapytania o uprawnienia wybierz opcję zezwolenia na wyświetlanie
powiadomień, co poinformuje przeglądarkę, że każda ze stron domeny może żądać wyświetlenia
powiadomienia na pulpicie. W ramach funkcji setPermission sprawdzamy najpierw, czy webkitNotifications
jest dostępny. Jeśli tak, skrypt wykonuje metodę requestPermission tego interfejsu. Spowoduje to wyświetlenie
użytkownikowi pytania dotyczącego uprawnień. Jeśli wcześniej zezwoliłeś na wyświetlanie powiadomień,
ponowne wywołanie requestPermission nie będzie miało żadnych skutków. W celach testowych można
usunąć uprawnienie dotyczące danej domeny, co jest opisane w poniższej wskazówce.
Wskazówka
Podczas pracy z powiadomieniami zauważysz, że po zablokowaniu lub zezwoleniu na wyświetlanie
powiadomień nie można już użyć metody requestPermission do ponownego zapytania użytkownika
o zgodę lub jej brak. Aby wyczyścić preferencje dotyczące powiadomień, należy przejść do ustawień
przeglądarki i ręcznie usunąć pozwolenie. W przeglądarce Chrome opcję tę można znaleźć poprzez
Ustawienia/Pokaż ustawienia zaawansowane…/Prywatność — Ustawienia treści…/Powiadomienia/
Zarządzaj wyjątkami…, jak pokazano na rysunku 13.8.
Rysunek 13.9. Lista Wzorzec nazwy hosta w przeglądarce Chrome, pozwalająca usunąć
informację o uprawnieniach dotyczących powiadomień
Uwaga
Każdy system ma ograniczenie co do liczby jednocześnie wyświetlanych powiadomień. Jeśli utworzysz
powiadomienie, a pulpit wyświetla aktualnie ich maksymalną liczbę, to nowe powiadomienie zostanie
umieszczone w kolejce i będzie tam, dopóki użytkownik nie usunie z pulpitu któregoś z wyświetlanych
powiadomień albo nie zostanie ono usunięte automatycznie.
Uwaga
Twitter API ma możliwość wykonywania za pośrednictwem Twitter REST API funkcji zwrotnych. W tym
przepisie wykorzystamy tę technikę do wykonywania bezpośrednich wywołań javascriptowych do API
Twittera. Z tego sposobu oraz formatu JSONP musimy skorzystać w celu obejścia kwestii bezpieczeństwa
żądań pomiędzy różnymi domenami, które występują zwykle przy podobnych żądaniach przesyłanych
przez klienta. Do wywołania metody można wykorzystać zewnętrzną bibliotekę, taką jak jQuery, lub
nawet zbudować w domenie własny serwer pośredniczący. Zauważ, że w celu ochrony przed nadużyciami
API Twittera ma, tak jak większość internetowych API, ograniczenia częstości wywołań, więc jeśli
będziesz go używał zbyt często, możesz otrzymać błąd „over rate limit” („przekroczone ograniczenie
częstości wywołań”).
Oprócz metod createNotification i show, które poznałeś w poprzednim przepisie, użyjemy również
metody cancel, dzięki której po pewnym czasie automatycznie usuniemy powiadomienia. W celu
Tworzenie strony powiadomień o tweetach 323
Zwykle, aby wywołać zewnętrzną usługę sieciową z użyciem funkcji zwrotnej, zapisujemy adres URL
wywołania REST w atrybucie src znacznika script. W tym przepisie przedstawimy interesujący alternatywny
sposób z wykorzystaniem wątku roboczego (patrz rozdział 12.), który za pomocą funkcji importScripts
otrzymuje wyniki, parsuje je, a następnie przesyła z powrotem do głównego wątku. Dzięki temu główny
wątek nie jest blokowany i może podczas odwoływania się do Twittera kontynuować pracę, a wyniki zostają
przetworzone — po prostu za każdym razem, kiedy chcesz pobrać wyniki, tworzony jest nowy wątek roboczy,
który po przetworzeniu wyników kończy działanie. Wątek roboczy nie może jednak utworzyć powiadomienia,
gdyż potrzebowałby w tym celu obiektu window, który nie jest dostępny w wątku roboczym. Odpowiednia
informacja jest wobec tego wysyłana do głównego wątku, a ten pokazuje powiadomienie. W związku
z przyjęciem takiego modelu działania kod przepisu został podzielony na dwa pliki. Listing 13.6 przedstawia
główną stronę, a listing 13.7 kod wątku roboczego. Zacznijmy od strony głównej przedstawionej na listingu 13.6:
1. Utwórz pustą stronę z odpowiednimi znacznikami i przyciskami Ustaw uprawnienia, Sprawdź
poziom uprawnień oraz Pobierz tweety.
2. Dodaj znaczniki script oraz zmienne globalne tworker i lastTweetId.
3. Dodaj funkcję setPermission, służącą do poproszenia o zezwolenie na wyświetlanie powiadomień
na pulpicie.
4. Dodaj funkcję checkPermissionLevel, służącą do pobierania i wyświetlania bieżącego poziomu
uprawnień.
5. Dodaj funkcję grabTweets, która uruchamia wątek roboczy, przypisuje procedurę nasłuchu
dotyczącą jego komunikatów oraz obsługuje tworzenie powiadomień.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>13.6. Powiadomienia z Twittera</title>
324 Rozdział 13. Zachowanie przeglądarek w HTML5
<script>
btnSetPermission.addEventListener('click',setPermission,false);
btnCheckPermissionLevel.addEventListener('click',checkPermissionLevel,false);
btnGrabTweets.addEventListener('click',grabTweets,false);
}
// sprawdź powiadomienia
function checkPermissionLevel() {
console.log('tworzenie powiadomienia');
// utwórz powiadomienie
var notification = webkitNotifications.createNotification(nIcon, nTitle, nBody);
// wyświetl powiadomienie
notification.show();
break;
break;
}
}, false);
</script>
</head>
<body>
<h1>Tworzenie powiadomień z nowych tweetów</h1>
Najpierw kliknij „Ustaw uprawnienia”, aby zezwolić na pokazywanie powiadomień.<br>
Następnie kliknij „Pobierz tweety”, aby rozpocząć pobieranie nowych tweetów.
<br><br>
<button id="btnSetPermission">Ustaw uprawnienia</button>
<button id="btnCheckPermissionLevel">Sprawdź poziom uprawnień</button>
<button id="btnGrabTweets">Pobierz tweety</button>
</body>
</html>
Po ukończeniu strony głównej należy utworzyć plik javascriptowy wątku roboczego służącego
do pobierania i parsowania tweetów — patrz listing 13.7:
1. Utwórz plik .js wątku roboczego z procedurą obsługi zdarzeń onmessage, dotyczących otrzymywania
komunikatów z głównej strony. Obsługa zdarzenia onmessage uwzględnia również import skryptu
API REST ze strony Twitter.com.
2. Dodaj funkcję getResult, która jest funkcją wywołania zwrotnego z wywołania API REST z funkcji
importScripts. Funkcja getResult będzie obsługiwać parsowanie wszystkich pobranych tweetów
i wysyłanie ich do głównego wątku w celu wyświetlenia. I to tyle, jeśli chodzi o wątek roboczy.
// listing.13.7.js
// Wątek roboczy pobierający tweety
// zapisz id_str jako największy identyfikator, aby nie pokazywać tweetów ponownie
maxId = data[i].id_str;
}
}
Po załadowaniu w przeglądarce głównej strony, ale przed kliknięciem przycisku Ustaw uprawnienia,
kliknij przycisk Sprawdź poziom uprawnień. Spowoduje to wywołanie funkcji checkPermissionLevel, która
sprawdzi, czy webkitNotifications jest dostępny, a następnie wykona metodę checkPermission, pobierając
328 Rozdział 13. Zachowanie przeglądarek w HTML5
aktualny poziom uprawnień w postaci wartości liczbowej, oraz wyświetli odpowiadające tej wartości
wyjaśnienie w postaci okna dialogowego. Dzięki temu szybko stwierdzisz, czy uprawnienia do wyświetlania
powiadomień na pulpicie są dla domeny poprawnie nadane. Możesz poćwiczyć, usuwając uprawnienia
w ustawieniach przeglądarki, blokując powiadomienia, zezwalając na nie oraz oglądając zmiany poziomu
uprawnień. Jeśli nie nadałeś uprawnień do wyświetlania powiadomień, zrób to przed kontynuowaniem
pracy z przepisem.
Po wczytaniu kodu skrypt ustawia globalne odwołanie do wątku roboczego o nazwie tworker oraz
zmienną globalną o nazwie lastTweetId, która będzie przechowywać „zakładkę” dotyczącą ostatniego
tweeta odczytanego dla użytkownika. Podczas ładowania strony skrypt ustawia wartość tej zmiennej na zero,
co oznacza, że strona została właśnie załadowana i żadne tweety nie zostały odczytane. Po uruchomieniu
wątku roboczego w celu pobrania listy tweetów zostanie mu przekazana wartość zmiennej lastTweetId
służąca do określenia, które tweety należy wyświetlić. Wartość tej zmiennej należy przechowywać na głównej
stronie, gdyż po zakończeniu pobierania i parsowania tweetów wątek roboczy zniknie. Przy każdym
odpytywaniu o tweety tworzony jest nowy wątek roboczy.
Aby rozpocząć proces pobierania tweetów dla użytkownika i wyświetlania powiadomień, kliknij przycisk
Pobierz tweety. Spowoduje to uruchomienie funkcji grabTweets, która najpierw doda do dziennika konsoli
komunikat potwierdzający rozpoczęcie procesu pobierania tweetów, a następnie utworzy nowy wątek
roboczy z pliku listing.13.7.js. Wątek roboczy jest tak zaprojektowany, że nie podejmie żadnych działań,
dopóki za pomocą komunikatu nie zostanie poinstruowany, aby rozpocząć przetwarzanie, więc w tym
miejscu przyjrzyjmy się pozostałej części funkcji grabTweets. W kolejnym kroku funkcja ustawia procedurę
obsługi zdarzenia message wątku roboczego, tak aby po wysłaniu przez wątek roboczy komunikatu można
go było obsłużyć. Wykorzystany będzie tu podobny sposób przekazywania wiadomości jak w przypadku
wątków roboczych z przepisów z rozdziału 12. — wątek może wysyłać różne rodzaje komunikatów o treści
w formacie JSON, zawierającej typ komunikatu i związane z tym typem parametry.
Z wątku roboczego do głównej strony są przesyłane dwa różne rodzaje komunikatów: TWEET oraz END.
Jeśli otrzymasz msgType o wartości TWEET, oznacza to, że wątek roboczy znalazł tweet do wyświetlenia. W takim
przypadku pozostała część komunikatu posłuży do wyświetlenia powiadomienia zawierającego ikonę
z awatarem użytkownika, nazwę użytkownika jako tytuł oraz tweet jako treść powiadomienia. W tym
przepisie kod tworzy powiadomienie, dodaje procedury obsługi zdarzeń ondisplay, onclose oraz onerror
i w końcu wyświetla powiadomienie — procedury obsługi zdarzeń onclose i onerror po prostu zapisują
w dzienniku konsoli komunikat informujący o przechwyceniu zdarzenia; procedura obsługi zdarzenia
ondisplay służy do odtworzenia dźwięku przy wyświetleniu powiadomienia. Następnie zostaje ustawiony
limit czasu, aby automatycznie usunąć z pulpitu powiadomienie, jeśli użytkownik go jeszcze nie zamknął.
Wreszcie, metoda show() wyświetla powiadomienie.
Komunikat END będzie oznaczać, że wątek roboczy zakończył pobieranie i przetwarzanie tweetów.
Dzięki temu, że wątek roboczy informuje o zakończeniu pracy, możesz się upewnić, iż w tym samym czasie
nie działa wiele wątków, oraz odczekać pewien czas pomiędzy zakończeniem pracy przez jeden z wątków
a jej rozpoczęciem przez następny. Komunikat END może zawierać identyfikator ostatniego tweeta albo,
jeśli nie pobrano żadnych tweetów, wartość zero. W razie potrzeby skrypt aktualizuje zakładkę wartością
identyfikatora ostatniego tweeta, aby zaznaczyć, która wiadomość została pobrana jako ostatnia. Po ustawieniu
identyfikatora ostatniego tweeta skrypt — w celu ponownego uruchomienia funkcji grabTweets — ustawia
odmierzanie czasu. W tym przepisie nie zawracaliśmy sobie głowy dodawaniem funkcji zatrzymywania
działania, ale można to łatwo zrealizować — czy to przy użyciu flagi zapobiegającej przyszłemu pobieraniu
danych, czy też, w razie potrzeby, poprzez anulowanie działania wątku roboczego. Na końcu funkcji
Podsumowanie 329
grabTweets kod nakazuje wątkowi roboczemu rozpoczęcie pracy, przesyłając do niego komunikat zawierający
zakładkę lastTweetId.
Przejdźmy teraz do wątku roboczego i omówmy pokrótce proces pobierania tweetów użytkownika.
Wątek roboczy obejmuje zasadniczo dwie sekcje: żądanie podania informacji za pomocą procedury obsługi
zdarzenia onmessage oraz parsowanie zbioru wyników w funkcji getResult. Działanie wątku roboczego jest
uruchamiane za pomocą komunikatu zawierającego identyfikator ostatniego tweeta — procedura obsługi
onmessage wątku pobiera ten identyfikator i tworzy adres URL żądania REST, wykorzystując metodę
importScripts wątków. Podobnie jak przy wykorzystaniu znaczników script, importScripts po prostu
ładuje do wątku roboczego zewnętrzny skrypt albo zbiór skryptów. Pozwala to obejść kwestie dotyczące
bezpieczeństwa związane z pracą z różnymi domenami. W żądaniu URL używamy parametru callback
łańcucha zapytania, aby przekazać nazwę funkcji getResult, która ma być uruchomiona wraz z wynikami
żądania. Ponadto pewne parametry ustawiamy dynamicznie, zależnie od tego, czy chodzi o pierwsze, czy
kolejne żądanie. Dalszych wyjaśnień dotyczących opcji wyszukiwania szukaj w dokumentacji API Twittera.
Po wywołaniu funkcji zwrotnej getResult z wynikami JSON wątek roboczy sprawdza, czy są jakieś tweety,
badając, czy długość danych jest większa od zera. Jeśli tak jest, co oznacza obecność tweetów do sparsowania,
skrypt przechodzi w pętli przez każdy element w wynikach i wydobywa z niego adres URL obrazka, nazwę
użytkownika oraz sam tweet. Kiedy skrypt wydobędzie już wartości tych pól, wątek roboczy wysyła do
głównego wątku komunikat z informacjami dotyczącymi powiadomienia. Po wysłaniu komunikatu wątek
roboczy zapisuje identyfikator tweeta w zmiennej, aby w końcowej fazie przetwarzania wysłać ją do strony
głównej jako następną zakładkę, informującą, od którego tweeta należy zacząć. Można zauważyć, że skrypt
parsuje zbiór wyników z tweetami w odwrotnej kolejności. Wynika to z faktu, że pierwszy rekord reprezentuje
najnowszy tweet, a ostatni najstarszy — a skoro chcemy wyświetlać powiadomienia w porządku, w jakim
zostały napisane, muszą one być wyświetlane w odwrotnej kolejności.
W tym przepisie pokazaliśmy, w jaki sposób wyświetlać i automatycznie usuwać powiadomienia.
Zademonstrowaliśmy także, w jaki sposób łączyć je z innymi API, tak aby powiadamiać użytkowników
o zdarzeniach na stronie, nawet jeśli nie oglądają oni okna przeglądarki.
Podsumowanie
W tym rozdziale dowiedziałeś się o pewnych pomniejszych rozszerzeniach API. Przeciąganie i upuszczanie,
pamięć offline oraz powiadomienia pozwalają znacznie poprawić działanie witryn i aplikacji. Zapewniają
również wyższy poziom integracji, szybko niwelujący różnice między aplikacjami działającymi w przeglądarce
a aplikacjami natywnymi. Będziemy się z zainteresowaniem przyglądać, w jaki sposób rozszerzenia te będą
wykorzystywane w przyszłości.
330 Rozdział 13. Zachowanie przeglądarek w HTML5
14
Praca z plikami lokalnymi
P rzed pojawieniem się HTML5 działania na plikach przeprowadzane w przeglądarce były słabo obsługiwane
i nie wzbudzały dużego zainteresowania. Zakres funkcjonalności obejmował zwykle tylko prosty wybór
pliku, który miał być wysłany do serwera. Teraz, dzięki HTML5 i uzupełniającemu go File API, mamy do
dyspozycji powiększony zestaw funkcjonalności oraz, co ważniejsze, ustandaryzowany zestaw funkcji, który
pozwala odczytać różne rodzaje plików oraz ich podstawowe atrybuty w przeglądarce za pomocą JavaScriptu.
W tym rozdziale poznasz nowy zbiór funkcji dotyczących File API i wykorzystasz przepisy, w których omówimy
podstawy oraz pewne nowe specyfikacje File API pojawiające się na horyzoncie.
Interfejs FileList jest listą plików wybranych przez użytkownika, po której elementach można po kolei
iterować, tak jak w przypadku tablic. Interfejs File udostępnia właściwości wybranego pliku. Za pomocą tych
dwóch interfejsów skrypty mogą przejść w pętli przez wybrane pliki i odczytać następujące najważniejsze
właściwości tych plików:
name — nazwa pliku,
type — typ pliku w formacie MIME i kodowaniu ASCII,
size — rozmiar pliku w bajtach,
lastModifiedDate — data i czas ostatniej modyfikacji pliku.
może się poruszać po strukturze katalogów albo wybierać plików samodzielnie, bez interakcji użytkownika
wybierającego plik. Tabela 14.1 pokazuje, które wersje każdej przeglądarki obsługują File API. Zakres obsługi
w poszczególnych przeglądarkach jest różny, o czym będziemy wspominać w odpowiednich miejscach
w przepisach tego rozdziału.
Android 3.0+
Chrome 9.0+
Firefox 3.6+
Internet Explorer 10.0+
iOS Safari -
Opera 11.1+
Safari -
<!DOCTYPE html>
<html><head>
<meta charset="UTF-8" />
<title>14.1. Odczyt atrybutów pliku</title>
<script>
// obsługa wybranego pliku
function handleFile(fileInput) {
// utwórz wynik
var output = 'Atrybuty pliku:<br>';
output += 'nazwa: ' + file.name + '<br>';
output += 'typ: ' + file.type + '<br>';
output += 'rozmiar: ' + (file.size/1024).toFixed(2) + 'KB<br>';
output += 'data ostatniej modyfikacji: ' + file.lastModifiedDate;
} else {
// interfejs obsługujący pliki nie jest dostępny w przeglądarce
fileAttributes.innerHTML = 'Interfejs obsługujący pliki nie jest dostępny';
}
}
</script>
</head>
<body>
<h1>Wybierz plik:</h1>
<section>
<input type="file" id="input" onchange="handleFile(this)" />
</section>
<br>
<section id="fileAttributes"></section>
</body>
</html>
Kiedy odwiedzający stronę utworzoną na podstawie listingu 14.1 wybierze jakiś plik, używając służącego
do tego przycisku, procedura obsługi zdarzenia onchange wywoła funkcję handleFile z elementem input
typu file jako parametrem. Wewnątrz tej funkcji najpierw pobierane jest odwołanie do elementu section,
służącego do pokazania wyników, po czym następuje weryfikacja, czy przeglądarka obsługuje interfejs
FileList, poprzez sprawdzenie obecności tablicy files. Jeśli przeglądarka nie obsługuje tego interfejsu,
w elemencie section o atrybucie fileAttributes jest wyświetlany odpowiedni komunikat, ale na taką sytuację
możesz zareagować według własnego pomysłu albo poprosić odwiedzającego o więcej informacji.
Jeśli interfejs FileList jest obsługiwany, za pomocą odwołania do pierwszego pliku w tablicy files
(fileInput.files[0]) jest pobierana instancja typu File, która następnie zostaje użyta do odczytania
właściwości name, type, size oraz lastModifiedDate. Ponieważ jednostką miary atrybutu size jest bajt,
a bardziej powszechne jest podawanie rozmiaru plików w kilobajtach, trzeba przeprowadzić odpowiednią
konwersję za pomocą wyrażenia, które przekształca bajty na kilobajty. Możesz poprawić to wyrażenie,
tworząc funkcję, która pobierze liczbę bajtów i pokaże konwersję na kilobajty, megabajty albo inną jednostkę,
zależnie od całkowitej liczby bajtów, tak jak jest to robione w zwykłych listach plików w katalogach.
Uwaga
Interfejs File dziedziczy z interfejsu Blob, w związku z czym zawiera atrybuty size oraz type. Atrybuty
name i lastModifiedDate są dodane w interfejsie File. Jednak w kilku przeglądarkach lastModifiedDate
nie jest dostępny i poproszony o wartość będzie zwracał undefined.
W tym przepisie pokazaliśmy atrybuty pliku wybranego przez odwiedzającego. Przydaje się to do
filtrowania plików przed przesłaniem do serwerów albo po prostu do pokazania użytkownikowi jakiegoś
komunikatu. W następnym przepisie rozwiniemy ten temat, udostępniając odwiedzającemu interfejs
pozwalający na wybranie wielu plików do przetworzenia.
334 Rozdział 14. Praca z plikami lokalnymi
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>14.2. Przetwarzanie wielu plików za pomocą FileList</title>
<style>
#dropZone {
width:300px;
border: 2px dashed #bbb;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
padding: 25px;
text-align: center;
font: 20pt bold;
color: #bbb;
}
#fileTable {
border: 1px solid #000;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
padding: 5px;
visibility:hidden;
}
tr:nth-child(odd) { background-color:#eee; }
tr:nth-child(even) { background-color:#fff; }
Przetwarzanie wielu plików za pomocą przeciągania i upuszczania 335
</style>
<script>
if (files.length>0) {
var row;
var cell;
var textNode;
// dodaj wiersz
var row = fileTable.insertRow(i);
textNode = document.createTextNode(files[i].name);
cell.appendChild(textNode);
if (files[i].lastModifiedDate != undefined) {
// dodaj komórkę na datę ostatniej modyfikacji pliku
cell = row.insertCell(3);
textNode = document.createTextNode(files[i].lastModifiedDate);
cell.appendChild(textNode);
}
}
fileTable.style.visibility = 'visible';
} else {
fileTable.style.visibility = 'hidden';
}
}
// wyczyść tabelę
function clearTable() {
// inicjalizuj okno
window.addEventListener('load',init,false);
</script>
</head>
<body>
<h1>Przeciągnij kilka plików i upuść na poniższą strefę:</h1>
<section id="fileSelection">
<!--
// Wybór wielu plików można również osiągnąć za pomocą atrybutu multiple
<input type="file" id="input" multiple="true" onchange="displayFiles(this.files)">
-->
<div id="dropZone">Tutaj upuszczaj pliki</div>
</section>
<section id="filesSelected">
<br>
<div id="fileCount"></div>
<table id="fileTable">
</table>
</section>
</body>
</html>
Interfejs FileReader 337
Po załadowaniu strony przeglądarka inicjalizuje strefę upuszczania za pomocą wywołania funkcji init
przez procedurę nasłuchu zdarzenia load okna. Funkcja init ustawia zmienną dropzone na element div
o identyfikatorze dropZone oraz rejestruje dwie procedury nasłuchu dla zdarzeń dragover i drop. Po aktywowaniu
zdarzenia dragover przeglądarka wywołuje funkcję handleDragOver, a po aktywowaniu zdarzenia drop
— funkcję handleFileDrop. Po upuszczeniu plików na strefie upuszczania i wywołaniu handleFileDrop
skrypt pobierze listę plików i przekaże ją metodzie displayFiles.
Funkcja displayFiles wywołuje funkcję pomocniczą clearTable, by wyczyścić wszystkie poprzednie wyniki.
clearTable usuwa po kolei wszystkie wiersze z tabeli. Następnie displayFiles sprawdza długość FileList,
aby dowiedzieć się, czy na strefę upuszczania zostały upuszczone jakieś pliki. Skrypt przechodzi w pętli przez
elementy tablicy, korzystając z indeksu do odczytu każdego interfejsu File. Funkcja pobiera i do nowego
wiersza tabeli dodaje jako nową komórkę atrybuty każdego obiektu File. Wreszcie, skrypt wyświetla tabelę
poprzez spowodowanie, że staje się widoczna, jak pokazano w przykładowym wykonaniu na rysunku 14.1.
Wskazówka
Atrybut type interfejsu File, który jest dziedziczony z interfejsu Blob, zwraca typ pliku MIME (ang.
Multipurpose Internet Mail Extensions). Typ MIME jest typu DOMString, w którym wszystkie litery są
małe i który jest zapisany przy użyciu kodowania ASCII. Ciąg tekstowy typu MIME może być używany
do filtrowania rodzajów plików, na przykład obrazków (image) lub innych. Mimo że na rysunku 14.1
zaprezentowano dwa obrazki różnych rodzajów, możesz łatwo stwierdzić, że oba pliki są obrazkami,
gdyż MIME zaczyna się od słowa image. Typ MIME udostępnia prosty i skuteczny sposób filtrowania
i sprawdzania plików, co pokażemy w następnym podrozdziale.
Interfejs FileReader
Specyfikacja HTML5 File API udostępnia nowy interfejs o nazwie FileReader, służący do wczytania plików
do pamięci klienta. Interfejs ten udostępnia metody, atrybuty i zdarzenia, które pozwalają programistom
odczytywać asynchronicznie pliki z JavaScriptu po stronie klienta. Poprzez zdarzenia tego interfejsu możesz
338 Rozdział 14. Praca z plikami lokalnymi
wyświetlać albo przetwarzać dane z plików. Ponieważ interfejs FileReader posiada trzy różne metody służące
do odczytu, umożliwia wczytywanie wielu różnych rodzajów plików:
readAsArrayBuffer(Blob) — zwraca zawartość pliku jako ArrayBuffer,
readAsText(Blob [, encoding]) — zwraca zawartość pliku jako tekst DOMString,
readAsDataURL(Blob) — zwraca zawartość pliku jako DOMString w postaci danych URL.
Ponieważ interfejs FileReader jest asynchroniczny, główny wątek strony może kontynuować przetwarzanie,
a do przechwytywania kluczowych etapów odczytu pliku są używane zdarzenia. Tabela 14.2 pokazuje zdarzenia
zdefiniowane w ramach interfejsu FileReader.
Metody służące do odczytu treści pliku znajdujące się w interfejsie FileReader oraz zdarzenia z tego
interfejsu mogą posłużyć do wykonania w przeglądarce klienta działań, które byłyby zwykle wykonywane
na serwerze po przesłaniu tam pliku. W następnym przepisie użyjemy metody readAsDataURL do utworzenia
miniatur wybranych obrazków w przeglądarce użytkownika i pokazania ich atrybutów.
Uwaga
Pliki mogą być także odczytywane za pomocą interfejsu synchronicznego FileReaderSync wraz z wątkiem
roboczym, gdyż Web Workers API pozwala metodom działać w innym wątku niż wątek główny. Interfejs
FileReaderSync ma te same metody co FileReader. Aby uzyskać więcej informacji o wykorzystaniu File
API w sposób synchroniczny, zajrzyj do W3C Working Draft.
dostępnych zasobów. Korzystając z HTML5 File API, nie musisz wysyłać plików w tę i z powrotem i możesz
wykorzystać moc obliczeniową komputera klienta do przetworzenia plików.
Ten przepis wykorzysta metodę readAsDataURL wraz ze zdarzeniem onload interfejsu FileReader do
odczytania wybranych plików. Ponadto przepis wyświetli atrybuty każdego pliku po najechaniu na jego
miniaturkę kursorem. Aby rozpocząć korzystanie z tego przepisu, wykonaj następujące kroki i utwórz
listing 14.3:
1. Utwórz pusty plik HTML i dodaj HTML-owy znacznik body z listingu 14.3, zawierający element
input typu file z atrybutem multiple oraz elementy służące do wyświetlania miniatur i atrybutów.
2. Dodaj element style zawierający trzy zbiory stylów do obsługi zaznaczonych i niezaznaczonych
obrazków.
3. Dodaj w elemencie script funkcję handleFiles, pokazaną na listingu 14.3, która jest uruchamiana
po wybraniu plików.
4. Dodaj funkcję showFile pokazującą atrybuty pliku, nad którym znajduje się kursor.
5. Dodaj funkcję clearFile, która po prostu czyści obszar atrybutów, kiedy kursor nie znajduje
się już nad obrazkiem.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>14.3. FileReader — panel z obrazkami lokalnymi</title>
<style>
.highlight,.unhighlight {
max-height:100px;
max-width:100px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
margin:10px;
}
.highlight {
border: 5px solid #6f0;
}
.unhighlight {
border: 5px solid #000;
}
</style>
<script>
// ustawienia domyślne
var fileLimit = 10; // maksymalna liczba plików przetwarzanych jednocześnie
var sizeLimit = 500; // ograniczenie rozmiaru plików w KB
var imageType = /image.*/; // typ MIME obrazków — wyrażenie regularne
imgPanel.innerHTML = '';
} else {
// plik jest za duży
alert(file.name+' ma rozmiar większy niż '+sizeLimit+'KB.');
}
} else {
// plik nie jest obrazkiem
alert(file.name+' nie jest obrazkiem.');
}
}
} else {
// wybrano zbyt wiele plików
imgPanel.innerHTML = 'Wybrano zbyt wiele plików. Maksymalna dozwolona liczba plików to '+fileLimit+'.';
}
}
// wyświetl informację na temat obrazka, nad którym znajduje się kursor myszy
function showFile() {
</script>
</head>
<body>
<h1>Wybierz obrazki do podglądu.</h1>
<section>
<input type="file" id="input" multiple="true" onchange="handleFiles(this.files)">
</section>
<section id="imgPanel"></section>
<section id="fileAttributes"></section>
</body>
</html>
Po załadowaniu strony utworzonej na podstawie listingu 14.3 prosi ona odwiedzającego o wybranie pliku
lub zbioru plików. Następnie przeglądarka aktywuje zdarzenie onchange elementu input. Główna funkcja
skryptu — handleFiles — ustawia najpierw najważniejsze wartości domyślne: maksymalną dozwoloną liczbę
plików, maksymalny rozmiar każdego pliku oraz wyrażenie regularne, które będzie używane do dopasowywania
typów MIME obrazków. Rozmiar pliku i liczba plików są ograniczone, aby uniknąć przeciążania pamięci
przeglądarki, gdyż przetwarzanie będzie wykonywane w pamięci komputera klienta. W tym przepisie skrypt
przejdzie w pętli przez pliki i utworzy wiele działających w tym samym czasie instancji interfejsu FileReader.
Skrypt mógłby być bardziej inteligentny, jeśli chodzi o przetwarzanie, i posiadać zbiór obiektów odczytujących,
które nie pobierałyby kolejnych plików do momentu zwolnienia jakiegoś obiektu odczytującego, ale ewentualną
implementację tego mechanizmu zostawiamy Tobie.
Skrypt sprawdza, czy liczba wybranych plików nie przekracza maksymalnej, a jeśli przekracza, wyświetla
odpowiedni komunikat. Jeśli wybrana jest odpowiednia liczba plików, wtedy przechodzi w pętli przez listę
plików (FileList) i sprawdza, czy każdy plik jest obrazkiem, poprzez dopasowywanie typu MIME oraz
upewnia się, że rozmiar każdego z plików jest mniejszy od maksimum, które ustanowiłeś. Jeśli każdy plik
spełnia te wymagania, przepis tworzy pojemnik obrazków z właściwościami dotyczącymi zdarzeń mouseover
i mouseout w celu pokazywania atrybutów. Teraz skrypt jest gotowy do odczytu pliku poprzez utworzenie
342 Rozdział 14. Praca z plikami lokalnymi
nowego obiektu FileReader dla każdego obrazka. Skrypt ustawia na obiekcie FileReader zdarzenie onload,
służące do załadowania wyniku (result) obiektu FileReader do atrybutu src elementu obrazka, co spowoduje
utworzenie miniatury, jak pokazano na rysunku 14.2.
Rysunek 14.2. Przykładowy wynik pokazujący wybrane przez użytkownika pliki obrazków
wyświetlone za pomocą metody readAsDataURL
Na koniec obiekt FileReader odczytuje każdy plik metodą readAsDataURL, która zwróci wynik w postaci
danych URL do ustawienia jako źródło obrazka. Pozostała część funkcji obsługuje błędy związane z różnymi
testami umieszczonymi w funkcji i wyświetla użytkownikowi odpowiednie komunikaty. Funkcje showFile
i clearFile wyświetlają atrybuty plików.
Przepis ten pokazuje prosty sposób użycia interfejsu FileReader i jednej z dostępnych metod odczytu. Poza
tym mamy nadzieję, że ten przepis pokazał Ci, iż musisz uważać przy uruchamianiu wielu asynchronicznych
odczytów jednocześnie w przeglądarce klienta. Aby się przekonać, jak ważne jest to ograniczenie, powiększ
limity dotyczące rozmiaru pliku i liczby plików (ale zrób to ostrożnie, ponieważ łatwo osiągnąć maksimum
pamięci dostępnej w przeglądarce). W następnym przepisie poznasz pewne dodatkowe funkcje interfejsu
FileReader, które pozwolą Ci zatrzymać przetwarzanie i przechwycić mogące wystąpić błędy.
Poza nową metodą — readAsText — w przepisie wykorzystamy również funkcję interfejsu FileReader
o nazwie abort. Ta metoda pozwala skryptowi albo użytkownikowi zatrzymać proces odczytu. Po poprawnym
wywołaniu metody abort FileReader generuje ABORT_ERR, aby potwierdzić, że nastąpiło zatrzymanie. Interfejs
FileReader ma pięć możliwych błędów, które mogą być wygenerowane z powodu problemów z odczytem
podanego pliku albo obiektu Blob. Pokazuje je tabela 14.3.
W tym przepisie zgłębisz jedną z ostatnich procedur obsługi zdarzeń udostępnianą przez FileReader
— onprogress. Procedura obsługi zdarzenia onprogress udostępnia powiadomienia dotyczące postępów
odczytu pliku i może być używana do wyświetlania użytkownikowi statusu odczytu. Procedura obsługi
zdarzenia onprogress zawiera trzy atrybuty: lengthComputable, loaded i total. Atrybut lengthComputable
jest flagą logiczną, która mówi, czy atrybuty loaded i total są dostępne. Atrybut loaded jest liczbą bajtów
wczytanych dotychczas do pamięci, podczas gdy total jest całkowitą liczbą bajtów do odczytania. Dzieląc
progress.loaded przez progress.total, możesz uzyskać procent wykonania zadania, który może być
wyświetlony użytkownikowi. W tym przepisie w miarę wczytywania pliku do pamięci strona będzie pokazywać
pasek postępu ze statusem procesu odczytu. Zauważ jednak, że wyzwalanie zdarzenia progress zależy od
implementacji interfejsu FileReader w przeglądarce, więc nie ma pewności co do tego, kiedy albo jak często
zdarzenie to będzie generowane. Następujące kroki pozwolą Ci utworzyć kod z listingu 14.4:
1. Utwórz pustą stronę HTML ze znacznikami body i style, takimi jak pokazane na listingu 14.4,
zawierającymi przycisk wyboru pliku, przycisk anulowania, blok informacji o pliku i element
div przeznaczony na dane z pliku.
2. Dodaj znaczniki style i nadaj style różnym elementom, między innymi do ukrywania przycisku
anulowania.
3. Dodaj znaczniki script i globalne odwołanie do instancji FileReader o nazwie textReader.
Będzie to instancja FileReader służąca do odczytu wybranego pliku CSV.
4. Dodaj wiersz zawierający wywołanie addEventListener dotyczące zdarzenia load i funkcji init,
która rejestruje funkcje obsługi zdarzeń obiektu textReader.
5. Dodaj odpowiednie funkcje zdarzeń: onErrorHandler, updateProgress, onAbortHandler,
onLoadStartHandler i onLoadHandler.
6. Dodaj funkcję cancelFileReader, która umożliwia odwiedzającemu zatrzymanie procesu odczytu.
7. Dodaj funkcję handleFile, która uruchamia proces odczytu wybranego pliku za pomocą metody
readAsText interfejsu FileReader.
344 Rozdział 14. Praca z plikami lokalnymi
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>14.4. Podstawowe File API</title>
<style>
#fileInfo {
border: 1px solid #000;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
padding: 5px;
visibility:hidden;
}
#progHolder {
float:right;
width:200px;
height:30px;
border:solid;
border-width:1px;
background-color:#999;
text-align:center;
}
#progMeter {
width:0px;
height:30px;
background-color:#9FF;
}
#btnCancel {
visibility:hidden;
}
</style>
<script>
// utwórz obiekt FileReader
var textReader = new FileReader();
function init() {
// skonfiguruj procedury obsługi obiektu FileReader
textReader.onerror = onErrorHandler;
textReader.onprogress = updateProgress;
textReader.onabort = onAbortHandler;
textReader.onloadstart = onLoadStartHandler;
textReader.onload = onLoadHandler;
}
} else {
// poinformuj użytkownika, że plik nie jest typu csv
alert(file.name + ' nie jest plikiem CSV.');
}
}
// ustaw wynik
var fileoutput = document.getElementById('fileoutput');
fileoutput.innerHTML = strDiv;
default:
alert('Wystąpił błąd przy odczycie pliku.');
};
}
// inicjalizuj okno
window.addEventListener('load',init,false);
</script>
</head>
<body>
<h1>Wybierz plik CSV do przetworzenia</h1>
<section>
<input type="file" id="input" onchange="handleFile(this)">
<button id="btnCancel" onclick="cancelFileReader();">Anuluj przetwarzanie</button>
</section>
<br>
<section id="fileInfo">
<div id="progHolder">
<div id="progMeter"></div>
</div>
<div id="fileAttributes"></div>
</section>
<br>
<div id="fileoutput"></div>
</body>
</html>
Po załadowaniu strony do przeglądarki odwiedzający może wybrać plik CSV do wczytania. Po wybraniu
pliku przeglądarka wywołuje funkcję handleFile, przekazując jej plik. Funkcja ta sprawdza najpierw, czy
wybrany plik jest typu CSV, dopasowując wyrażenie regularne do typu MIME. Jeśli typ MIME pliku nie
pasuje do wyrażenia, wtedy przeglądarka wyświetla powiadomienie informujące użytkownika, że plik nie
ma formatu CSV. Jeśli plik ma format CSV, wtedy kod wywołuje metodę readAsText instancji FileReader
z plikiem przekazanym jako parametr. Rozpoczyna to proces odczytu w sposób asynchroniczny, który
powoduje wywołanie odpowiednich zdarzeń w miarę postępów odczytu. Aby potwierdzić, że odczyt jest
asynchroniczny, po wywołaniu funkcji readAsText umieściliśmy wyświetlanie atrybutów pliku. Przeglądarka
pokaże atrybuty zaraz po wywołaniu readAsText, gdyż odczyt nie blokuje głównego wątku skryptu.
W funkcji init w skrypcie zarejestrowaliśmy funkcję onLoadStartHandler, wywoływaną, kiedy wystąpi
zdarzenie onloadstart, które zostanie aktywowane, kiedy FileReader rozpocznie odczytywanie pliku. Gdy
to zdarzenie wystąpi, onLoadStartHandler wyświetli przycisk anulowania, wskaźnik postępu i wynikowy
element div.
W czasie odczytywania wskaźnik postępu jest aktualizowany poprzez metodę updateProgress, wywoływaną,
kiedy FileReader aktywuje zdarzenie onprogress. Metoda updateProgress sprawdza, czy dostępne są atrybuty
loaded i total, sprawdzając atrybut lengthComputable. Jeśli atrybut ten ma wartość true, oznacza to, że
atrybuty loaded i total są dostępne. updateProgress wykorzystuje je do obliczenia procentowego ukończenia
zadania i wyświetlenia tej wartości za pomocą wskaźnika postępu, który jest budowany poprzez ustawienie
szerokości elementu div o identyfikatorze progMeter w elemencie div o identyfikatorze progHolder.
Po zakończeniu odczytywania pliku przez obiekt FileReader przeglądarka aktywuje zdarzenie onload
i wywołuje funkcję onLoadHandler, przekazując jej wyniki w zdarzeniu. W tej metodzie wyłączyliśmy przycisk
anulowania i ustawiliśmy wartość postępu na 100 procent, tak aby odwiedzający wiedział, że plik został
348 Rozdział 14. Praca z plikami lokalnymi
wczytany do pamięci. Ponieważ pliki CSV mają dane ułożone w wierszach, dzielimy następnie wynik według
znaku przejścia do nowego wiersza, tworząc tablicę wierszy za pomocą następującego polecenia:
var fileArr = evt.target.result.split('\n');
Odwołanie result przechowuje tekstową zawartość pliku w pamięci. Po podzieleniu wyniku na tablicę,
której wiersze zawierają wartości rozdzielone przecinkami, skrypt przechodzi w pętli przez każdy element
tablicy i dzieli wartości według znaku przecinka. Przechodząc przez każdy wiersz, skrypt dodaje wartości
do wynikowego ciągu tekstowego, który zostaje wyświetlony w elemencie div przeznaczonym na wyniki
odczytania pliku, kiedy wszystkie wiersze są przetworzone, jak pokazano na rysunku 14.3.
Sposób parsowania pliku z wartościami oddzielonymi przecinkami użyty w tym przepisie jest dość prosty.
Nie obsługuje przypadków takich jak przecinki występujące w wartościach i nie rozumie różnicy między
wartościami zawartymi i niezawartymi w cudzysłowach. W internecie możesz znaleźć bardziej rozbudowane
sposoby parsowania, lecz w celu pokazania, jak działa metoda readAsText, użyliśmy w przepisie prostego
modelu.
W przepisie zawarliśmy możliwość zatrzymania odczytu za pomocą przycisku anulowania. Jeśli odwiedzający
kliknie przycisk anulowania, kiedy FileReader odczytuje plik, przeglądarka wywoła metodę cancelFileReader,
która z kolei wywoła metodę abort na instancji FileReader — textReader. Instruuje ona instancję FileReader,
aby anulować proces odczytu, usuwa z pamięci wszystkie już odczytane informacje i aktywuje zdarzenie
onabort. Skrypt przechwytuje z kolei zdarzenie onabort za pomocą metody onaborthandler i wyświetla
odwiedzającemu komunikat potwierdzający anulowanie odczytu.
Może zauważyłeś, że skrypt zawiera procedurę obsługi wszystkich błędów przekazywanych w zdarzeniu
onerror. Po przerwaniu procesu odczytu przeglądarka tworzy zdarzenie błędu o nazwie ABORT_ERR i wywołuje
metodę onErrorHandler z tym błędem. Skrypt po prostu ignoruje ten błąd, gdyż przerwanie odczytu jest
obsługiwane gdzie indziej. Jednak inne błędy, które mogą się pojawić, będą obsłużone odpowiednim
komunikatem wyświetlanym użytkownikowi.
W tym przepisie pokazaliśmy zalety korzystania z interfejsu FileReader do przetwarzania pliku CSV
w przeglądarce. Wyniki parsowania pliku CSV mogą być filtrowane, wyświetlane i przygotowywane do
przekazania na serwer bez wykonywania działań na serwerze. Przepis zawiera funkcje odczytu pliku,
przerywania procesu i obsługi błędów, które mogą wystąpić. Może posłużyć jako wzór pozwalający rozpocząć
przetwarzanie plików na Twoich stronach internetowych.
Rozszerzone specyfikacje File API 349
Uwaga
Począwszy od wersji 12, przeglądarka Chrome używa niestandardowej wersji wywołania
requestFileSystem — webkitRequestFileSystem. Parametry wywołania są takie same, lecz trzeba
używać odpowiednich wywołań zależnie od wersji Chrome. Można zakładać, że w miarę krzepnięcia
specyfikacji i wzrostu liczby przeglądarek obsługujących specyfikacje File API wywołania będą znowu
ogólne i takie same w różnych przeglądarkach.
Istnieje wiele obiektów i metod w obu omawianych API, lecz kilka jest kluczowych dla tego przepisu
i pracy z systemem plików oraz plikami. Oto kilka bardziej przydatnych interfejsów Directories and System:
FileSystem — reprezentuje system plików, z którym pracujemy,
Entry — reprezentuje ogólny wpis w systemie plików,
DirectoryEntry — reprezentuje katalog w systemie plików,
FileEntry — reprezentuje plik w systemie plików.
Interfejsy DirectoryEntry i FileEntry są oparte na interfejsie Entry, który posiada standardowe metody
copy, move i remove. Oba omawiane API mają odpowiednie zdarzenia i zbiór możliwych błędów. Dodatkowo
API udostępniają zarówno wywołania asynchroniczne do użytku w sposób osadzony, jak i synchroniczne,
z których się korzysta w wątkach roboczych. Każdy z interfejsów posiada metody i atrybuty. Zalecamy Ci
przejrzenie najnowszych wersji specyfikacji w celu uzyskania aktualnych informacji na ich temat. W tabeli
14.4 opisujemy kilka spośród metod, które będziemy wykorzystywać w tym rozdziale.
Interfejs FileWriter ma również atrybuty length oraz position, które mogą być użyte do ustawienia
pozycji wskaźnika określającego miejsce dopisywania lub zapisywania informacji w pliku.
Jest wiele metod związanych z File API: Directories and System, w tym kopiujących i zmieniających
położenie plików. Są również atrybuty pliku i katalogu, które znajdziesz w zwykłym eksploratorze plików
i katalogów. Za pomocą tych metod i atrybutów możesz stosunkowo łatwo utworzyć swój własny eksplorator
plików i katalogów. Niestety nie mamy tutaj miejsca na utworzenie takiego przepisu, ale wszystkie niezbędne
informacje potrzebne do korzystania z tych metod i atrybutów możesz znaleźć w specyfikacji File API:
Directories and System. Należy zwrócić jednak uwagę na to, że ze względów bezpieczeństwa interfejsy te
mają do czynienia z systemem plików sztucznie „wygenerowanym” przez przeglądarkę dla domeny, z której
pochodzi strona. W następnym przepisie utworzymy plik w lokalnym systemie plików i zapiszemy w nim
jakieś dane.
350 Rozdział 14. Praca z plikami lokalnymi
Tabela 14.4. Kluczowe metody File API: Directories and System oraz Writer
Uwaga
Jako że nie ma obecnie interfejsu związanego z uprawnieniami dotyczącymi bezpieczeństwa,
pozwalającego użytkownikowi zezwolić przeglądarce na dostęp do zapisywania pliku lokalnego,
musisz poinstruować przeglądarkę Chrome, że zezwolenie zostało udzielone, uruchamiając ją
z dwoma argumentami: --unlimited-quota-for-files oraz --allow-file-access-from-files.
Aby włączyć przeglądarkę Chrome z tymi argumentami, możesz uruchomić terminal w systemie
Mac OS i użyć następującego polecenia:
open /Applications/Google\ Chrome.app -n --args --unlimited-quota-for-files --allow-file-access-from-files
To polecenie uruchomi przeglądarkę Chrome, przekazując jej argumenty wiersza poleceń, które
nakazują przeglądarce umożliwienie dostępu do plików i ustawienie braku limitu rozmiaru plików.
Jeśli nie uruchomisz Chrome w ten sposób, otrzymasz błędy bezpieczeństwa lub uprawnień, które
uniemożliwią Ci dostęp do systemu plików1.
1
W czasie pracy nad tłumaczeniem książki udało mi się uruchomić przykład z przepisu w systemie Windows Vista bez potrzeby
stosowania opisywanych flag — przyp. tłum.
Tworzenie lokalnego pliku 351
Podany skrypt normalnie użyłby metody requestFileSystem, aby pobrać odwołanie do lokalnego systemu
plików. Ponieważ jednak specyfikacja ciągle się zmieniała, w przeglądarce Chrome 12 i nowszych jest
stosowana własna wersja tej metody — webkitRequestFileSystem. Ponadto skrypt korzysta z warunku
logicznego, tworząc obiekt Blob poprzez BlobBuilder albo WebKitBlobBuilder, w zależności od wersji
przeglądarki. Aby utworzyć stronę, wykonaj następujące kroki składające się na listing 14.5:
1. Utwórz pusty plik HTML i dodaj HTML-owe body z listingu 14.5, które zawiera element input
typu email, kilka elementów button oraz element div do wyświetlania listy adresów e-mail.
2. Dodaj funkcję fileErrorHandler, która będzie obsługiwać wszystkie błędy dotyczące katalogów
albo plików napotkane w kodzie asynchronicznym.
3. Dodaj w skrypcie funkcję fileAction, pokazaną na listingu 14.5, która pobiera odwołanie
do pliku i wywołuje odpowiednią funkcję w oparciu o działanie podjęte przez użytkownika.
4. Dodaj funkcje writeToFile, readFromFile oraz removeFile, które obsługują konkretne działania
z wykorzystaniem przekazanego odwołania do pliku.
5. Dodaj procedurę nasłuchu zdarzenia dotyczącą załadowania strony, która uruchomi funkcję
fileAction, pokazującą adresy e-mail, jeśli istnieje plik lokalny, w którym są zapisane.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>14.5. Tworzenie pliku — zapis asynchroniczny</title>
<script>
case FileError.INVALID_MODIFICATION_ERR:
msg = 'Zażądano błędnej modyfikacji.';
break;
case FileError.QUOTA_EXCEEDED_ERR:
msg = 'Brak miejsca z powodu przekroczenia ograniczenia rozmiaru zapisu.';
break;
case FileError.TYPE_MISMATCH_ERR:
msg = 'Nieprawidłowy typ pliku lub katalogu.';
break;
case FileError.PATH_EXISTS_ERR:
msg = 'Ścieżka już istnieje.';
break;
default:
msg = 'Nieznany błąd File API.';
break;
};
}, fileErrorHandler);
}
// odczytaj plik
reader.readAsText(file);
}, fileErrorHandler);
}
// usuń plik
fileEntry.remove(function() {
console.log('Plik został usunięty.');
}, fileErrorHandler);
}
</script>
</head>
<body>
<input type="email" id="emailAddress" /><button onClick="fileAction('write');">Dodaj adres e-mail</button>
<button onClick="fileAction('read');">Odczytaj adresy e-mail</button>
<button onClick="fileAction('remove');">Usuń plik</button>
<br/><br/>
<div id="emailList"></div>
</body>
</html>
354 Rozdział 14. Praca z plikami lokalnymi
Aby uruchomić stronę utworzoną na listingu 14.5, włącz Chrome za pomocą polecenia zawierającego
argumenty dotyczące uprawnień do lokalnego systemu plików. Jeśli przeglądarka Chrome nie zostanie
uruchomiona z tymi parametrami, strona nie będzie mogła wykonywać działań na plikach lokalnych.
Po załadowaniu, w przypadku gdy plik już istnieje, strona ma załadować adresy e-mail z pliku i je wyświetlić.
W tym celu strona wywołuje funkcję fileAction z wartością read. W funkcji fileAction wykonywany jest
szereg asynchronicznych wywołań funkcji. Najpierw pobierane jest odwołanie do systemu plików za pomocą
metody webkitRequestFileSystem, której przekazywana jest flaga dotycząca trwałości plików, informacja
dotycząca spodziewanej wielkości miejsca potrzebnego na przechowywanie danych, funkcja wywołania
zwrotnego oraz procedura obsługi błędów. Flaga trwałości informuje przeglądarkę, co zrobić z plikami,
a konkretnie: czy pozwolić przeglądarce usunąć pliki w razie potrzeby (TEMPORARY), czy je pozostawić
(PERSISTENT). W tym przykładzie utworzyliśmy jedną, ogólną procedurę obsługi błędów dla wszystkich
wywołań metod dotyczących katalogów i plików, mimo że niektóre kody błędów mogą nie mieć zastosowania.
Jednak zbiór kodów błędów jest taki sam dla całego File API. Możesz oczywiście mieć oddzielne procedury
obsługi błędów z bardziej wyspecjalizowanymi komunikatami błędów i działaniami dostosowanymi do
własnych potrzeb.
Po uzyskaniu uchwytu systemu plików skrypt wykonuje metodę getFile na głównym obiekcie systemu
plików, przekazując jej nazwę pliku, parametr dotyczący otwarcia pliku, funkcję wywołania zwrotnego
dotyczącego pomyślnego wykonania metody i procedurę obsługi błędów. W tym przypadku informujemy
metodę, żeby tworzyła plik, jeśli go nie ma, poprzez ustawienie flagi create na true. Jeśli ta flaga byłaby
ustawiona na false, metoda nie utworzyłaby nowego pliku i działałaby jedynie jako funkcja otwierająca plik.
Po uzyskaniu obiektu pliku skrypt wywołuje następnie funkcję readFromFile, która kontynuuje asynchroniczny
przebieg działań, pobierając plik i odczytując go poprzez instancję służącego do tego obiektu. Kiedy obiekt
zakończy czytanie pliku, skrypt wyświetli wyniki w odpowiednim elemencie div, jak pokazano na rysunku 14.4.
Teraz, kiedy plik już istnieje, użytkownik może dodawać adresy e-mail, wprowadzając je i klikając przycisk
Dodaj adres e-mail, który uruchomi fileAction. Funkcja fileAction uruchomi writeToFile, w której
utworzysz obiekt fileWriter służący do obsługi zapisu tekstu do pliku. Skrypt najpierw ustawia procedury
obsługi dotyczące momentu zakończenia działania przez fileWriter (onwriteend) oraz przypadku, kiedy
zapis tekstu z jakiegoś powodu się nie powiódł (onerror). Potem za pomocą BlobBuilder tworzy obiekt Blob
Podsumowanie 355
zawierający wprowadzony adres e-mail. Odpowiedni interfejs jest w tym przypadku wybierany za pomocą
wyrażenia warunkowego, gdyż jest on różny w różnych wersjach przeglądarki Chrome. Następnie skrypt
za pomocą metody seek obiektu fileWriter przesuwa wskaźnik położenia na koniec pliku w oparciu
o właściwość length. Pamiętaj, że kiedy adres jest dodawany do pliku, na końcu każdego adresu e-mail
należy dopisać HTML-owy znacznik przejścia do nowego wiersza (<br/>) dla ułatwienia wyświetlania
wielu adresów e-mail w elemencie div po wczytaniu pliku. Możesz sformatować dane z pliku w układzie,
który najlepiej pasuje do pokazywanych informacji. Po zakończeniu zapisywania do pliku nowych adresów
e-mail skrypt uruchamia odczyt, aby zaktualizować listę wyświetlaną na stronie.
Dodatkowo skrypt udostępnia możliwość usuwania pliku przez wywołanie funkcji remove na obiekcie
FileEntry. Spowoduje to usunięcie pliku z systemu plików. Jeśli opuścisz stronę i powrócisz bez usuwania
pliku, plik nie zniknie, gdyż w wywołaniu webkitRequestFileSystem poprosiłeś system plików, żeby plik był
trwały. W najbliższych miesiącach powinien nastąpić postęp w obsłudze specyfikacji File API: Directories
and System oraz File API: Writer przez przeglądarki różnych producentów.
Podsumowanie
W tym rozdziale dowiedziałeś się, jak wyświetlać atrybuty plików i odczytywać pliki w przeglądarce za pomocą
nowego File API. Dodatkowo poznałeś kilka nowszych specyfikacji dotyczących plików, które dodają funkcje
inne niż tylko odczytywanie plików. Przenosząc działania, które normalnie musiałyby być wykonywane za
pomocą przetwarzania po stronie serwera, do przeglądarki, możesz rozłożyć przetwarzanie na komputery
klienta i poprawić działanie stron z punktu widzenia odwiedzającego.
356 Rozdział 14. Praca z plikami lokalnymi
15
Integracja z urządzeniami
przenośnymi
W ciągu ostatnich 20 lat HTML przeszedł długą drogę i cały czas ewoluuje dzięki pracy twórców
przeglądarek i różnych grup, takich jak grupy robocze W3C. HTML5, jaki znamy, jest zbiorem
HTML-owych znaczników i atrybutów, javascriptowych API oraz stylów CSS. W tej książce omówiliśmy
wiele spośród rozszerzających go API, które zostały dodane w ramach HTML5. Jednak prace nad ulepszaniem
działania przeglądarek z punktu widzenia użytkownika, w szczególności w związku z coraz powszechniejszym
korzystaniem z urządzeń przenośnych, nie zostały zaniechane, a nawet są intensywniejsze niż kiedykolwiek.
W ciągu ostatnich kilku lat nastąpiła prawdziwa eksplozja w branży urządzeń przenośnych, które teraz
znacząco różnią się od telefonów komórkowych z przeszłości. Nie są to już telefony komórkowe zawierające
wiele innych funkcji, lecz urządzenia przenośne, w których telefon jest tylko jedną z funkcji.
W tym rozdziale zapoznasz się z jednym z najbardziej ekscytujących i nowych obszarów dotyczących
działania przeglądarek: integracją z różnymi funkcjami urządzeń (mowa tu o aparatach fotograficznych,
mikrofonach, żyroskopach, akcelerometrach) i aplikacjami, takimi jak bazy danych kontaktowych, aplikacje
kalendarza i galerie zdjęć. Te API mają umożliwić stronom internetowym oferowanie szerokiego zakresu
funkcji, w tym wideokonferencji, planowania spotkań w kalendarzu użytkownika, dodawania zdjęć z galerii
użytkownika, sprawdzania poziomu naładowania akumulatorów lub innych zadań, które zwykle były
zarezerwowane dla natywnych aplikacji urządzeń albo wyspecjalizowanych wtyczek. Niedługo również twórcy
stron internetowych będą mogli korzystać z tych funkcji.
Jak możesz sobie wyobrazić, zakres tematyczny pokrywany przez te API jest dość szeroki. Grupa robocza
robi duże postępy w tworzeniu API i specyfikacji zdarzeń, które mogą zostać wykorzystane przez producentów
przeglądarek. Mimo że jest jeszcze wcześnie, niektórzy producenci już zaczęli je włączać do swoich produktów.
Zbiór API i zdarzeń urządzeń grupy roboczej Device APIs zawiera następujące pozycje:
Application Registration API — umożliwia stronom internetowym rejestrowanie się w systemie
jako aplikacje i pozwala im na obsługę wywołań z identyfikatorem rejestracji.
Battery Status API* — udostępnia informacje dotyczące akumulatorów urządzenia.
Beep API — umożliwia sterowanie sygnałami dźwiękowymi systemu urządzenia.
Calendar API* — umożliwia odczyt wpisów w kalendarzu na urządzeniu.
Contacts API* — umożliwia odczyt kontaktów oraz zawartych w nich informacji pochodzących
z aplikacji przechowującej kontakty w urządzeniu.
Gallery API* — umożliwia dostęp do galerii urządzenia, która może zawierać pliki dźwiękowe,
wideo i zdjęcia1.
Generic Sensor API — umożliwia integrację i obsługę różnych czujników w urządzeniu.
HTML Media Capture* — udostępnia atrybuty i funkcje HTML-owe do przechwytywania
w urządzeniu mediów, takich jak dźwięk, wideo i obraz.
Media Capture API* — udostępnia interfejs programistyczny do przechwytywania mediów
za pomocą kamery i mikrofonu urządzenia.
Menu API — udostępnia możliwość kontrolowania menu aplikacji urządzenia.
Messaging API* — pozwala stronie klienta wysłać wiadomość opartą o schematy URI mms,
sms albo mailto.
Network Information API* — pobiera aktualny typ połączenia sieciowego urządzenia.
Tasks API — umożliwia dostęp do zadań użytkownika zarządzanych na urządzeniu.
Vibration API — udostępnia kontrolę nad wibracjami urządzenia2.
1
Prace nad tą specyfikacją nie są kontynuowane, a sama specyfikacja powinna być uważana za historyczną — przyp. tłum.
2
Na stronie www.w3.org znajduje się już opublikowana wersja robocza tej specyfikacji — przyp. tłum.
3
12 lipca 2012 roku została opublikowana wersja robocza specyfikacji Pick Contacts Intent, która docelowo ma zastąpić
Contacts API — przyp. red.
Contacts API 359
Contacts API
Contacts API umożliwia użytkownikowi komunikującemu się ze stroną w przeglądarce udostępnianie tej
stronie informacji o kontakcie (lub kontaktach) z lokalnej aplikacji użytkownika przechowującej kontakty.
Użytkownik może się zdecydować na udostępnianie stronie jedynie określonych pól. Dodatkowo użytkownik
może się zdecydować udostępnić tylko kontakty spełniające kryteria wymagane przez stronę, na przykład
kontakty, które mają w polu przechowującym miasto wartość „Warszawa”. Jest to API tylko do odczytu,
w związku z czym obecnie nie mogą być dodawane nowe wpisy. Specyfikacja Contacts API zaleca stosowanie
aktualnie używanych formatów, takich jak vCard, do dodawania albo aktualizacji kontaktów w repozytorium
kontaktów użytkownika. Ponieważ Contacts API umożliwia dostęp do informacji osobistych i poufnych,
przeglądarka musi poprosić o zgodę użytkownika na udzielenie dostępu do tego API.
Po udzieleniu zgody JavaScript na stronie może użyć interfejsu Contacts, aby rozpocząć odnajdywanie
kontaktów. Interfejs Contacts ma jedną metodę, find, która pozwala mu odnaleźć jeden kontakt lub większą
ich liczbę, jak pokazano w poniższej sygnaturze metody find:
void find (DOMString[] pola, ContactFindCB funkcjaSukcesu, opcjonalnie ContactErrorCB funkcjaBłędu,
´opcjonalnie ContactFindOptions opcje);
Aby uniknąć zablokowania strony podczas wyszukiwania kontaktów, metoda find wykorzystuje
funkcje zwrotne, które są uruchamiane w przypadku jej pomyślnego lub niepomyślnego wykonania. Jeśli
funkcja odnalazła pasujące kontakty oraz pola, które powinny zostać zwrócone, przeglądarka wywołuje
funkcję wywołania zwrotnego dotyczącą pomyślnego wykonania z przekazaną jej w zmiennej tablicą
wyników. Jeśli metoda find zakończyła działanie bez błędu, ale nie zwróciła żadnych wyników, do funkcji
wywołania zwrotnego dotyczącego pomyślnego wykonania zostanie przekazana wartość null, oznaczająca
nieodnalezienie dopasowanych rekordów kontaktów.
Zwracany rekord kontaktu jest instancją interfejsu Contacts, który zawiera szereg atrybutów. Typami
niektórych z tych atrybutów są inne interfejsy dotyczące kontaktów, takie jak ContactName, ContactAddress
i tak dalej, gdyż kontakt może w rzeczywistości mieć wiele elementów składowych, na przykład kilka
adresów. W tabeli 15.1 pokazujemy atrybuty interfejsu Contacts. Nie będziemy w tym miejscu opisywać
wszystkich tych interfejsów, ale łatwo można je odnaleźć w aktualnej wersji roboczej specyfikacji na stronie
http://www.w3.org/TR/contacts-api/.
Uwaga
W chwili pisania tej książki nie było przeglądarki obsługującej Contacts API, ale prace nad specyfikacją
trwały. Poniższy przepis udostępniamy jako przykład — może on zawierać błędy albo różnice w stosunku
do rzeczywistych implementacji w ich ostatecznej postaci.
360 Rozdział 15. Integracja z urządzeniami przenośnymi
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>15.1. Pobierz wszystkie kontakty</title>
<script>
// funkcja inicjalizująca stronę
function init() {
// ustaw procedurę obsługi przycisku
var btnFindAll = document.getElementById('findAll');
btnFindAll.addEventListener('click',findAllContacts,false);
}
} else {
</script>
</head>
<body>
<h1>Pobierz wszystkie kontakty z numerami telefonów komórkowych</h1>
<button id="findAll">Znajdź wszystkie kontakty</button>
<div id="divResults"></div>
</body>
</html>
W powyższym przykładzie, kiedy użytkownik kliknie przycisk Znajdź wszystkie kontakty, przeglądarka
wywoła funkcję findAllContacts, która wykonuje zapytanie za pomocą interfejsu Contacts. W funkcji
findAllContacts kod najpierw sprawdza, czy interfejs Contacts jest dostępny. Jeśli nie jest, przeglądarka
pokazuje powiadomienie, ale możesz zastąpić je alternatywnym zachowaniem, które lepiej sprawdzi się
w Twoim przypadku. Jeśli interfejs Contacts jest dostępny, kod tworzy tablicę pól, które mają być zwrócone
przez metodę find tego interfejsu. Następnie kod wywołuje metodę find i przekazuje jej tablicę pól oraz
funkcje wywołania zwrotnego dotyczące pomyślnego i niepomyślnego wywołania metody.
Pole phoneNumbers, pobrane za pośrednictwem interfejsu Contacts, ma format interfejsu ContactField.
Ten interfejs składa się z atrybutów type, value oraz pref, jak pokazano w tabeli 15.2.
Atrybut type ma typ DOMString i w przypadku pola phoneNumbers atrybut ten może mieć wartość home
albo mobile. Atrybut value to sam numer, a atrybut pref jest wartością logiczną, która mówi, czy mamy
do czynienia z wartością preferowaną, czy główną dla tego kontaktu. Przy wywołaniu funkcji zwrotnej
dotyczącej pomyślnego wykonania, czyli contactsFindSuccess, przeglądarka przekazuje jej zbiór wyników.
Jest on tablicą instancji interfejsu dotyczącego kontaktów, przez które można przejść w pętli. W tym przypadku
nazwaliśmy zmienną z wynikami contacts.
W funkcji wywołania zwrotnego dotyczącej pomyślnego wykonania przepis najpierw tworzy odwołanie
do elementu div, który jest przeznaczony na wyniki i znajduje się na stronie, tak abyś mógł wyświetlać
wszystkie pasujące wyniki. Następnie funkcja przechodzi w pętli przez tablicę kontaktów — dla każdego
kontaktu przepis przechodzi przez jego numery telefonów (phoneNumbers), a dla każdego numeru telefonu
kod sprawdza type, żeby dowiedzieć się, czy to numer telefonu komórkowego. Jeśli tak jest, kod wyświetla
nazwę oraz numer telefonu w elemencie div.
Użyta w tym przepisie metoda find interfejsu Contacts wykonuje zapytanie o wszystkie kontakty,
przekazując funkcje wywołania zwrotnego dotyczące pomyślnego i niepomyślnego wykonania. Metoda
Messaging API 363
ta może przyjąć czwarty, opcjonalny parametr typu ContactFindOptions. Według specyfikacji API Contacts
sposób implementacji tego interfejsu zależy od konkretnej przeglądarki, jednak celem tego interfejsu jest
stworzenie filtra nakładanego na podane pola kontaktów. Jest to prosta metoda filtrująca i dopasuje do
zadanego ciągu dowolne z podanych pól. Na przykład przy podaniu wartości filtra „son” i pobieraniu pól
displayName oraz emails dowolny kontakt, który posiada ciąg „son” w którymś z tych zbiorów pól, na przykład
„sonny@....com” albo „Chuck Hudson”, zostanie dopasowany. W tym przypadku przekazany metodzie
czwarty parametr wyglądałby następująco:
{filter: 'son'}
Uwaga
Contacts API jest zaprojektowane wyłącznie jako usługa tylko do odczytu danych o kontaktach
zapisanych w urządzeniu. Aby wykonać dodawanie i aktualizację kontaktów w pamięci danych
urządzenia, powinieneś umieścić dane kontaktu w standardowym formacie aplikacji z kontaktami,
na przykład tekstowym vCard, a następnie odwołać się do tekstu tej karty poprzez element href,
tak aby użytkownik mógł pobrać kartę do swojej aplikacji.
Messaging API
Messaging API zaprojektowano, by umożliwić stronie wykorzystanie schematów URI sms, mms oraz mailto
do wysyłania komunikatów na określony adres. API jest stosunkowo proste, ponieważ ma jeden interfejs
i jedną metodę: device i sendMessage. Interfejs device ma być częścią interfejsu nawigatora, więc aby sprawdzić
obsługiwanie Messaging API, możesz użyć następującego kodu:
if (navigator.device.sendMessage) {
… tutaj umieść Twój kod sendMessage …
}
Jeśli przeglądarka obsługuje Messaging API, możesz użyć metody sendMessage, aby wysłać wiadomość
do określonego odbiorcy. Poniżej pokazano składnię tej metody:
void sendMessage (DOMString to [,Blob attachments] [, messagingErrorCB]);
Parametr to przyjmuje URI (mające schemat sms, mms albo mailto) i akceptuje łańcuchy zapytania
zawierające elementy body i podobne. Pole attachments może być wykorzystane na zdjęcia albo wideo,
a ostatni parametr jest opcjonalną procedurą obsługi błędów. Aby wysłać prostą wiadomość poprzez SMS,
możesz wywołać sendMessage w następujący sposób:
navigator.device.sendMessage('sms:+17705551212?body=Wkrótce%20przyjadę');
Jeśli podana jest funkcja wywołania zwrotnego obsługi błędów — co jest zalecane, ponieważ inaczej nie
będziesz wiedzieć, że wywołanie sendMessage się nie powiodło — to przeglądarka przekaże jej obiekt błędu
z jego kodem. Tabela 15.3 zawiera listę możliwych kodów błędów.
364 Rozdział 15. Integracja z urządzeniami przenośnymi
Mimo że Messaging API jest krótką specyfikacją, jej konsekwencje są niezwykłe. Strony będą mogły wysyłać
wiadomości tekstowe, wideo i zdjęcia poprzez wiadomości MMS i e-mail prosto z przeglądarki urządzenia.
Wskazówka
Oczekuje się, że metoda sendMessage zaimplementowana w przeglądarkach będzie obsługiwać
funkcjonalności URI. Wtedy możliwe będzie wysłanie wiadomość SMS do wielu odbiorców, oddzielając
po prostu numery przecinkami, na przykład „sms: +48501123456,+48601123456…”.
Znając rodzaj połączenia, możesz w sposób dynamiczny zmieniać arkusze stylów pomiędzy wersjami
pełną i lekką albo modyfikować działanie strony, pobierając jedynie częściowe wyniki w przypadku
wolniejszego połączenia.
Oprócz interfejsu Connection mamy jeszcze dwa nowe zdarzenia: online i offline. Są one powiązane
z obiektem window i mogą być używane do sprawdzania zmian rodzaju połączenia (w przypadku zdarzenia
online) albo obsługi sytuacji rozłączania połączenia z siecią.
Wskazówka
Zdarzenie online może być wysyłane wiele razy, w przypadku kiedy użytkownik zmienia rodzaje
połączenia. Z tego powodu w Twoim kodzie funkcjonalność wykonywana w przypadku tego zdarzenia
powinna być zminimalizowana.
Przechwytywanie obrazu za pomocą elementu input typu file 365
Można dodać dodatkowy atrybut, sugerujący, w jaki sposób przeglądarka powinna przechwycić dane
wejściowe. Atrybut capture może przyjąć jedną z czterech wartości: camera, camcorder, microphone albo
filesystem. Wartością domyślną, jeśli nie podano atrybutu capture, jest filesystem. Przykładowy element
input typu file służący do przechwytywania dźwięku z mikrofonu wyglądałby następująco:
<input type="file" accept="audio/*" capture="microphone">
Za pomocą atrybutów accept i capture będziesz mógł w ciekawy sposób wykorzystywać mikrofon
i kamerę urządzenia. Istnieją już grupy pracujące nad implementacją wywołań dwukierunkowej transmisji
strumieniowej audio/wideo pomiędzy urządzeniami wykorzystującymi HTML Media Capture i inne
technologie HTML5, na przykład WebSocket.
Uwaga
W momencie pisania tego tekstu ten przepis działał na urządzeniach z systemem Android 3.0 bądź
nowszym, które obsługują HTML Media Capture, dlatego zrzuty ekranu do tego przepisu pochodzą
z tabletu z systemem Android.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>15.2. Przechwytywanie mediów</title>
<script>
// funkcja obsługująca HTML-owe przechwytywanie mediów z pliku
function handleCapture(files) {
// spodziewamy się tylko jednego zdjęcia w danym momencie, ale przejdź w pętli przez wszystkie dostarczone pliki
for (var i = 0; i < files.length; i++) {
Po załadowaniu w przeglądarce strona pokazuje zwykły przycisk Wybierz plik, jak pokazano na rysunku 15.1.
Kiedy użytkownik kliknie przycisk, przeglądarka po odczytaniu wartości atrybutów accept i capture wie,
że ma wykorzystać aparat fotograficzny, co w tym przypadku powoduje uruchomienie ekranu robienia zdjęć
systemu Android, jak pokazano na rysunku 15.2.
Po zrobieniu zdjęcia i przekazaniu na stronę jego pliku przeglądarka wywołuje funkcję handleCapture,
jako argument przekazując jej plik. W funkcji handleCapture najpierw sprawdzany jest typ pliku, a po
potwierdzeniu, że plik jest obrazkiem, tworzony jest nowy element image, który następnie jest dodawany
do strony, a do niego jest ładowany plik. Na rysunku 15.3 przycisk Wybierz plik został kliknięty kilka razy,
a uzyskane zdjęcia w postaci miniatur zostały dodane do elementu div, dzięki czemu można je oglądać.
368 Rozdział 15. Integracja z urządzeniami przenośnymi
Wszystkie trzy zdarzenia dotyczące położenia urządzenia są generowane na obiekcie window, a ich rejestracja
nie odbiega od rejestracji innych zwykłych zdarzeń na tym obiekcie. Zdarzenie deviceorientation poprzez
swoje dane przekazane do procedury obsługi udostępnia cztery możliwe do odczytu atrybuty: alpha, beta,
gamma i absolute. Właściwości alpha, beta i gamma odpowiadają ułożeniu urządzenia w trzech wymiarach.
Właściwość alpha reprezentuje obrót w płaszczyźnie poziomej, a beta i gamma odpowiadają nachyleniu
w lewo/prawo oraz w przód/tył. Na przykład laptop na biurku miałby beta równe 0 i gamma równe 0, podczas
gdy alpha miałaby wartość oznaczającą kierunek, w jakim jest ustawiony. W przypadku laptopa zorientowanie
w przestrzeni jest wyznaczane dla klawiatury, a nie ekranu, podczas gdy orientacja telefonu komórkowego
dotyczy samego telefonu. Właściwość absolute informuje, czy przeglądarka może podać bezwzględne wartości
Tworzenie poziomicy 369
kątów kierunków. Jeśli nie może, jej wartość będzie równa false. W większości naszych testów wartość ta
była rzeczywiście równa false, chociaż kąty beta i gamma miały wartości bezwzględne, a alpha miała arbitralną
wartość 0 podczas uruchamiania strony.
Zdarzenie compassneedscalibration jest proste i powiadamia okno, że wewnętrzny kompas urządzenia
wymaga kalibracji. Według specyfikacji zdarzenie to może zostać anulowane przez stronę internetową
lub aplikację, a domyślny interfejs użytkownika służący do kalibracji urządzenia może zostać zastąpiony
niestandardową kalibracją udostępnioną przez aplikację internetową. Jednak szczegóły dotyczące tego
zdarzenia nie są jeszcze ustalone.
Zdarzenie devicemotion jest bardziej skomplikowane, ponieważ zawiera atrybuty dotyczące przyspieszenia,
rotacji i interwału. Atrybut acceleration jest instancją nowego interfejsu, DeviceAcceleration, który
udostępnia atrybuty przyspieszenia x, y, z w formie typu danych double, pokazujące przyspieszenie
w kierunkach X, Y i Z. Szybkość rotacji jest również instancją interfejsu, ale interfejsu DeviceRotationRate,
który udostępnia dane o obrocie w postaci argumentów alpha, beta, gamma. Wszystkie atrybuty zdarzenia
devicemotion są w specyfikacji określone jako opcjonalne. Tabela 15.4 pokazuje atrybuty zdarzenia
devicemotion bardziej szczegółowo.
Uwaga
Ten przepis wymaga od Twojej przeglądarki obsługi elementu canvas oraz tego, żeby urządzenie,
na którym uruchamiasz stronę, posiadało wewnętrzny żyroskop udostępniający wartości beta i gamma.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>15.3. Poziomica</title>
<style>
.canvas {
height:100px;
width:100px;
}
</style>
<script>
var canvasX;
var canvasY;
var canvasXCenter;
var canvasYCenter;
// narysuj pęcherzyk
ctxBubble.fillStyle='#0FF';
ctxBubble.beginPath();
ctxBubble.arc(canvasXCenter,canvasYCenter,10,0,Math.PI*2,true);
ctxBubble.closePath();
ctxBubble.fill();
}
}, true);
}
</script>
</head>
<body>
<h1>Rozdział 15. Poziomica</h1>
<!-- Elementy canvas są ułożone w warstwy według poniższej definicji -->
<canvas id="background" style="position:absolute; left:100px; top:150px;">
Tło poziomicy.
</canvas>
<canvas id="bubble" style="position:absolute; left:100px; top:150px;">
Pęcherzyk poruszający się i pokazujący poziom.
</canvas>
<canvas id="circle" style="position:absolute; left:100px; top:150px;">
Nałożone okręgi pokazujące środek.
</canvas>
<br>
Wartość beta: <span id="betaValue"></span><br>
Wartość gamma: <span id="gammaValue"></span>
</body>
</html>
Po załadowaniu strony do przeglądarki i wywołaniu funkcji init skrypt najpierw utworzy różne składniki
poziomicy. Poziomnica składa się z trzech elementów canvas: tła, pęcherzyka oraz znajdującego się na
pierwszym planie środkowego pierścienia. Cel podzielenia poziomicy na te trzy składniki jest dwojaki. Po
pierwsze, umożliwia ułożenie warstwowe z pęcherzykiem znajdującym się ponad tłem, ale pod środkowym
Podsumowanie 373
pierścieniem, aby wyglądało to jak w prawdziwej poziomicy. Po drugie, pozwala Ci to programowo przesuwać
pęcherzyk, bez konieczności odrysowywania pozostałych elementów przy każdym ruchu.
Po narysowaniu składników poziomicy skrypt ustawia procedurę obsługi zdarzenia deviceorientation.
Funkcja obsługi zdarzenia najpierw sprawdza, czy doszło do jakiejkolwiek zmiany wartości beta lub gamma,
a następnie przypisuje zmiennym tymczasowym nowe wartości, jeśli taka zmiana zaistniała. Następnie
funkcja ustawia pewne zmienne korekty pozycji, które są używane w obliczeniach dotyczących położenia
płótna pęcherzyka. Zmienna adjFactor służy do „przyspieszenia” ruchu, podczas gdy zmienna adjMax
określa maksymalną odległość, o jaką pęcherzyk można przesunąć. Po obliczeniu nowych współrzędnych
górnej i lewej krawędzi płótna pęcherzyka płótno jest przesuwane i wyświetlane są zaktualizowane kąty beta
i gamma. Zauważ, że w przypadku kątów beta i gamma używamy jedynie całkowitych części wartości typu
double ze zdarzenia. Kąty przekazywane w naszych testach są niezmiernie dokładne, więc chcąc zapobiec
przesuwaniu płótna przy najdrobniejszych zmianach kąta, zdecydowaliśmy się przesuwać go tylko wtedy,
gdy nastąpiła zmiana o cały stopień.
Podsumowanie
W tym rozdziale poznałeś kilka nowych i jeszcze tworzonych specyfikacji API oraz zdarzeń, których celem
jest zapewnienie dostępu do wszelkiego rodzaju informacji i funkcji urządzeń. Ukończenie, publikacja
i udostępnienie ich do użytku w różnych przeglądarkach jest tylko kwestią czasu. Aplikacje internetowe będą
w końcu miały dostęp do funkcji urządzeń, które teraz są wykorzystywane tylko w natywnych aplikacjach.
Wiele specyfikacji dotyczących API urządzeń pozostaje jeszcze nieukończonych, ale postępy prac wykazują,
że możliwości HTML5 ciągle się zwiększają.
374 Rozdział 15. Integracja z urządzeniami przenośnymi
Przepisy
showFile, 342
E F showGrid, 136, 138
efekt hover, 100 FIFO, first in, first out, 218 showImage, 207
efekty, 140 File API, 331, 349, 355 startWatch, 244
gradientu, 96 Directories and System, 349 startWorkerThread, 287
przejścia, 101 Writer, 349, 355 swapCache, 316
element film, 163 towerHandleDragOver, 308
abbr, 64 filtrowanie plików, 334 wywołania zwrotnego, 229
address, 56 flaga trwałości, 354
article, 34, 36 formatowanie
aside, 37 kwadratu, 141
G
audio, 185 napisów, 174 generator czcionek, 94
b, 63 formaty plików, 93 geokodowanie, 224
canvas, 76, 133, 153 formularz, 108 Geolocation API, 75, 223, 245
cite, 58 kontaktowy, 109, 313 gniazdo sieciowe, 279
datalist, 120 rejestracyjny, 129–132 Google Font Directory, 94
del, 58 z obliczeniami, 313 gra Wieże Hanoi, 303, 307
details, 54 funkcja gradient liniowy, 96
div, 37 animateSprite, 152 gradienty, 95, 141
dl, 61 blockHandleDragStart, 307 gradienty z zatrzymaniami, 97
doctype, 26 calculateDistance, 234 grupowanie
em, 64 changeField, 260 nagłówków, 31
figcaption, 53 checkStorage, 260 treści, 35
figure, 51 clearFile, 342
footer, 39 clearRect, 146
form, 123, 128 displayFiles, 337 H
header, 29 dragStartHandler, 301
historia
hgroup, 31 drawImage, 149
przeglądarki, 203
hr, 64 fileAction, 351, 354
sesji, 203
i, 64 geoSuccess, 231
strony, 213
image, 365 getGroceryItems, 276
History API, 203
input, 77 getResult, 329
HTML, Hypertext Markup Language,
input typu file, 365 grabTweets, 328
13
link, 27 growBars, 162
HTML Media Capture, 365
mark, 57 handleCapture, 367
HTML5, 15
meter, 122 handleDragOver, 337
HTML5 Boilerplate, 75
nav, 31 handleFileDrop, 337
HTML5 Outliner, 41
ol, 59 initGraph, 161
HTML5Shiv, 74
output, 118 initSettings, 161
progress, 121 loadPages, 214
s, 58 loadStations, 200 I
script, 27 moveGear, 156
section, 34–37, 62 nextState, 211 identyfikator stateInfo, 210
small, 63 onLoadStartHandler, 347 implementacja historii, 205
source, 166 onOpen, 283 informacje o
strong, 64 onStorageEvent, 266 kontakcie, 359
time, 53 remove, 355 lokalizacji, 228
track, 172 reverseGeoCode, 234 położeniu geograficznym, 223
video, 163, 169, 171 sendNumber, 295 przeglądarce, 84
elementy setLocation, 234 interfejs
listy, 108 setPermission, 320 BlobBuilder, 349
usunięte, 64 setStation, 200 DataTransfer, 298, 301
Skorowidz 381
elementu
N canvas, 370
P
nadawanie stylu, 141, 253 datalist, 120 pamięć
nagłówek, 30 output, 119 lokalna, 258, 261
CACHE, 311 progress, 122 podręczna, 309, 312
FALLBACK, 311 video, 164 sesji, 250, 252
NETWORK, 312 File API, 332 para klucz-wartość, 248, 266
napisy wideo, 172, 173 gradientów, 95 parametry
narzędzia History API, 204 funkcji find, 359
Google Chrome, 252 Internet Explorera, 73 metody
płótna, 139 iPhone’a, 91 executeSql, 269
narzędzie pamięci podręcznej, 309 getCurrentPosition, 225
@font-face Generator, 93 pola wprowadzania liczby, 116 openDatabase, 268
HTML5 Outliner, 41 próbnika kolorów, 118 transaction, 269
nawigacja, 31 przejść, 98 watchPosition, 239
nazwy klas, 28 transformacji, 98 parsowanie
Network Information API, 364 typu wejściowego pliku, 348
notacja z kropką, 276 datetime, 113 pliku CSV, 342
notatki, 262 email, 110 tweetów, 326
Notification API, 316, 317 search, 112 pasek
tel, 111 boczny, 37
url, 111 postępu, 122
O wątków roboczych, 285 przewijania, 183
WebSocket API, 280 plik
obiekt
zapytań o media, 86 CSV, 342, 347
ApplicationCache, 315
zdarzenia onmessage, 287 httpd.conf, 168
Blob, 351
zdarzenia popstate, 210 lokalny, 351
coords, 227
zdarzeń, 266 manifestu, 309, 310
danych stanu, 215 współdzielonego wątku
odczyt atrybutów pliku, 332
dataTransfer, 302 roboczego, 294
odnajdywanie lokalizacji, 225
FileEntry, 355 pliki
odświeżanie pamięci podręcznej, 310
FileReader, 342 .appcache, 310
odtwarzacz
fileWriter, 355 .eot, 93
Flash Player, 186
localStorage, 249, 260 .htm, 27
JW Playera, 186
lokalizacji, 227 .js, 294
radia internetowego, 194
sessionStorage, 249 .mov, 165
odtwarzacze
storageEvent, 261 .mp3, 185
dźwięku, 185
sworker, 295 .mp4, 166
wideo, 183
timestamp, 227 .ogv, 166
odwrócone geokodowanie, 224
WebSocket, 279 .otf, 93
offline, 313
WindowDatabase, 268 .svg, 93
określanie odległości, 233
obrazek, 148 .ttf, 93
opakowywanie łączy, 65
obsługa .vtt, 172
opcje
@font-face, 92 .webm, 166
CSS-a, 81
animacji, 103, 104 .woff, 93
lokalizacji, 233
atrybutu dźwiękowe, 189
osadzanie
autofocus, 123 płótno, 135, 153
dźwięku, 186
formnovalidate, 127 pobieranie
wideo, 167
multiple, 124 kontaktów, 360
oznaczanie
pattern, 126 numerów telefonów, 361
daty i czasu, 53
required, 125 stanu z historii, 209
rysunków, 51
step, 127 tweetów, 328
wiadomości, 34
błędów, 231, 269, 276, 354 z historii, 213
Skorowidz 383