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 не отключена. Если вы хотите, чтобы код выполнялся при каждом посещении страницы, лучше использовать событие pagebeforeshow .

Вот рабочий пример: http://jsfiddle.net/Gajotres/Q3Usv/, чтобы продемонстрировать эту проблему.

Еще несколько заметок по этому вопросу. Независимо от того, используете ли вы 1 html несколько страниц или несколько парадигм HTML-файлов, рекомендуется разделить всю вашу пользовательскую обработку страниц JavaScript в один отдельный файл JavaScript. Это поможет улучшить ваш код, но у вас будет намного лучший обзор кода, особенно при создании мобильного приложения jQuery .

Есть еще одно специальное мероприятие jQuery Mobile, которое называется mobileinit . Когда запускается jQuery Mobile , он вызывает событие mobileinit для объекта документа. Чтобы переопределить настройки по умолчанию, свяжите их с 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 - событие страницы

  4. страница A - страница события перед

  5. страница А - событие удаление страницы

  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, событие клика будет привязано к кнопке # test-button . Проверьте это, переходя от страницы 1 к странице 2 и обратно несколько раз. Есть несколько способов предотвратить эту проблему:

Решение 1

Лучшее решение будет использовать pageinitдля привязки событий. Если вы посмотрите на официальную документацию, вы обнаружите, что она pageinitсработает ТОЛЬКО один раз, точно так же, как документ готов, так что события никак не будут связаны снова. Это лучшее решение, потому что у вас нет затрат на обработку, как при удалении событий методом off.

Рабочий пример 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 Filter, например:

$('#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/

Причуды события pageChange - срабатывание дважды

Иногда событие pagechange может инициироваться дважды, и оно не имеет ничего общего с упомянутой ранее проблемой.

Причина, по которой событие pagebeforechange происходит дважды, связана с рекурсивным вызовом в changePage, когда toPage не является расширенным объектом DOM jQuery. Эта рекурсия опасна, так как разработчику разрешено изменять toPage внутри события. Если разработчик последовательно устанавливает toPage в строку, в обработчике события pagebeforechange, независимо от того, был ли это объект, будет бесконечный рекурсивный цикл. Событие pageload передает новую страницу как свойство страницы объекта данных (это следует добавить в документацию, в данный момент она не указана). Следовательно, событие загрузки страницы может использоваться для доступа к загруженной странице.

В двух словах это происходит потому, что вы отправляете дополнительные параметры через 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, происходит несколько событий и последующих действий. На высоком уровне происходят следующие действия:

  • Процесс смены страницы начат
  • Новая страница загружена
  • Содержимое этой страницы «улучшено» (стилизовано)
  • Происходит переход (слайд / поп / etc) с существующей страницы на новую.

Это средний тест перехода страницы:

Загрузка и обработка страницы: 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 для напоминания), использование on / off для привязки / отмены привязки событий, 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
У вас есть страница до показа дважды. Он указан как номер 5 и номер 8. Он вызывается дважды?
Чейз Робертс

Это была опечатка, я ее исправил, pagebeforeshow сработает только один раз. Спасибо, что заметили это.
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.