jQuery Mobile:文档准备和页面事件

我正在使用jQuery Mobile,我很难理解经典文档准备和jQuery Mobile页面事件之间的差异。

  1. 真正的区别是什么?

    为什么要

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

    比…

    $(document).on('pageinit') {
    
    
    });
    
  2. What is the order of page events, when you transition from one page to another?

  3. How can I send data from one page to another and is it possible to access data from previous page?

157479 次浏览

jQuery Mobile 1.4更新:

我最初的文章是针对旧的页面处理方式,基本上是jQuery Mobile 1.4之前的所有内容。旧的处理方式现在已弃用,它将一直活跃到(包括)jQuery Mobile 1.5,所以你仍然可以使用下面提到的所有东西,至少到明年的jQuery Mobile 1.6。

旧的事件,包括pageinit已经不存在了,它们被pagecontainer小部件取代。Pageinit被完全擦除,你可以使用pagecreate代替,该事件保持不变,它不会被改变。

如果你对页面事件处理的新方法感兴趣,请查看here,在任何其他情况下,请继续阅读本文。即使你使用的是jQuery Mobile 1.4 +,你也应该阅读这个答案,它超越了页面事件,所以你可能会发现很多有用的信息。

旧的内容:

这篇文章也可以在我的博客HERE中找到。

$(document).on('pageinit') vs $(document).ready()

你在jQuery中学到的第一件事是在$(document).ready()函数中调用代码,这样DOM一加载就会立即执行。然而,在jQuery移动中,Ajax用于在导航时将每个页面的内容加载到DOM中。因此,$(document).ready()将在加载第一个页面之前触发,并且用于页面操作的每个代码将在页面刷新后执行。这可能是一个非常微妙的错误。在一些系统上,它可能表现得很好,但在另一些系统上,它可能会导致不稳定,难以重复的怪异现象发生。

经典jQuery语法:

$(document).ready(function() {


});

为了解决这个问题(相信我,这是一个问题)jQuery移动开发人员创建了页面事件。简而言之,页面事件是在页面执行的特定点触发的事件。其中一个页面事件是pageinit事件,我们可以这样使用它:

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


});

我们甚至可以更进一步,使用页面id来代替文档选择器。假设我们有一个id为指数的jQuery Mobile页面:

<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移动事件,它被称为mobileinit。当jQuery移动开始时,它在文档对象上触发mobileinit事件。要覆盖默认设置,将它们绑定到mobileinit。使用mobileinit的一个很好的例子是关闭Ajax页面加载,或者改变默认的Ajax加载器行为。

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

页面事件转换顺序

首先,所有事件都可以在这里找到:http://api.jquerymobile.com/category/events/

假设我们有一个页面a和一个页面B,这是一个卸载/加载顺序:

  1. page B - event pagebeforecate

  2. page B - event pagecreate

  3. page B - event pageinit

  4. page A - event < A href="http://api.jquerymobile.com/pagebeforehide/" rel="noreferrer">pagebeforehide

  5. page A - event < A href="http://api.jquerymobile.com/pageremove/" rel="noreferrer">pageremove

  6. page A - event < A href="http://api.jquerymobile.com/pagehide/" rel="noreferrer">pagehide

  7. page B - event pagebeforeshow

  8. page B - event pageshow

为了更好地理解页面事件,请阅读以下内容:

  • pagebeforeloadpageloadpageloadfailed在加载外部页面时被触发
  • pagebeforechangepagechangepagechangefailed是页面更改事件。当用户在应用程序中的页面之间导航时触发这些事件。
  • pagebeforeshowpagebeforehidepageshowpagehide是页面转换事件。这些事件在转换之前、期间和之后被触发,并被命名。
  • pagebeforecreatepagecreatepageinit用于页面初始化。
  • 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的工作方式与经典的web应用程序不同。每次访问某个页面时,它都会一次又一次地绑定事件,这取决于您如何绑定事件。这不是错误,这只是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/

每次你访问页面#指数点击事件将被绑定到按钮#测试按钮。通过从第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过滤器选择器,像这样:

