jQuery Mobile: документ готовий до подій сторінки


269

Я використовую jQuery Mobile, і у мене виникають проблеми з розумінням відмінностей між класичними документами, готовими до документа, та подіями сторінки jQuery Mobile.

  1. Яка реальна різниця?

    Чому повинен

    <!-- language: lang-js -->
    
    $(document).ready() {
    
    });

    бути краще, ніж

    $(document).on('pageinit') {
    
    });
  2. Який порядок подій сторінки при переході з однієї сторінки на іншу?

  3. Як я можу надсилати дані з однієї сторінки на іншу і чи можливий доступ до даних з попередньої сторінки?


Під питанням 1 вони обоє однакові. Чи можете ви це змінити чи пояснити трохи більше того, що ви маєте на увазі?
Кірк

Так, менш ніж через рік щодо події pageinit "ця подія була вимкнена в 1.4.0 на користь створення сторінки". Дивіться api.jquerymobile.com/pageinit
DOK

Відповіді:


439

Оновлення jQuery Mobile 1.4:

Моя оригінальна стаття була призначена для старого способу обробки сторінки, в основному все до jQuery Mobile 1.4. Старий спосіб обробки тепер застарілий, і він залишатиметься активним до (включаючи) jQuery Mobile 1.5, тому ви все ще можете використовувати все, що було зазначено нижче, принаймні до наступного року та jQuery Mobile 1.6.

Старі події, включаючи pageinit , вже не існують, вони замінюються віджетом pagecontainer . Pageinit видаляється повністю, і замість цього ви можете використовувати pagecreate , що подія залишилася такою ж, і її не буде змінено.

Якщо вас цікавить новий спосіб обробки подій на сторінці, погляньте сторінці , тут , у будь-якому іншому випадку сміливо продовжуйте цю статтю. Ви повинні прочитати цю відповідь, навіть якщо ви використовуєте jQuery Mobile 1.4 +, вона виходить за рамки подій сторінки, тому ви, ймовірно, знайдете багато корисної інформації.

Старіший вміст:

Цю статтю можна знайти також у складі мого блогу ТУТ .

$(document).on('pageinit') проти $(document).ready()

Перше, що ви дізнаєтесь у jQuery - це зателефонувати до коду всередині $(document).ready()функції, щоб все виконалось, як тільки DOM буде завантажений. Однак у jQuery Mobile Ajax використовується для завантаження вмісту кожної сторінки в DOM під час навігації. Через це $(document).ready()спрацює перед завантаженням вашої першої сторінки, і кожен код, призначений для маніпулювання сторінкою, буде виконуватися після оновлення сторінки. Це може бути дуже тонка помилка. У деяких системах може здатися, що вона працює чудово, але в інших може спричинити нестабільність, складно повторити дивацтва.

Класичний синтаксис jQuery:

$(document).ready(function() {

});

Щоб вирішити цю проблему (і повірте, це проблема), розробники jQuery Mobile створили події на сторінці. У двох словах, події - це події, викликані певним моментом виконання сторінки. Однією з таких подій на сторінці є подія pageinit, і ми можемо використовувати її так:

$(document).on('pageinit', function() {

});

Ми можемо піти ще далі і використовувати ідентифікатор сторінки замість вибору документа. Скажімо, у нас є сторінка jQuery Mobile з індексом id :

<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>

Для виконання коду, який буде доступний лише для сторінки індексу, ми могли використовувати цей синтаксис:

$('#index').on('pageinit', function() {

});

Подія Pageinit буде виконуватися кожного разу, коли під час завантаження сторінки буде показана вперше. Він не запуститься знову, якщо сторінку не буде оновлено вручну або не буде вимкнено завантаження сторінки Ajax. Якщо ви хочете, щоб код виконувався щоразу, коли ви відвідуєте сторінку, краще скористатися сторінкою шоу.

Ось робочий приклад: http://jsfiddle.net/Gajotres/Q3Usv/, щоб продемонструвати цю проблему.

