Aktualizacja jQuery Mobile 1.4:
Mój oryginalny artykuł był przeznaczony do starego sposobu obsługi strony, w zasadzie wszystkiego przed jQuery Mobile 1.4. Stary sposób obsługi jest już przestarzały i pozostanie aktywny do (włącznie) jQuery Mobile 1.5, więc możesz nadal korzystać ze wszystkiego, co wspomniano poniżej, przynajmniej do przyszłego roku i jQuery Mobile 1.6.
Stare zdarzenia, w tym pageinit , już nie istnieją, są one zastąpione widżetem pojemnika do przechowywania . Pageinit został całkowicie usunięty i zamiast tego można użyć funkcji tworzenia kopii , zdarzenie pozostało takie samo i nie będzie zmieniane.
Jeśli jesteś zainteresowany nowym sposobem obsługi zdarzeń na stronie, zajrzyj tutaj , w każdym innym przypadku zachęcamy do kontynuowania tego artykułu. Powinieneś przeczytać tę odpowiedź, nawet jeśli używasz jQuery Mobile 1.4 +, wykracza ona poza zdarzenia na stronie, więc prawdopodobnie znajdziesz wiele przydatnych informacji.
Starsze treści:
Ten artykuł można również znaleźć w ramach mojego bloga TUTAJ .
$(document).on('pageinit')
vs $(document).ready()
Pierwszą rzeczą, której uczysz się w jQuery, jest wywoływanie kodu wewnątrz $(document).ready()
funkcji, aby wszystko wykonało się po załadowaniu DOM. Jednak w jQuery Mobile Ajax służy do ładowania zawartości każdej strony do DOM podczas nawigacji. Z tego powodu $(document).ready()
uruchomi się przed załadowaniem pierwszej strony, a każdy kod przeznaczony do manipulacji stroną zostanie wykonany po odświeżeniu strony. Może to być bardzo subtelny błąd. W niektórych systemach może się wydawać, że działa dobrze, ale w innych może powodować nieregularne, trudne do powtórzenia dziwności.
Klasyczna składnia jQuery:
$(document).ready(function() {
});
Aby rozwiązać ten problem (i zaufaj mi, to jest problem) programiści jQuery Mobile stworzyli zdarzenia strony. W skrócie zdarzenia strony to zdarzenia uruchamiane w danym punkcie wykonania strony. Jednym z takich zdarzeń na stronie jest zdarzenie pageinit , którego możemy użyć w następujący sposób:
$(document).on('pageinit', function() {
});
Możemy pójść jeszcze dalej i użyć identyfikatora strony zamiast selektora dokumentów. Załóżmy, że mamy stronę jQuery Mobile z indeksem identyfikatora :
<div data-role="page" id="index">
<div data-theme="a" data-role="header">
<h3>
First Page
</h3>
<a href="#second" class="ui-btn-right">Next</a>
</div>
<div data-role="content">
<a href="#" data-role="button" id="test-button">Test button</a>
</div>
<div data-theme="a" data-role="footer" data-position="fixed">
</div>
</div>
Aby wykonać kod, który będzie dostępny tylko dla strony indeksu, możemy użyć tej składni:
$('#index').on('pageinit', function() {
});
Zdarzenie Pageinit będzie wykonywane za każdym razem, gdy strona będzie ładowana i pokazywana po raz pierwszy. Nie uruchomi się ponownie, chyba że strona zostanie ręcznie odświeżona lub ładowanie strony Ajax zostanie wyłączone. Jeśli chcesz, aby kod był wykonywany za każdym razem, gdy odwiedzasz stronę, lepiej użyć zdarzenia pagebeforeshow .
Oto działający przykład: http://jsfiddle.net/Gajotres/Q3Usv/, aby zademonstrować ten problem.
Jeszcze kilka notatek na to pytanie. Bez względu na to, czy używasz 1 html wielu stron, czy paradygmatu wielu plików HTML, zaleca się rozdzielenie wszystkich niestandardowych metod obsługi stron JavaScript na jeden osobny plik JavaScript. Zauważy to, że twój kod będzie lepszy, ale będziesz miał znacznie lepszy przegląd kodu, szczególnie podczas tworzenia aplikacji jQuery Mobile .
Jest też inne specjalne wydarzenie jQuery Mobile, które nazywa się mobileinit . Po uruchomieniu jQuery Mobile wyzwala zdarzenie mobileinit na obiekcie dokumentu. Aby zastąpić ustawienia domyślne, powiąż je z mobileinit . Jednym z dobrych przykładów użycia mobileinit jest wyłączenie ładowania strony Ajax lub zmiana domyślnego zachowania modułu ładującego Ajax.
$(document).on("mobileinit", function(){
//apply overrides here
});
Kolejność przejścia zdarzeń strony
Po pierwsze wszystkie wydarzenia można znaleźć tutaj: http://api.jquerymobile.com/category/events/
Powiedzmy, że mamy stronę A i stronę B, jest to kolejność rozładowywania / ładowania:
strona B - strona zdarzenia przed utworzeniem
Strona B - zdarzenie pagecreate
Strona B - zdarzenie pageinit
strona A - strona zdarzenia przed ukryciem
strona A - usunięcie strony zdarzenia
strona A - zdarzenie pagehide
strona B - strona wydarzenia przed odświeżeniem
strona B - pokaz stron wydarzenia
Aby lepiej zrozumieć zdarzenia na stronie, przeczytaj to:
pagebeforeload
, pageload
I pageloadfailed
są wypalane, gdy strona zewnętrzna jest ładowany
pagebeforechange
, pagechange
I pagechangefailed
są strona wydarzenia zmieniają. Te zdarzenia są uruchamiane, gdy użytkownik porusza się między stronami w aplikacjach.
pagebeforeshow
, pagebeforehide
, pageshow
I pagehide
są strona wydarzenia przejściowe. Te zdarzenia są uruchamiane przed przejściem, w trakcie i po przejściu i są nazywane.
pagebeforecreate
, pagecreate
I pageinit
są na stronie inicjalizacji.
pageremove
można uruchomić, a następnie obsłużyć, gdy strona zostanie usunięta z DOM
Przykład ładowania strony jsFiddle: http://jsfiddle.net/Gajotres/QGnft/
Jeśli AJAX nie jest włączony, niektóre zdarzenia mogą się nie uruchamiać.
Zapobiegaj przenoszeniu stron
Jeśli z jakiegoś powodu konieczne jest uniemożliwienie przejścia strony pod pewnym warunkiem, można to zrobić za pomocą tego kodu:
$(document).on('pagebeforechange', function(e, data){
var to = data.toPage,
from = data.options.fromPage;
if (typeof to === 'string') {
var u = $.mobile.path.parseUrl(to);
to = u.hash || '#' + u.pathname.substring(1);
if (from) from = '#' + from.attr('id');
if (from === '#index' && to === '#second') {
alert('Can not transition from #index to #second!');
e.preventDefault();
e.stopPropagation();
// remove active status on a button, if transition was triggered with a button
$.mobile.activePage.find('.ui-btn-active').removeClass('ui-btn-active ui-focus ui-btn');;
}
}
});
Ten przykład zadziała w każdym przypadku, ponieważ uruchomi się na początku każdego przejścia strony, a co najważniejsze, zapobiegnie zmianie strony przed przejściem strony.
Oto działający przykład:
Zapobiegaj wiązaniu / wyzwalaniu wielu zdarzeń
jQuery Mobile
działa w inny sposób niż klasyczne aplikacje internetowe. W zależności od tego, w jaki sposób udało Ci się powiązać wydarzenia za każdym razem, gdy odwiedzasz stronę, będzie ona wiązać wydarzenia w kółko. To nie jest błąd, to po prostu sposób jQuery Mobile
obsługi jego stron. Na przykład spójrz na ten fragment kodu:
$(document).on('pagebeforeshow','#index' ,function(e,data){
$(document).on('click', '#test-button',function(e) {
alert('Button click');
});
});
Przykład działającego jsFiddle: http://jsfiddle.net/Gajotres/CCfL4/
Za każdym razem, gdy odwiedzasz stronę, # kliknięcie indeksu będzie przypisane do przycisku # przycisk testu . Przetestuj go, przechodząc kilkakrotnie ze strony 1 na stronę 2 i wstecz. Istnieje kilka sposobów zapobiegania temu problemowi:
Rozwiązanie 1
Najlepszym rozwiązaniem byłoby pageinit
powiązanie zdarzeń. Jeśli spojrzysz na oficjalną dokumentację, przekonasz się, że pageinit
uruchomi się TYLKO raz, tak jak dokument gotowy, więc nie ma możliwości, aby zdarzenia zostały ponownie powiązane. Jest to najlepsze rozwiązanie, ponieważ nie ma narzutów związanych z przetwarzaniem, takich jak usuwanie zdarzeń metodą wyłączoną.
Przykład działającego jsFiddle: http://jsfiddle.net/Gajotres/AAFH8/
To robocze rozwiązanie zostało wykonane na podstawie poprzedniego problematycznego przykładu.
Rozwiązanie 2
Usuń zdarzenie przed powiązaniem:
$(document).on('pagebeforeshow', '#index', function(){
$(document).off('click', '#test-button').on('click', '#test-button',function(e) {
alert('Button click');
});
});
Przykład działającego jsFiddle: http://jsfiddle.net/Gajotres/K8YmG/
Rozwiązanie 3
Użyj selektora filtru jQuery, takiego jak ten:
$('#carousel div:Event(!click)').each(function(){
//If click is not bind to #carousel div do something
});
Ponieważ filtr zdarzeń nie jest częścią oficjalnej struktury jQuery, można go znaleźć tutaj: http://www.codenothing.com/archives/2009/event-filter/
W skrócie, jeśli głównym problemem jest szybkość, to Rozwiązanie 2 jest znacznie lepsze niż Rozwiązanie 1.
Rozwiązanie 4
Nowy, prawdopodobnie najłatwiejszy ze wszystkich.
$(document).on('pagebeforeshow', '#index', function(){
$(document).on('click', '#test-button',function(e) {
if(e.handled !== true) // This will prevent event triggering more than once
{
alert('Clicked');
e.handled = true;
}
});
});
Przykład działającego jsFiddle: http://jsfiddle.net/Gajotres/Yerv9/
Tnx do sholsinger dla tego rozwiązania: http://sholsinger.com/archive/2011/08/prevent-jquery-live-handlers-from-firing-multiple-times/
pageChange zdarzenia dziwactwa - wyzwalanie dwa razy
Czasami zdarzenie zmiany strony może zostać uruchomione dwukrotnie i nie ma to nic wspólnego z wyżej wymienionym problemem.
Powodem, dla którego zdarzenie pagebeforechange występuje dwukrotnie, jest wywołanie rekurencyjne w changePage, gdy toPage nie jest obiektem DOM wzbogaconym o jQuery. Ta rekurencja jest niebezpieczna, ponieważ programista może zmienić stronę na stronę w ramach zdarzenia. Jeśli programista konsekwentnie ustawia wartość toPage na ciąg znaków, w procedurze obsługi zdarzeń pagebeforechange, niezależnie od tego, czy był to obiekt, powstanie nieskończona pętla rekurencyjna. Zdarzenie pageload przekazuje nową stronę jako właściwość strony obiektu danych (należy ją dodać do dokumentacji, obecnie jej nie ma na liście). Zdarzenie pageload może zatem zostać wykorzystane do uzyskania dostępu do załadowanej strony.
W kilku słowach dzieje się tak, ponieważ przesyłasz dodatkowe parametry przez pageChange.
Przykład:
<a data-role="button" data-icon="arrow-r" data-iconpos="right" href="#care-plan-view?id=9e273f31-2672-47fd-9baa-6c35f093a800&name=Sat"><h3>Sat</h3></a>
Aby rozwiązać ten problem, użyj dowolnego zdarzenia strony wymienionego w kolejności przejścia zdarzeń strony .
Czas zmiany strony
Jak wspomniano, po przejściu z jednej strony jQuery Mobile na inną, zazwyczaj albo poprzez kliknięcie łącza do innej strony jQuery Mobile, która już istnieje w DOM, lub przez ręczne wywołanie $ .mobile.changePage, następuje kilka zdarzeń i kolejne działania. Na wysokim poziomie występują następujące działania:
- Rozpoczęto proces zmiany strony
- Załadowana jest nowa strona
- Treść tej strony jest „ulepszona” (stylizowana)
- Następuje przejście (slajd / pop / itp.) Z istniejącej strony do nowej strony
Jest to średni test przejścia strony:
Ładowanie strony i przetwarzanie: 3 ms
Ulepszenie strony: 45 ms
Przejście: 604 ms
Całkowity czas: 670 ms
* Te wartości są w milisekundach.
Jak widać, zdarzenie przejścia pochłania prawie 90% czasu wykonania.
Manipulowanie danymi / parametrami między przejściami stron
Możliwe jest przesyłanie parametrów z jednej strony na drugą podczas przejścia strony. Można to zrobić na kilka sposobów.
Odniesienie: https://stackoverflow.com/a/13932240/1848600
Rozwiązanie 1:
Możesz przekazać wartości za pomocą changePage:
$.mobile.changePage('page2.html', { dataUrl : "page2.html?paremeter=123", data : { 'paremeter' : '123' }, reloadPage : true, changeHash : true });
I przeczytaj je w ten sposób:
$(document).on('pagebeforeshow', "#index", function (event, data) {
var parameters = $(this).data("url").split("?")[1];;
parameter = parameters.replace("parameter=","");
alert(parameter);
});
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="widdiv=device-widdiv, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<title>
</title>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.css" />
<script src="http://www.dragan-gaic.info/js/jquery-1.8.2.min.js">
</script>
<script src="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.js"></script>
<script>
$(document).on('pagebeforeshow', "#index",function () {
$(document).on('click', "#changePage",function () {
$.mobile.changePage('second.html', { dataUrl : "second.html?paremeter=123", data : { 'paremeter' : '123' }, reloadPage : false, changeHash : true });
});
});
$(document).on('pagebeforeshow', "#second",function () {
var parameters = $(this).data("url").split("?")[1];;
parameter = parameters.replace("parameter=","");
alert(parameter);
});
</script>
</head>
<body>
<!-- Home -->
<div data-role="page" id="index">
<div data-role="header">
<h3>
First Page
</h3>
</div>
<div data-role="content">
<a data-role="button" id="changePage">Test</a>
</div> <!--content-->
</div><!--page-->
</body>
</html>
second.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="widdiv=device-widdiv, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<title>
</title>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.css" />
<script src="http://www.dragan-gaic.info/js/jquery-1.8.2.min.js">
</script>
<script src="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.js"></script>
</head>
<body>
<!-- Home -->
<div data-role="page" id="second">
<div data-role="header">
<h3>
Second Page
</h3>
</div>
<div data-role="content">
</div> <!--content-->
</div><!--page-->
</body>
</html>
Rozwiązanie 2:
Możesz też utworzyć trwały obiekt JavaScript do celów przechowywania. Dopóki Ajax jest używany do ładowania strony (a strona nie jest w żaden sposób przeładowywana), obiekt ten pozostanie aktywny.
var storeObject = {
firstname : '',
lastname : ''
}
Przykład: http://jsfiddle.net/Gajotres/9KKbx/
Rozwiązanie 3:
Możesz również uzyskać dostęp do danych z poprzedniej strony w następujący sposób:
$(document).on('pagebeforeshow', '#index',function (e, data) {
alert(data.prevPage.attr('id'));
});
Obiekt prevPage zawiera całą poprzednią stronę.
Rozwiązanie 4:
Jako ostatnie rozwiązanie mamy sprytną implementację HTML localStorage. Działa tylko z przeglądarkami HTML5 (w tym z przeglądarkami Android i iOS), ale wszystkie przechowywane dane są trwałe przez odświeżanie strony.
if(typeof(Storage)!=="undefined") {
localStorage.firstname="Dragan";
localStorage.lastname="Gaic";
}
Przykład: http://jsfiddle.net/Gajotres/J9NTr/
Prawdopodobnie najlepsze rozwiązanie, ale zawiedzie w niektórych wersjach iOS 5.X. To dobrze znany błąd.
Nie używaj .live()
/ .bind()
/.delegate()
Zapomniałem wspomnieć (i tnx andleer dla przypomnienia mi) użycie włączania / wyłączania do wiązania / cofania powiązania zdarzeń, live / die i bind / unbind są przestarzałe.
Metoda jQuery .live () była postrzegana jako dar niebios, kiedy została wprowadzona do API w wersji 1.3. W typowej aplikacji jQuery może istnieć wiele manipulacji DOM i przechwytywanie i odczepianie może być bardzo uciążliwe, gdy elementy pojawiają się i znikają. Ta .live()
metoda umożliwiła przechwycenie zdarzenia przez cały czas działania aplikacji na podstawie jej selektora. Świetnie prawda? Źle, .live()
metoda jest bardzo powolna. .live()
Metoda rzeczywiście haki jego zdarzeń do obiektu dokumentu, co oznacza, że bańka musi wydarzenie się z elementu, który wygenerował zdarzenie, aż osiągnie dokument. Może to być niezwykle czasochłonne.
To jest teraz przestarzałe. Ludzie w zespole jQuery nie zalecają już jego użycia i ja też tego nie robią. Mimo że przechwytywanie i odczepianie zdarzeń może być żmudne, kod będzie znacznie szybszy bez tej .live()
metody niż z nią.
Zamiast tego .live()
powinieneś użyć .on()
. .on()
jest około 2-3 razy szybszy niż .live () . Spójrz na to wiążące kryterium odniesienia dla wydarzenia: http://jsperf.com/jquery-live-vs-delegate-vs-on/34 , wszystko będzie jasne.
Benchmarking:
Istnieje doskonały skrypt do testów porównawczych zdarzeń stron jQuery Mobile . Można go znaleźć tutaj: https://github.com/jquery/jquery-mobile/blob/master/tools/page-change-time.js . Ale zanim cokolwiek z tym zrobisz, radzę usunąć alert
system powiadomień (każda „strona zmiany” pokaże te dane, zatrzymując aplikację) i zmienić ją na console.log
działającą.
Zasadniczo ten skrypt rejestruje wszystkie zdarzenia na stronie, a jeśli dokładnie przeczytasz ten artykuł (opisy zdarzeń na stronie), będziesz wiedział, ile czasu spędził jQm na ulepszenia strony, przejścia stron ...
Uwagi końcowe
Zawsze i mam na myśli zawsze czytać oficjalną dokumentację jQuery Mobile . Zwykle dostarcza potrzebnych informacji, w przeciwieństwie do innych dokumentów, ta jest raczej dobra, z wystarczającą ilością wyjaśnień i przykładów kodu.
Zmiany:
- 30.01.2013 - Dodano nową metodę zapobiegania wyzwalaniu wielu zdarzeń
- 31.01.2013 - Dodano lepsze wyjaśnienie dotyczące manipulacji rozdziałem Dane / parametry między przejściami stron
- 03.02.2013 - Dodano nową treść / przykłady do rozdziału Manipulacja danymi / parametrami między przejściami stron
- 22.05.2013 - Dodano rozwiązanie do zapobiegania zmianom stron / zmianom oraz dodano linki do oficjalnej dokumentacji interfejsu API zdarzeń stron
- 18.05.2013 - Dodano inne rozwiązanie przeciwko wiązaniu wielu zdarzeń