$('#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事件异常——触发两次

有时页面更改事件可以触发两次,它与前面提到的问题没有任何关系。

pagebeforechange事件发生两次的原因是由于当toPage不是jQuery增强的DOM对象时,在changePage中进行了递归调用。这种递归是危险的,因为允许开发人员在事件中更改toPage。如果开发人员始终将toPage设置为字符串,则在pagebeforechange事件处理程序中,无论它是否是一个对象,都将导致无限递归循环。pageload事件将新页面作为数据对象的页面属性传递(这应该添加到文档中,目前没有列出)。因此,可以使用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页面切换到另一个页面时,通常是通过单击DOM中已经存在的另一个jQuery Mobile页面的链接,或者手动调用$. 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:

作为最后一个解决方案,我们有一个漂亮的localStorage的HTML实现。它只适用于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已弃用。

jQuery的.live()方法在1.3版引入API时被视为天赐之物。在一个典型的jQuery应用程序中,可能有很多DOM操作,随着元素的来来去去,钩子和解钩子会变得非常乏味。.live()方法使得基于它的选择器在应用程序的生命周期中挂钩一个事件成为可能。伟大的对吧?错误,.live()方法非常慢。.live()方法实际上将其事件绑定到文档对象,这意味着事件必须从生成事件的元素冒泡上升,直到到达文档。这是非常耗时的。

现在已弃用。jQuery团队的人不再推荐使用它,我也不推荐。尽管钩子和解钩子事件可能很乏味,但是没有.live()方法你的代码会比有.live()方法快得多。

你应该使用.on()而不是.live().on().live ()快2-3倍。看看这个事件绑定基准:http://jsperf.com/jquery-live-vs-delegate-vs-on/34,从那里一切都清楚了。

基准:

这里有一个用于jQuery移动页面事件基准测试的优秀脚本。它可以在这里找到:https://github.com/jquery/jquery-mobile/blob/master/tools/page-change-time.js。但在你对它做任何事情之前,我建议你删除它的alert通知系统(每个“更改页面”将通过停止应用程序向你显示这些数据),并将其更改为console.log函数。

基本上,这个脚本将记录所有的页面事件,如果您仔细阅读本文(页面事件描述),您将知道jQm在页面增强、页面转换....上花费了多少时间

最后指出

总是,我的意思是总是阅读官方jQuery移动文档。它通常会为您提供所需的信息,与其他文档不同的是,这个文档相当不错,有足够的解释和代码示例。

变化:

  • 30.01.2013 -增加了一个新的方法多事件触发预防
  • 2013年1月31日-对页面转换之间的数据/参数操作章增加了更好的说明
  • 03.02.2013 -增加了新的内容/例子章节页面转换之间的数据/参数操作
  • 22.05.2013 -增加了防止页面转换/更改的解决方案,并添加了官方页面事件API文档的链接
  • 18.05.2013 -增加了另一个针对多事件绑定的解决方案

这是正确的方法:

要执行仅对索引页可用的代码,可以使用以下语法:

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

在jQuery-mobile中,文档准备和页面事件之间的简单区别是:

  1. 文档就绪事件用于整个HTML页面,

    $(document).ready(function(e) {
    // Your code
    });
    
  2. When there is a page event, use for handling particular page event:

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

You can also use document for handling the pageinit event:

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


});

有些人可能会觉得这很有用。只需复制粘贴到你的页面,你会得到一个序列,其中事件在Chrome控制台中被触发(Ctrl + 转变 + )。

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

您不会在控制台中看到unload,因为它是在页面卸载时(当您离开页面时)触发的。像这样使用它:

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

你会明白我的意思。

当您使用.on()时,它基本上是您正在使用的一个实时查询。

另一方面,.ready(在您的例子中)是一个静态查询。在使用它时,您可以动态地更新数据,而不必等待页面加载。当输入特定值时,可以简单地将值传递到数据库中(如果需要)。

在输入数据(帐户、帖子甚至评论)的表单中,经常使用实时查询。