Трохи більше приміток до цього питання. Незалежно від того, якщо ви використовуєте 1 html кілька сторінок або парадигму декількох файлів HTML, радимо розділити всі ваші користувацькі сторінки обробки JavaScript в один окремий файл JavaScript. Це помітить, що полегшить ваш код, але у вас буде набагато кращий огляд коду, особливо під час створення програми jQuery Mobile .

Також є ще одна спеціальна подія jQuery Mobile, і вона називається mobileinit . Коли jQuery Mobile запускається, він запускає подію mobileinit на об'єкті документа. Щоб змінити налаштування за замовчуванням, прив’яжіть їх до mobileinit . Одним із хороших прикладів використання мобільних пристроїв є вимкнення завантаження сторінки Ajax або зміна поведінки завантажувача Ajax за замовчуванням.

$(document).on("mobileinit", function(){
  //apply overrides here
});

Порядок переходу подій на сторінці

По-перше, всі події можна знайти тут: http://api.jquerymobile.com/category/events/

Скажімо, у нас є сторінка A і сторінка B, це порядок вивантаження / завантаження:

  1. сторінка B - сторінка подій, попередньо створити

  2. сторінка B - подія pagecreate

  3. сторінка B - подія pageinit

  4. сторінка A - сторінка подій перед головою

  5. сторінка A - усунення сторінки події

  6. сторінка A - подія pagehide

  7. сторінка B - сторінка події перед попереднім шоу

  8. сторінка B - покази сторінок

Для кращого розуміння подій сторінки читайте це:

  • pagebeforeload, pageloadі pageloadfailedзнімаються під час завантаження зовнішньої сторінки
  • pagebeforechange, pagechangeі pagechangefailedє подіями зміни сторінки. Ці події запускаються, коли користувач переходить між сторінками в додатках.
  • pagebeforeshow, pagebeforehide, pageshowІ pagehideподії , перехідні сторінки. Ці події запускаються до, під час та після переходу та мають назву.
  • pagebeforecreate, pagecreateі pageinitпризначені для ініціалізації сторінки.
  • pageremove може бути звільнено та оброблено після видалення сторінки з DOM

Приклад завантаження сторінки jsFiddle: http://jsfiddle.net/Gajotres/QGnft/

Якщо AJAX не ввімкнено, деякі події можуть не спрацьовувати.

Запобігання переходу сторінки

Якщо з певних причин перехід сторінки потрібно запобігти за певної умови, це можна зробити за допомогою цього коду:

$(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');;
        }
    }
});

Цей приклад буде працювати в будь-якому випадку, оскільки він запускатиме при випробовуванні кожного переходу сторінки, і що найважливіше, це запобіжить зміні сторінки до того, як може відбутися перехід сторінки.

Ось робочий приклад:

Запобігати багаторазовому прив'язці / запуску подій

jQuery Mobileпрацює інакше, ніж класичні веб-додатки. Залежно від того, як вам вдалося зв’язати свої події кожного разу, коли ви відвідуєте якусь сторінку, вона пов'язуватиме події знову і знову. Це не помилка, це просто те, як jQuery Mobileобробляє свої сторінки. Наприклад, подивіться на цей фрагмент коду:

$(document).on('pagebeforeshow','#index' ,function(e,data){
    $(document).on('click', '#test-button',function(e) {
        alert('Button click');
    });
});

Приклад роботи jsFiddle: http://jsfiddle.net/Gajotres/CCfL4/

Кожен раз, коли ви відвідуєте сторінку #index, подія натискання буде прив’язана до кнопки # тестова кнопка . Перевірте це, переміщаючи з сторінки 1 на сторінку 2 і назад кілька разів. Є кілька способів запобігти цій проблемі:

Рішення 1

Найкращим рішенням буде використання pageinitприв'язки подій. Якщо ви подивитесь на офіційну документацію, то виявите, що вона pageinitбуде запущена ТІЛЬКИ один раз, як і готовий документ, тому немає жодного способу, щоб події не були пов'язані знову. Це найкраще рішення, оскільки у вас немає обробки накладних витрат, як при видаленні подій методом вимкнення.

Приклад роботи jsFiddle: http://jsfiddle.net/Gajotres/AAFH8/

Це робоче рішення зроблене на основі попереднього проблемного прикладу.

Рішення 2

Видаліть подію, перш ніж зв’язати її:

$(document).on('pagebeforeshow', '#index', function(){
    $(document).off('click', '#test-button').on('click', '#test-button',function(e) {
        alert('Button click');
    });
});

Приклад роботи jsFiddle: http://jsfiddle.net/Gajotres/K8YmG/

Рішення 3

Скористайтеся селектором фільтра jQuery, наприклад таким:

$('#carousel div:Event(!click)').each(function(){
    //If click is not bind to #carousel div do something
});

Оскільки фільтр подій не є частиною офіційної системи jQuery, його можна знайти тут: http://www.codenothing.com/archives/2009/event-filter/

Коротше кажучи, якщо швидкість - це ваша головна проблема, то рішення 2 набагато краще, ніж рішення 1.

Рішення 4

Нова, мабуть, найпростіша з усіх.

$(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;
        }
    });
});

Приклад роботи jsFiddle: http://jsfiddle.net/Gajotres/Yerv9/

Tnx до sholsinger для цього рішення: http://sholsinger.com/archive/2011/08/prevent-jquery-live-handlers-from-firing-multiple-times/

pageЗмінити вигадки події - запускається двічі

Іноді подія зміни сторінки може викликати двічі, і це не має нічого спільного з проблемою, згаданою раніше.

Причина того, що подія pagebeforechange відбувається двічі, пов’язана з рекурсивним викликом в changePage, коли toPage не є об'єктом DOM, розширеним jQuery. Ця рекурсія небезпечна, оскільки розробник може змінити точку сторінки в рамках події. Якщо розробник послідовно встановлює toPage на рядок, всередині сторінки обробляти події pagebechange, незалежно від того, був це об'єктом чи ні, не призведе до нескінченного рекурсивного циклу. Подія завантаження сторінки передає нову сторінку як властивість сторінки об’єкта даних (Це слід додати до документації, вона наразі не вказана). Тому подія завантаження сторінки може бути використана для доступу до завантаженої сторінки.

У кількох словах це відбувається, тому що ви надсилаєте додаткові параметри через pageChange.

Приклад:

<a data-role="button" data-icon="arrow-r" data-iconpos="right" href="#care-plan-view?id=9e273f31-2672-47fd-9baa-6c35f093a800&amp;name=Sat"><h3>Sat</h3></a>

Щоб виправити цю проблему, використовуйте будь-яку подію сторінки, вказану в порядку переходу сторінки .

Час зміни сторінки

Як зазначалося, при переході з однієї сторінки jQuery Mobile на іншу, як правило, або через натискання на посилання на іншу сторінку jQuery Mobile, яка вже існує в DOM, або за допомогою виклику $ .mobile.changePage вручну, відбувається кілька подій та наступні дії. На високому рівні відбуваються такі дії:

  • Починається процес зміни сторінки
  • Завантажується нова сторінка
  • Вміст цієї сторінки "покращений" (стилізований)
  • Відбувається перехід (слайд / попс тощо) з існуючої на нову сторінку

Це середній показник переходу сторінки:

Завантаження сторінки та обробка: 3 мс

Покращення сторінки: 45 мс

Перехід: 604 мс

Загальний час: 670 мс

* Ці значення є в мілісекундах.

Отже, як ви бачите, перехідна подія з'їдає майже 90% часу виконання.

Маніпулювання даними / параметрами між переходами сторінки

Під час переходу сторінки можна надсилати параметр / с з однієї сторінки на іншу. Це можна зробити декількома способами.

Довідка: https://stackoverflow.com/a/13932240/1848600

Рішення 1:

Ви можете передавати значення за допомогою changePage:

$.mobile.changePage('page2.html', { dataUrl : "page2.html?paremeter=123", data : { 'paremeter' : '123' }, reloadPage : true, changeHash : true });

І читайте їх так:

$(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>

Рішення 2:

Або ви можете створити стійкий об’єкт JavaScript для зберігання. Поки Ajax використовується для завантаження сторінки (і сторінка жодним чином не перезавантажується), цей об'єкт залишатиметься активним.

var storeObject = {
    firstname : '',
    lastname : ''
}

Приклад: http://jsfiddle.net/Gajotres/9KKbx/

Рішення 3:

Ви також можете отримати доступ до даних з попередньої сторінки, як це:

$(document).on('pagebeforeshow', '#index',function (e, data) {
    alert(data.prevPage.attr('id'));
});

об’єкт prevPage містить повну попередню сторінку.

Рішення 4:

Як останнє рішення, ми маємо чудову HTML-реалізацію localStorage. Він працює лише з браузерами HTML5 (включаючи браузери Android та iOS), але всі збережені дані зберігаються через оновлення сторінки.

if(typeof(Storage)!=="undefined") {
    localStorage.firstname="Dragan";
    localStorage.lastname="Gaic";
}

Приклад: http://jsfiddle.net/Gajotres/J9NTr/

Можливо, найкраще рішення, але воно не вдасться в деяких версіях iOS 5.X. Це добре відома помилка.

Не використовувати .live()/ .bind()/.delegate()

Я забув згадати (і tnx andleer для нагадування) використовувати ввімкнення / вимкнення для прив'язки / відключення подій, Live / die та bind / unbind застаріли.

Метод .live () jQuery розглядався як знахідка, коли він був представлений в API версії 1.3. У типовому додатку jQuery може бути багато маніпуляцій з DOM, і це може стати дуже стомлюючим підключити та зняти, як елементи приходять і йдуть. .live()Метод дозволив підключити подія для життя додатки на основі його вибору. Чудово правильно? Неправильно, .live()метод надзвичайно повільний. .live()Метод фактично перехоплює свої події на об'єкт документа, який означає , що необхідно подія пузиритися від елемента , який згенерував подія , поки він не досягне документа. Це може бути надзвичайно багато часу.

Зараз це застаріло. Люди, які працюють в команді jQuery, більше не рекомендують її використовувати, а також я. Незважаючи на те, що це може бути стомлюючим підключити і зняти події, ваш код буде набагато швидшим без .live()методу, ніж з ним.

Замість .live()вас слід використовувати .on(). .on()приблизно в 2-3 рази швидше, ніж .live () . Погляньте на цей орієнтир прив'язки цієї події: http://jsperf.com/jquery-live-vs-delegate-vs-on/34 , звідти все буде зрозуміло.

Бенчмаркінг:

Існує чудовий сценарій, створений для тестування подій на сторінці jQuery Mobile . Його можна знайти тут: https://github.com/jquery/jquery-mobile/blob/master/tools/page-change-time.js . Але перш ніж робити щось з цим, я раджу вам видалити його alertсистему сповіщень (кожна "сторінка змін" відображатиме вам ці дані, зупинивши додаток) та змініть їх на console.logфункціонування.

В основному цей сценарій буде реєструвати всі ваші події на сторінці, і якщо ви уважно прочитаєте цю статтю (описи подій на сторінці), ви дізнаєтесь, скільки часу витратили jQm на вдосконалення сторінки, переходи сторінок ....

Підсумкові ноти

Завжди, і я маю на увазі завжди читати офіційну документацію jQuery Mobile . Зазвичай він надасть вам необхідну інформацію, і на відміну від іншої документації, ця досить хороша, з достатньою кількістю пояснень та прикладів коду.

Зміни:

  • 30.01.2013 - Додано новий метод запобігання, що викликає багаторазові події
  • 31.01.2013 - Додано краще уточнення для маніпулювання розділом " Дані / Параметри" між переходами сторінок
  • 03.02.2013 - До розділу « Маніпуляції з даними / параметрами» між переходами сторінки додано новий вміст / приклади
  • 22.05.2013 - Додано рішення для запобігання переходам / змінам сторінок та додано посилання на документацію API офіційних подій сторінки
  • 18.05.2013 - Додано ще одне рішення проти прив'язки до кількох подій

2
$().live()було знецінено в jQuery 1.7 та видалено в 1.9, тому воно дійсно повинно бути частиною будь-якого рішення jQuery Mobile. Поточна мінімальна версія ядра для jQM 1.7.
andleer

16
+1 дуже корисний підсумок критичної поведінки навколо завантаження сторінки
RedFilter

2
pagecreateподія запускається лише один раз, коли сторінка створена вперше. тож якщо ми зв’яжемо події натискання всередині, pagecreateвона не запуститься кілька разів. Щось я зрозумів, розробляючи додаток. Але ми не завжди можемо використовувати pagecreateприв'язку подій, тому рішення, яке ви надали, є найкращим. +1 надано
Джей Маю

1
У вас є сторінкаBeforeShow двічі. Він вказаний як номер 5 і номер 8. Чи дзвонять йому двічі?
Чейз Робертс

Це був помилковий помилок, я це виправив, сторінка перед показом запуститься лише один раз. Дякуємо, що помітили це.
Gajotres

17

Дехто з вас може вважати це корисним. Просто скопіюйте вставте його на свою сторінку, і ви отримаєте послідовність, в якій події запускаються в консолі Chrome ( Ctrl+ Shift+ I).

$(document).on('pagebeforecreate',function(){console.log('pagebeforecreate');});
$(document).on('pagecreate',function(){console.log('pagecreate');});
$(document).on('pageinit',function(){console.log('pageinit');});
$(document).on('pagebeforehide',function(){console.log('pagebeforehide');});
$(document).on('pagebeforeshow',function(){console.log('pagebeforeshow');});
$(document).on('pageremove',function(){console.log('pageremove');});
$(document).on('pageshow',function(){console.log('pageshow');});
$(document).on('pagehide',function(){console.log('pagehide');});
$(window).load(function () {console.log("window loaded");});
$(window).unload(function () {console.log("window unloaded");});
$(function () {console.log('document ready');});

Ви не збираєтеся бачити вивантаження в консолі, оскільки воно випускається під час завантаження сторінки (коли ви віддаляєтесь від сторінки). Використовуйте його так:

$(window).unload(function () { debugger; console.log("window unloaded");});

І ти побачиш, що я маю на увазі.


4

Це правильний спосіб:

Для виконання коду, який буде доступний лише на сторінці індексу, ми могли б використовувати цей синтаксис:

$(document).on('pageinit', "#index",  function() {
    ...
});

10
відповідь вище говорить те ж саме, ви не думаєте? :)
Омар

Дякуємо за хорошу швидку виправлення короткої відповіді. :-)
SharpC

1

Проста різниця між документом, готовим до документа, та подією сторінки в jQuery-mobile полягає в тому, що:

  1. Подія готового документа використовується для всієї сторінки HTML,

    $(document).ready(function(e) {
        // Your code
    });
  2. Коли відбувається подія на сторінці, використовуйте для обробки певної події сторінки:

    <div data-role="page" id="second">
        <div data-role="header">
            <h3>
                Page header
            </h3>
        </div>
        <div data-role="content">
            Page content
        </div> <!--content-->
        <div data-role="footer">
            Page footer
        </div> <!--footer-->
    </div><!--page-->

Ви також можете використовувати документ для обробки події pageinit:

$(document).on('pageinit', "#mypage", function() {

});

-1

Поки ви використовуєте .on (), ви в основному використовуєте запит наживо.

З іншого боку, .ready (як і у вашому випадку) є статичним запитом. Під час його використання ви можете динамічно оновлювати дані та не потрібно чекати завантаження сторінки. Ви можете просто передати значення у вашу базу даних (якщо потрібно), коли вводиться певне значення.

Використання живих запитів поширене у формах, де ми вводимо дані (акаунт, повідомлення чи навіть коментарі).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.