mobilebone.js-mobile移动web APP单页切换骨架

这篇文章发布于 2014年10月31日,星期五,13:34,归类于 JS实例, Mobile相关。 阅读 282302 次, 今日 73 次 291 条评论

 
牛逼大了,有官网网站啦!没错,地址就是:http://www.mobilebone.org/

一、mobilebone.js吹牛不打草稿

mobilebone.js是鄙人2014下半年个人开源项目代表作。

先容我吹嘘一番,反正吹牛又不要交税。

  • 轻便体积小
  • 原生无依赖
  • 插件可扩展
  • 设计无限制
  • 动效可定制
  • 动静两相宜
  • 能进亦能退
  • 桌面也兼修

一句话功能简介
跟传统网页浏览的差别仅仅在于无刷新!

例如,我们浏览首页,首页上有个如下HTML链接:

<a href="mocamoca.html">摩擦摩擦</a>

在传统页面,页面会刷新跳转至mocamoca.html, 但是,引入mobilebone.js后,就是无属性滑动到mocamoca.html页面。

OK,我特意用手机拍手机拍了段视频给大家感受下(视频中的页面为项目中的测试页面)(舍不得买iPhone, 测试机为Android)(优酷上传太慢40K/s果断投奔爱奇艺了2M/s):

当然,你也可以打开自己的手机浏览器、或者微信扫描下面二维码,体验同时顺便帮忙众测下,可以评论或者去Github项目issues(https://github.com/zhangxinxu/mobilebone/issues)反馈给类问题(前后两个图分别是develop和master两个分支的测试页):

develop分支的测试页地址 项目测试页面二维码

如果你是在手机浏览器访问本页面,试试直接点击这里感受一番。

二、mobilebone.js项目、资源以及八卦

Mobilebone项目已经发布到Github上了,项目地址为:https://github.com/zhangxinxu/mobilebone

注意注意:有任何使用问题,建议前去 https://github.com/zhangxinxu/mobilebone/issues 提问,方便有其他类似问题的小伙伴发现与查找,我也能及时反馈与更新。

如果你觉得此项目很赞,欢迎star, 如果你想参与建设,欢迎fork!

clone该项目可使用下面命令行代码:

git clone git://github.com/zhangxinxu/mobilebone.git

也可以使用npm:

npm install mobilebone

目录结构:

--
  --example           精湛示例
  --docs              APIs文档
  --test              UI比较挫的测试页面
    index.html        测试引导
    --base-slide      基本切换
    --ajax-html       Ajax请求HTML测试
    --ajax-json       Ajax请求JSON数据测试
    --callback        回调测试
    --transition      其他切换效果测试,animate.css可以关注下
    --fixed-header-footer    固定的头部与底部
    --form-submit     表单提交
    --prevent-default Mobilebone默认行为的中断与介入
    --modular-load    模块化加载测试
    --backbone        与backbone组合使用演示
    --complex         复杂实例,微信模拟。fastclick.js好基友可以关注下
  --src               mobilebone.js和mobilebone.css文件

初次将项目publish到npm上,经验不足,若有什么不对头的地方,欢迎诸位帮忙指正!

更新于2014-11-13 01:31
新的专门的API文档页面已经出炉,可以点击这里或者这里查看

更深入内容,还是需要参考本文内容。

更新于2014-11-14 16:45
今天博友_iancj发布了基于mobilebone.js的webapp demo一枚,地址是http://iancj.github.io/qq/, 项目地址是:https://github.com/iancj/qq. 其对Mobilebone的评价如下截图(Mobilebone.js真的很好用):
_iancj对Mobilebone的评价

欢迎其他小伙伴展示你的实例,加入Mobilebone的建设。

为什么叫Mobilebone?
1. 顾名思意,移动骨头。mobilebone.js也确实人如其名,只做一件事情,移动端单页切换,所以mobilebone.js很轻便,也很灵活,也没什么限制,反而铸就了其强大。
2. 以前的一些个人项目,会加上zxx之类的标示,甚觉得格局太低了。所以,命名为Mobilebone这么大气的名词,就是希望可以冲出中国,走向世界。万一火了呢,就可以跟Backbone平起平坐啦!
3. 我特意百度谷歌之,恩,没人用这个名词。Github上也没有类似名字的项目,于是就是它的,独一无二的!

为何想到做Mobilebone?
早在11年移动刚兴起的时候,我就折腾过jQuery Mobile以及Phonegap. 好吧,也就是折腾过,后来很快就把jQuery Mobile给丢弃了,主要有两点:一是重,而是UI限制太大,只适合个人项目!但是,其switch切换是挺值得借鉴的。

上个月,应该是上上个月,游击了一个手Q的项目,做重构稿的时候,写了个单页切换方法,主要是为了重构稿交互演示。结果被开发直接拿去用了,于是问题来了,此切换方法并没有添加Ajax处理,也没有history路由处理。以至于最后的实现代码不醇厚-用人话表示就是“乱”。此时,我意识到,应该可以写个专门负责单页切换的JS组件。加上自己多年的相关积累,Android2.3这些版本不需要兼容,我觉得时机很成熟了。

于是,前后一个多月,利用业余时间,编写、测试与反复优化、细节调整,终于发布了这个个人项目。我会积极在厂内外推广,农村和城市齐包围,万一真火了呢!

Mobilebone适用场景
类原生APP的过场体验,适用于这些场景:
1. Phonegap等类似跨移动开发平台,其静态页面都是index.html, 单页面,因此,需要跟原生一样的过场体验。自己帮设计师实现iOS原型时候需要。
2. Hybird appHybrid app开发,原生APP内嵌web APP, 为了两者体验一致,不至于交互太唐突,也需要无刷新过场效果。例如,上面提到的那个手Q项目。
3. 就算是纯粹的移动web APP, 使用无刷新模式也不失为一种不错的选型策略。
4. 一些高大上的在线幻灯片演示,无需兼容的翻页滚屏网站……等

Mobilebone兼容性
mobilebone.js基于ES5编写,应用了部分HTML特性,原生JS,不依赖任何其他JS框架,不支持Android 2.3及其以下版本,不支持IE6-IE9. 如果这些搓浏览器引入mobilebone.js, 不会报错,不影响正常使用。

三、mobilebone.js基本使用

首先,引入相关的CSS和JS:

<link rel="stylesheet" href="mobilebone.css">
<script src="mobilebone.js"></script>

此时,就会有一个全局的Mobilebone对象,包含一些属性与方法。

然后,HTML结构有一定的规则。

body
  page
  page
  page

上面规则是什么意思呢?

我们可以看下mobilebone.css结构相关的CSS代码:
结构相关的CSS代码

传统网页,body基本上就是页面代名词,滚动条多半也是body标签产生。但是,一个页面貌似只能一个body,所以,单页switch切换主体就不能是body元素,于是,降级,以body子元素page作为每个页面的框架结构,担当body角色。于是,偶们看到的切换效果,就是page间的相互纠缠效果。

这种HTML结构与CSS布局的另外一个好处就是,可以方便实现兼容的头部底部固定效果(position:fixed效果问题依然多多)。例如下面这个base-slide测试页面的HTML结构:

<body>
  <div id="pageHome" class="page out"></div>
  <div id="page1" class="page out"></div>
  <div id="page2" class="page out"></div>
  <div id="page3" class="page out"></div>
</body>

.page对应的元素就是我们的每一个页面,我们可以在此div中尽情书写我们的设计布局。一般而言,要在page元素内部再嵌套一个content元素,主要为了实现滚动。如果使用iScroll滚动, 无需定高;如果原生滚动,content元素需要有特定高度值。

然后,什么也不用做,页面就能进入无刷新切换模式,超赞的有木有。

如果你想做一些设置,直接在引入mobilebone.js之后设置就好了。例如下面Mobilebone.captureLink的设置:

<script src="mobilebone.js"></script>
<script>
Mobilebone.captureLink = false;
</script>

因为,Mobilebone的默认初始化在DOMContentLoaded之后,因此不需要担心顺序问题。不过,如果你是页面load完毕后再以模块化方式(如seajs~)加载mobilebone.js,需要手动初始化一下,此时,就要注意顺序,初始化在参数设置的前面,如下:

Mobilebone.captureLink = false;
Mobilebone.init();  // 初始化

记住,一个页面只能初始化一次,以免文档事件重复绑定。

页面title变化
当从一个页面切换到另外一个页面时候,自然是会有title的变化的。如果是基本切换,则取自进入page页面元素的data-title属性值;如果是Ajax请求,且返回的是HTML字符串,如果返回标签包含title元素,则会取title的值作为页面的标题,或者通过触发行为的a元素的data-title属性设置。

具体可参考Ajax请求HTML测试页面的相关源代码。

四、mobilebone.js与基本切换

所谓基本切换,指的是无请求,无延迟的即时切换。表现为:每个page在第1次加载完毕后,就已经存在页面,所谓的切换仅仅是这些page元素的位置变化。

就好比我们使用PowerPoint文件,每次打开一个屁屁踢幻灯片文件,一个一个幻灯片页面实际都是已经存在的,我们的浏览,其实都是幻灯片页面的位置变化,这就是基本切换。

在DOM层面,只有一种情况会触发基本切换,href值为锚链的a元素。例如:

<a href="#pageId">

于是,当我们tap/click这个a元素的时候,Mobilebone会自动寻找idpageId的页面,如果此页面存在,则发生切换动画;如果没有该页面元素,没有任何反应,死链。

若有兴趣,可以轻戳这里访问感受基本切换效果。

相反的过场方向
动画的方向不可能都是从右往左的,例如,返回,显然是需要刚进入动画相反,符合正常认知。要实现,很简单,通过添加data-rel="back"就可以了,例如:

<a href="#pageHome" data-rel="back">返回</a>

此时,元素过新增一个类名reverse反方向运动。

然而,有时候,我们无法确定动画的方向,例如,固定在底部的导航,如果导航3从导航2过来,自然是正方向;但如果是从导航4过来,则要反方向。data-rel该如何设置呢?

哈,使用"auto"即可,如下:

<a href="#pageHome" data-rel="auto">前进还是后退?</a>

Mobilebone会自动判别页面在舞台上的位置,智能识别运动方向。浏览器的历史记录前进与后退也是采用的"auto"判别机制。

此应用可参考头尾固定测试页面test/fixed-header-footer/index.html.

data-rel控制访问同样适用于下面的Ajax切换。

五、mobilebone.js与Ajax切换

实际项目,可能有10+个页面,显然是不可能全部一次性载入的,又大又慢,对于流量如金的移动页面,是损耗也是浪费。所以,页面内容还是要一个一个加载实在,这就需要Ajax切换了。

Ajax加载并切换的实现很简单,你不需要做任何操作,就跟传统的web页面一样就好,使用href指向要加载的页面地址,例如:

<a href="ajax.html">

此时,当我们tap/click这个a元素的时候,Mobilebone会以Ajax的形式请求ajax.html这个页面,返回的数据会封装成page页面,并以指定的过场动画载入。是不是简单得有点过分了?没错,所以下面要加点料。

1. Ajax请求参数
既然是Ajax请求,自然少不了请求参数了。Mobilebone中的Ajax借用了jQuery中$.ajax()方法的参数命名,主要如下:

var defaults = {
    url: "",
    dataType: "",
    data: {},
    timeout: 10000,
    async: true,
    username: "",
    password: "",
    success: function() {},
    error: function() {},
    complete: function() {}	
}

<a>元素传参策略
对于元素,我们是直接通过属性设置传参。有两种支持的形式,分别为data-*data-params.

  • data-*是指需要传递参数,把参数名替换这里的星号(不区分大小写),并赋予参数值。例如:
    <a href="ajax.html" data-timeout="30000">

    就是设置请求超时时间为30秒。

    不过仍有两个注意点:

    1. 如果href值正常,则data-url地址会被忽略,依然使用href对应地址作为Ajax请求地址。
    2. 没有data-data, 而是data-formdata. 例如:
      <a href="ajax.html" data-formdata="c=1&d=1">
  • data-params值则是查询序列串。因为如果需要自定义的参数过多,标签上就会有很多data-*属性,略啰嗦。于是,可以以查询序列串的形式作为data-params的值,例如:
    <a href="ajax.html" data-params="datatype=json&timeout=20000&success=fun_success">

    有人可能会疑问,如果存在data-*data-params冲突情况怎么办?哈,data-*优先级大于data-params. 所以,类似下面代码,则最后请求超时时间为30s, data-params中的20s会被忽略。

    <a href="ajax.html" data-timeout="30000" data-params="datatype=json&timeout=20000&success=fun_success">

    还有一点,不支持data参数的序列化使用,请使用上面的data-formdata. 有兴趣可以轻戳这里看下如何使用的。

Ajax回调函数
Ajax回调函数有三个,跟jQuery的Ajax请求一样,分别是success, error, 与complete. 分别表示请求成功,请求失败与请求完成(包含部分失败情况)。注意,下面开始高能了:

<a href="ajax.html" data-success="globalObject.fun.xxx_ajax_success">

上面的传参就是上面提到的data-*策略,类似,错误回调,可以使用data-error或者data-params="error=xxx". 下面问题来了,globalObject.fun.xxx_ajax_success表示什么意思?

如果使用了上面代码,只要不是估计瞎搞,在JS的世界里肯定有下面这位兄弟:

window.globalObject = {
    fun: {
        xxx_ajax_success: function() {}
    }
};

意思就是,当请求成功的时候,执行全局对象globalObject下的子对象fun下面的xxx_ajax_success这个方法。最后一个字符串段一定是方法名,否则是不会有任何执行的。例如:

<a href="ajax.html" data-success="xxx_ajax_success">

则表示请求成功的时候,调用全局方法xxx_ajax_success.

  • 成功回调函数支持两个参数,success(response, status), 其中response表示Ajax请求返回的内容,HTML或者JSON. status这个参数其实没啥用,返回成功的状态码。其中,默认的this上下文是Ajax执行的完整参数们,是个对象。结构类似本小节展示的defaults对象。
  • 错误回调也支持两个参数,error(xhr, status), 其中xhr是发送的请求对象,status跟上面一样意思,就不多说了。其中,默认的this上下文是Ajax执行的完整参数们,是个对象。与其他两个回调有一个很大的不同,多了个message属性,告知了错误原因,如,网络掉线、超时或是是JSON解析异常等。
  • 完成回调也支持两个参数,complete(xhr, status), 其中xhr是发送的请求对象,status跟上面一样意思,就不多说了。其中,默认的this上下文是Ajax执行的完整参数们。

字符串类型返回值
默认返回的是字符串,会按照HTML字符串处理。

例如,我们请求ajax.html页面,该页面最好是完整的body > page结构。因为,就算页面JS挂掉,无法阻止默认链接行为,发生跳转,也不会影响可用性。但是,如果请求的是个动态页面,直接返回的是干净的HTML代码,如果没有page元素,则不能含有html以及body标签。

因为Mobilebone会寻找返回HTML中的page元素作为页面载入;如果没有,则会将返回的所有HTML封装在自己创建的page中。简言之,要么返回“完整HTML页面代码”, 要么返回“干净的HTML片段代码”。

返回页面的title
如果是返回完整网页标签结果,则会使用<title>标签里面的文字作为请求页面的title;如果是返回HTML片段,抱歉,你只能在点击的元素上通过data-title属性设置。

2. JSON类型的请求
由于某些团队的中间层还没成熟,前后端半分离状态,导致请求得到的数据只能是JSON数据。虽然个人建议是后台那边使用某些框架基直接吐HTML返回,但现实是骨感的。不过不要太多担心,Mobilebone是有考虑过这种情况的,其暴露了一个方法名为Mobilebone.jsonHandle(json), 专门用来处理JSON数据源,需要返回渲染的HTML视图代码或者直接就是page元素。

支持一个参数json, 此参数必须,为Ajax请求返回的JSON数据。于是,你就可以在此处理方法中套用模板,吐出页面完整HTML. Mobilebone会自动根据吐出的内容生成页面,并以过场动画形式载入。

需要注意的是,Mobilebone.jsonHandle是个全局的唯一的方法,所以,如果页面有多个JSON渲染,请使用返回的JSON数据的id或其他标志量做区分,精准返回HTML数据(也可以自己返回页面-不多见)(可参考测试页面中Backbone的例子)。

下面是JSON测试页面的例子代码:

Mobilebone.jsonHandle = function(json) {
    var page = document.createElement("div");
    page.className = "page out";
    page.setAttribute("data-title", json.title);
    page.innerHTML = json.html;
    return page;
};

简单示意,不要太认真。若有兴趣,可以轻戳这里访问体验下。

更好的JSON包括HTML加载建议
//zxx: 很多人这里有误解:并不是推荐大家直接载入HTML页面,而是可以尝试不依赖Mobilebone你自己的方法装载数据。

虽然Mobilebone提供了直接请求JSON数据的方法,并提供了视图渲染接口。但是,以我个人经验,对于实际开发,这种实现策略是不推荐的。我认为更好的实现方法应该是这样的。页面骨架,也就是page主体,也就是一个空div默认就载入,然后所有的切换都是基本切换,而不是Ajax切换。在切换即将开始的时候(回调),您就可以使用自己,例如Zepto的Ajax方法去请求你需要的JSON数据,做你任何想做的事情,完全没有Mobilebone的限制。

页面slide是有时间的,350ms, 这个时间点很可就就完成呈现了最终的页面。于是,我们看到的就是,一点击,页面slide, slide结束,内容呈现。哇哦哦~~操作感不要太流畅哦!而且技术上更可控,因为数据请求、处理与Mobilebone完全解耦。

当然,如果就是个原型页面、简单的静态页面,或者是不喜JS的小伙伴,依赖Mobilebone的Ajax整体请求与呈现策略显然是最好的选择,因为,你什么都不需要做~

补充于2017-07-06
上面提到的“更好的JSON和HTML加载建议”现在google提出了一个专门的名词,叫做“App Shell 模型”,其思路都是一致的,如果无法访问可以查看这里的备份地址

其核心思想都是:核心应用基础架构和 UI 从数据中分离出来。请务必使初始加载尽可能简单,在打开网络应用后仅显示页面的布局。

3. 请求页面的缓存机制
默认情况下,Ajax请求的页面,如果之前已经请求并载入,下次请求时候,就会启用基本切换,也就是直接使用之前的page过场,而不是再次发起Ajax请求。这就是Mobilebone请求页面的缓存机制。但是,实际开发时候,有些页面数据是需要实时更新,不能被缓存的。此时怎么破?很简单,使用data-reload="true"即可(="true"可缺省)!例如下面代码演示:

<a href="ajax.html" data-reload>

于是,请求的页面就不会被缓存了,而是不断的新旧替换。

对了,Mobilebone中的所有Ajax请求都加了时间戳,也就是只要有请求发生,基本上都不会使用浏览器缓存。

更新于2014-01-26
v2.3.2+ 新增data-reload="root", 表示相对“url根地址重新加载”。于是data-reload就有了两种不同的使用场景:

  1. url完整地址重加载data-reload/data-reload="true".
  2. url根地址重新加载data-reload="root" (v2.3.2+)

分别表示什么意思呢?

url完整地址重加载

如下两个请求:

<a href="detail.php?id=112" data-reload="true">请求详情页</a>
<a href="detail.php?id=113" data-reload="true">请求详情页</a>

可以看到查询id是不一样的。所谓“完整地址重加载”是指,只有在第二次请求的页面url(包括查询字符串)完全匹配的时候,才移除缓存,重新加载!

比方说上面的两个请求,最后页面HTML会有两个独立的页面。

url根地址重加载

请求地址还是一样,但data-reload值为root:

<a href="detail.php?id=112" data-reload="root">请求详情页</a>
<a href="detail.php?id=113" data-reload="root">请求详情页</a>

这里的就是“根地址重加载”,指只要Ajax请求的url的根地址是一样的,就不会缓存,直接清除之前同源页面。

所以,这里,页面上永远最多就一个详情页对应HTML. 很多小伙伴喜欢使用全局id绑定事件,此时,就务必需要设置data-reload="root", 以免id冲突,事件重复绑定等问题。

更新于2014-04-13
v2.4.4+ 移除了data-reload="root",使用了更智能的缓存更新机制,所以上面2014-01-26的更新作废,向前兼容,无需修改。

4. Ajax加载的loading效果
Ajax是个需要等待响应的过程,尤其网络较差的情况,比如高峰时段的地铁。Mobilebone自带loading效果。

默认情况的loading效果为,35%白色半透明全屏遮罩,中间是个斑斓的菊花旋动效果,如下截图:
默认的loading效果

此效果对于90%的移动开发,以及部分的PC页面是适用的。但是,应用场景千千万,有时候,我们loading可以希望出现在局部,例如,我们点击的按钮、或导航上。此时该怎么办?对此,Mobilebone也留了一手。很简单,使用data-mask="true"(="true"可缺省)就可以了。如下所示:

<a href="ajax.html" data-mask>

于是,当我们点击这个链接时候,loading相关的HTML就会显示在这个a元素中,通过简单的CSS控制,就能实现我们需要的自定义loading效果了,例如,只覆盖按钮,或者菊花在文字后面显示。如下面两截图效果:

按钮覆盖loading

loading跟随按钮文字效果

若有兴趣,可以轻戳这里访问体验下。

对了,插一句:Mobilebone已经对Ajax连续点击可能会重复请求的问题作了处理,大家无需担心额外的请求损耗。

5. 避免Ajax加载,使用传统刷新
Mobilebone默认会对同域的地址做Ajax请求无刷新加载。但是,万一人家就是希望要请求呢?以及,虽然跨域,但是人家依然希望使用Ajax请求了(如跨子域而已)。

轮到data-ajax属性出场了。如下代码:

<a href="ajax.html" data-ajax="false">  // 此情况下也可使用data-rel="external"

于是,点击上面<a>元素时候,就会是浏览器的刷新跳转。

如果人家要页面上所有的链接,或者大部分都是跳转,总不能一个一个设置data-ajax="false"吧,哈,Mobilebone提供了一个全局参数,Mobilebone.captureLink,只要设置成false布尔值,页面所有链接诶都是传统可刷新跳转链接。

Mobilebone.captureLink = false;

例如,测试引导首页就是这么设置的。

事情还没有结束。Mobilebone自带域名判断技能,如果跨域,默认会认为是刷新链接。但是,XMLHttpRequest 2.0支持Ajax跨域。例如a.qq.com下的页面请求b.qq.com, 此时需要按照Ajax请求来走,怎么办?还是data-ajax, 这回值设置成"true"就可以了。

<a href="//b.qq.com/ajax.html" data-ajax="true">

六、mobilebone.js与过场回调方法

所谓“过场回调”,就是从牛A页面切换到牛C页面时候,触发的一些回调函数。

Mobilebone提供了多个回调接口,以应对各种交互需求。有如下四个:

  • onpagefirstinto(pageInto, pageOut, response → options)
    当过场页面第一次进入的时候执行,一般用在事件绑定,或实现元素动态显示。支持三个参数:

    pageInto
    进入的page元素。这个参数一定会存在的。
    pageOut
    离开的page元素。这个参数可能为null, 如页面刷新时候。
    response
    返回的数据。这个参数多半应用在Ajax请求时候。
    options(v2.1.0+)
    之前的参数response重置为options, options{}对象,根据应用场景不同,options属性也有所不同。通常常用的参数有:

    • options.response 返回的数据。这个参数多半应用在Ajax请求时候。
    • options.target (触发过场)点击的页面元素。
    • options.id 页面标示id, 一般仅在url请求时候存在。
    • options.history 是否在浏览历史中添加一条记录。此属性大可不必关心。
    • options.remove 是否删除同id的页面。此属性大可不必关心。
  • callback(pageInto, pageOut, response → options)
    每次过场页面进入的时候都会执行。参数与onpagefirstinto含义一致,不赘述。
  • fallback(pageInto, pageOut, response → options)
    每次过场页面退出的时候都会执行, v.1.1.4+新增。参数与onpagefirstinto含义一致,不赘述。
  • animationstart(page, into_or_out, options)
    过场动画开始的时候执行。离开的页面和进入的页面都会触发。三个参数:

    page
    当前动画的page元素。
    into_or_out
    字符串。只可能下面两个值之一:"into", "out"
    options
    v2.1.0新增,包含一些参数,方便回调处理。含义与onpagefirstinto一致,不赘述。
  • animationend(page, into_or_out, options)
    过场动画结束的时候执行。离开的页面和进入的页面都会触发。参数与animationstart含义一致,不赘述。

重要:补充于2015-05-30(v2.5.8)
尤其当使用data-callbackdata-fallback处理一些回调的时候,有可能前后两个页面结构一致,数据不一样,就很有可能出现id一致的情况,如果单纯使用$("#ID")document.querySelector("#ID"), 则得到的元素并不是新页面你希望选择的元素。怎么办,请使用回调方法中的页面参数作为容器去获取对应的元素,例如:

Mobilebone.callback = function(pagein) {
    // NOT: var element = document.querySelector("#ID");
    var element = pagein.querySelector("#ID");
    // do sth by using elememt...
};

下面问题来了,如何绑定这些回调方法?

OK,跟Ajax参数绑定类似,使用自定义属性。同样是两种模式:data-*data-params.

例如下面这个data-*模式:

<div id="pageHome" class="page out" data-onpagefirstinto="home" data-animationstart="start">

或者下面这个data-params模式:

<div id="page1" class="page out" data-params="animationstart=start&animationend=end">

模式不是重点,重点是函数名关键字。例如data-onpagefirstinto="home"表示什么意思呢?

有些类似Ajax的回调,但有差别。在默认没有任何设置的情况下,Mobilebone会认为home是个全局函数的名字。于是,会使用类似window.home()的形式执行该回调方法。但是,显然,JS中应该是要尽量避免不必要的全局变量的,对于实际项目而言,都是全局方法是不实际的。于是,Mobilebone暴露了一个可以修改回调主对象的接口,Mobilebone.rootTransition. 例如,你的页面有如下对象:

FUN = {
    home: function(pageInto, pageOut, response) {},
    start: function(page, into_or_out) {},
    end: function(page, into_or_out) {}
};

则,你就可以设置:

Mobilebone.rootTransition = FUN;

“哎呀,我不想修改全局,只想局部开花”,OK,没问题,使用root关键字修改根对象。例如:

<div id="page1" class="page out" data-root="window">

哈,page1的回调函数又变成window对象下面的啦~ 同样,支持data-params查询字符串模式。另外,data-root也支持对象级联,可以帮你获取层级较深的方法。

关于过场回调,如有兴趣,可轻戳这里浏览观摩体验。

直接全局设置
有些回调,每个页面发生过场的时候都会执行,我总不会每个页面都加一个data-callback吧,又累又啰嗦。此时,你可以使用全局回调设置。直接:

Mobilebone.callback = function() {};

每次有页面进入都会执行,此实例可参考头尾固定的那个例子

如果发生冲突,也就是data-callback也存在,则直接覆盖全局的Mobilebone.callback方法。

七、mobilebone.js与使用其他过场动画

mobilebone.css默认只提供了一种过场效果,就是左右slide效果。

如果你希望有更多的过渡效果。试试外链一个animate.css, 此CSS位置位于Github项目的test/transition/animate.css, 包含fade, slideup, slidedown, turn, flow等多个过场动画效果,然后,添加特定属性,就可以实现我们的效果了,很easy!

<link rel="stylesheet" href="http://rawgit.com/zhangxinxu/mobilebone/master/src/mobilebone.css">

于是,我们在page元素上,使用data-form="xxx"data-params="form=xxx"指定特定的过场动效。例如,fade淡入淡出效果:

<div id="page1" class="page out" data-form="fade">

八、mobilebone.js与模块化加载

mobilebone.js可以符合AMD, CMD规范的加载器加载。例如seajs, 或requirejs.

用法其实很简单的:

var Mobilebone = require('mobilebone');

然后,用法基本上就跟平常时候一样,除了需要手动初始化一下:

// Mobilebone API设置...然后...
Mobilebone.init();

若有兴趣,可参考test/modular-load中各个文件的源代码示意(index.html使用的是seajs, require.html使用的是require.js)。这里要看代码,效果没啥看头。

九、mobilebone.js与地址栏前进、后退与刷新

Mobilebone强大的另外一个体现就是利用HTML5 history API和地址栏融为了一体。

地址栏的前进、后退或者刷新都跟传统网页一样,可以准确显示对应内容。因此,无论是Web APP或者Hybird APPHybrid app,手机上的返回键都能很好地操控我们的内容呈现,就跟Native APP感受一样(文章开始的视频应该有所体现)。

原理
早些年时候,我直接使用hash做路由指向,后来发现经常会干扰定位。原因可参考我之前的文章:“URL锚点HTML定位技术机制、应用与问题”。

这里有必要再次感谢下jQuery Mobile. Mobilebone的history处理与jQuery Mobile一样,路由地址前面加了一个&符号,从而解决了锚点定位的问题。

当然,从使用者的角度讲,这些你都不需要关心。

十、mobilebone.js与插件扩展

mobilebone.js核心就是切换,其也只做了这一件事情。正是因为这种简单与专一,方能体现其强大。如果你想有更丰富强大的功能,你可以很自如地进行扩展。

前面的animate.css过场动效就是不错的扩展。当然,不知CSS,JS层面的扩展更具有潜力。

举个例子,你希望swipe时候,页面也有过渡效果,OK,你参考API文档扩展下就可以了,或者添加键盘控制,实现类似在线幻灯片浏览的效果,也是可以的。

plugins/ppt文件夹下面,就有我写的一个在线幻灯片演示插件,做的事情其实很简单,单击非链接区域,上下左右键盘,以及鼠标滚动,会让幻灯片前后播放。

您可以在PC或者pad上访问该演示页面

当然,如果你有其他idea, 也能做出其他很精彩的作品。

十一、mobilebone.js其他tips说明

1. tap/click事件依赖
该Tips还是蛮重要的!所谓事件依赖,就是,如果浏览器大环境支持tap事件(比如使用了含touch events的Zepto.js),则使用tap事件来触发一系列的过场行为,否则就使用click事件。然而,大家可能都知道的,在移动设备上,click具有较长时间的延迟,用户在操作的时候总会有点怪怪的不顺畅的感觉。怎么办?在这里,我必须郑重推荐下fastclick.js. 很多小伙伴都在用它,Github上面的star要奔万的节奏去了,https://github.com/ftlabs/fastclick.

直接引入fastclick.js,然后如下代码绑定:

FastClick.attach(document.body);

然后我们就能愉快地在移动设备上玩耍啦!此js对Mobilebone很友好,有种千年好基友的感觉。在test/complex演示的模拟微信交互页面上就使用了fastclick.js.

2. 一些原型扩展方法

  1. 给元素扩展了一个getParentElementByTag方法,根据标签寻找匹配的父元素,没有返回null.
  2. 给字符串扩展了个queryToObject方法,可以把查询字符串转换成对象。 此方法在v1.1.3+中私有

3. 已知问题

  1. 一些老三星手机上,会出现第一次animation页面比例突然变小再恢复的诡异问题。希望有经验的朋友赐教!

4. 其他杂七杂八tips

  1. 在Github上查看mobilebone.js时候,最好在后面加上?ts=4会以最佳排版效果显示。
    https://github.com/zhangxinxu/mobilebone/blob/master/src/mobilebone.js?ts=4
  2. Github上mobilebone.js以及mobilebone.css都是非压缩版本(因为我比较懒),实际上线,需要大家自行压缩。我测试了下,现在写这段文字的此时此刻最新的JS版本压缩后只有8.69K, Gzip后真的只有3~4K,跟着图标大小差不多。

十二、mobilebone.js API文档

见下面表格,颜色淡的就是表示不常用的,正常颜色的是可以关注的:

API名称 类型 默认值 示例 吐槽
Mobilebone.support 布尔值 是否兼容Mobilebone, 只读,亲,只读
Mobilebone.VERSION 字符串 当前mobilebone.js的版本号,只读,注意了,只读
Mobilebone.autoInit v1.1.6+此API删除 布尔值 true Mobilebone.autoInit = true 是否DOM加载完毕后自动初始化,默认为true. 如果页面加载完毕之后的require加载,此值失效,按false处理。
Mobilebone.captureLink 布尔值 true Mobilebone.captureLink = true 是否捕获页面上的a标签,执行无刷新过场效果。此为全局设置,影响整个页面。默认为true
Mobilebone.captureForm(v2.0.0+) 布尔值 true Mobilebone.captureForm = true 是否捕获页面上的form表单元素的默认submit提交事件,执行无刷新提交以及过场效果。此为全局设置,影响整个页面。默认为true
Mobilebone.rootTransition 对象 window Mobilebone.rootTransition = window 过场回调方法的根对象。默认是全局window.
Mobilebone.mergeCallback(v1.1.4+) 布尔值 true Mobilebone.mergeCallback= true 全局的回调方法和每个页面上自定义的回调方法是合并还是覆盖。默认true表示合并。
Mobilebone.classAnimation(v2.1.1+) 字符串 "slide" Mobilebone.classAnimation = "flip" 过场动画相关联的类名
Mobilebone.classPage 字符串 "page" Mobilebone.classPage = "page" page元素的标志类名
Mobilebone.classMask 字符串 "mask" Mobilebone.classMask = "mask" mask元素的标志类名
Mobilebone.pushStateEnabled 布尔值 true Mobilebone.pushStateEnabled = true; 是否启用历史记录。此参数我是没有想到需要使用的理由,但总感觉可能用到,于是就放着。
Mobilebone.evalScript(v2.5.0+) 布尔值 false Mobilebone.evalScript = true; 是否执行Ajax请求的HTML字符串里面的内联JS脚本,默认不执行。
API名称 类型 返回类型 参数 示例 吐槽
Mobilebone.transition(pageInto, pageOut, back, options)

或者

Mobilebone.transition(pageInto, pageOut, options)

函数 pageInto

DOM元素。表示进入的page元素. 必须参数。

pageOut

DOM元素。表示要移出的page元素. 可选。没有移出元素使用null.

back

布尔值。是否反方向过场。可缺省。

options

对象。此参数多内部使用。一般有用的是两个参数接口:idresponse.
Mobilebone.transition(element);
Mobilebone.transition(element1, element2);
Mobilebone.transition(element1, element2, true);
Mobilebone.transition(element1, element2, { id: “only” });
Mobilebone.transition(element1, element2, true, { id: “only” });
此API在插件扩展的时候应该是最常用的。此方法为Mobilebone切换的核心。含缓存机制、事件回调触发等。
Mobilebone.getCleanUrl(trigger, url, params) 函数 字符串 trigger

DOM元素。一般表示<a>元素。可选参数,和url至少一个存在。

url

字符串。Ajax请求地址。如果trigger有合法href值,此参数酱油。可选参数,和trigger至少有一个有效。

params

对象或者字符串。主要用来获得请求查询数据.可选。
Mobilebone.getCleanUrl(elementOfA);
Mobilebone.getCleanUrl(elementOfA, ”, “a=1&b=2”);
Mobilebone.getCleanUrl(null, “xxx.html”);
Mobilebone.getCleanUrl(null, “xxx.html?a=1&b=2”);
Mobilebone.getCleanUrl(null, “xxx.html”, “a=1&b=2”);
获得干净完整的Ajax请求地址。基本上,此函数API内部用得多,大家大可不必关心。
Mobilebone.getPage(children) v1.1.6+此API删除 函数 DOM元素
或null
children

DOM元素。必须参数。
Mobilebone.getCleanUrl(childElement) 根据子元素获取当前所在的page元素。
Mobilebone.createPage(dom_or_html, element_or_options, options) 函数 dom_or_html

DOM元素或字符串。必须参数。

element_or_options

DOM元素或者对象。可选参数,作用是获知需要移除的页面, 作用是获取自定义的一些属性,例如data-title等。可以是<a>元素,page元素,也可以是第3个options参数。

options

键值序列对象。可选参数。此参数多内部触发,基本上来自Ajax请求的参数。主要作用是获得response返回数据。
Mobilebone.createPage(pageDom);
Mobilebone.createPage(generalDom);
Mobilebone.createPage(‘<div class=”page out”>xxx</div>’);
Mobilebone.createPage(‘<p>xxx</p>’);
Mobilebone.createPage(pageDom, triggerLink);
Mobilebone.createPage(pageDom, { reponse: ‘<div…>’ });
Mobilebone.createPage(pageDom, triggerLink, { reponse: ‘<div…>’ });
重要API. 直接根据DOM或者HTML字符串创建页面,并载入。别看API名字较长,好像很复杂,其实很简单滴。
Mobilebone.getFunction(keys) 函数 对象

函数
keys

字符串。必须参数。当字符串级联的时候,例如’a.b.c’, 用来返回纯正的window.a.b.c这个对象,才能执行与调用。
Mobilebone.getFunction(“a.b.c”) 此API内用,大家很少会用到,不要太关心。
Mobilebone.ajax(trigger_or_options) 函数 trigger_or_options

<a>元素或者Ajax请求参数对象。必须参数。
Mobilebone.ajax(document.querySelector(“a”));
Mobilebone.ajax({
  url: ‘xxx.html’,
  success: function() {}
});
很重要,应该会比较多用到的API,大家需要留意。
Mobilebone.submit(formElement) (v2.0.0+) 函数 formElement

<form>表单元素。必须参数。
Mobilebone.ajax(document.querySelector(“form”)); 可以让页面的表单的默认提交事件变成Mobilebone下的Ajax提交事件,会把返回内容变成新页面并执行过场。
Mobilebone.isBack(page_in, page_out) 函数 布尔值 page_in

元素。必须参数。进入的元素。

page_out

元素。可选参数。离开的元素。
此API没什么机会使用的,内用居多,不必太在意。
Mobilebone.jsonHandle(json) 函数 DOM元素

HTML字符串
json

JSON数据。必须参数。
此方法JSON请求必用。全局方法,因此,如果存在多个JSON需要处理的情况,请使用JSON数据中特定的标志量进行区分,例如,返回个id. 如:

{
  "id": "homePage" ,
  "data": []  
}

和这个:

{
  "id": "listPage" ,
  "data": []  
}
Mobilebone.init(); 函数 初始化方法。默认DOM载入完毕会执行,无需关心。如果Mobilebone.autoInitfalse, 或此方法在页面load完毕后动态载入,则需要手动初始化。
Mobilebone.handleTapEvent(event) 函数 起初我只是为了排版好看才将此方法暴露出来的。后来发现,某些场景还是可以用到的。比方说你做了某些操作,直接把click给永久消灭了,然后使用自己的自定义tap方法,此时就可以类似这样处理:

document.body.addEventListener('myCustomTapEvent', Mobilebone.handleTapEvent, false);

来绑定过场效果。

十三、mobilebone.js结语与展望

一分耕耘一分收获,终于赶在月底前把这篇文章发布了,扳一扳手指头,快40天去了,鲜有项目做这么长时间。来鹅厂目前最大的收获之一就是做产品的态度,一定要花足够的精力去精雕细琢,kill每一个痛点,完善每一个体验。虽然过程很辛苦,但是做出来的东西大家会都喜欢。看上去好像谁到知道,做产品要用心,但事非经过不知难,一定要亲历与感悟,才能真正成为自己所得之物。

虽然上面调侃多次“说不定回火”,但自己实际并没有真在意。我并不确定有多少人会使用mobilebone.js, 但是,唯一我确定的是,心有多大,舞台就有多大,如果没有制作精品的态度,没有完成世界top级作品的胸怀,没有用心的反复雕琢与实践,一定是不会火的,最多就是发布时候众人捧个场、鼓个掌,然后,就没有然后了,就像千千万万昙花一现的小企业一般。

所以,细心的你可能发现,mobilebone.js中的注释都是英文的,且API用法,参数,示例都放上面了;提示信息也是蹩脚英文,ReadMe.md也有英文介绍。当下,虽举目三尺皆白壁,但心在壁外三万里。用人话表示就是,虽然我身处一个小屋子,但我希望我做的东西能够漂洋过海得到肯定。因此,花了很多额外的功夫做了些国际化的工作,万一哪个老外慕名前来,也不会因为看不懂中文文档而放弃。

谋事在人成事在天,做好自己能够最好的一切,期待理想之花静静绽放。恩,至少,我自己用起来是很顺手的!

希望大家多多支持,共同建设,提出问题或提供建议!

回头,如果大家关注度不错,我会申请个相关域名,把此项目独立出去……一切才刚刚开始……

十四、重要更新说明

1. 更新于2014-12-30
首先从v2.0.0开始对表单提交过场进行了支持。然后,注意然后,有一个重大的介入,就是即将发布的v2.2.0版本加入了Mobilebone中介阻断介入机制,也就是可以让你在Mobilebone执行内置的行为之前可以让你做很多其他的事情,你可以决定是继续还是阻断,执行其他行为。

这就是data-preventDefault, 我一般写做data-preventdefault(属性名不区分大小写)。

其阻止(或介入)Mobilebone的默认行为包括,页面过场;a链接交互;表单提交。

链接交互

例如,页面上有如下HTML:

<a href="form.html">点击加载一个表单</a>

如果没有其他设置,Mobilebone会自动请求form.html这个页面并过场载入。但是,往往,我们可能需要先判断用户是否已经登录了,此时,就需要data-preventdefault(不区分大小写)的介入。如:

<a href="form.html" data-preventdefault="isLoginout">点击加载一个表单</a>

于是,我们就可以在isLogin这个方法中做阻断判断了:

var isLoginout = function(target) {
    // 支持一个参数target, 指的就是对应的a元素
    // 如果没有登录,返回true
    if (isLogin == false) return true;    
};

从上面代码可以看到,当data-preventdefault中介函数返回值为true的时候,就会中断Mobilebone的默认行为,你就可以做一些你想做的其他事情,例如,先弹出个登录弹框。

表单提交前的验证

data-preventdefault中介函数还有一个很重要的应用就是表单验证。默认情况下,表单点击提交按钮,就会走原生验证,然后Ajax过场。但是,实际开发,验证是交给自己,而不是浏览器,于是,在提交之前,我们有必要验证下,此时,就得靠data-preventdefault.

写在form标签上,如下:

<form method="get" action="search.html" data-preventDefault="validate_false" novalidate></form>

由于data-preventdefault中介函数返回true时候才中断,因此,对应的函数关键字应该是“验证不通过”的意思,例如这里的validate_false.

var validate_false = function(form) {
    // 支持一个参数form, 指的就是表单元素
    // 如果文本框没有值
    if (input.value.trim() == "") {
        // 显示错误提示
        // xxxx.show();
        // 中断Mobilebone行为
        return true;    
    }
};

页面过场(进入/离开)

page页面元素上也可以使用此中介阻断API,其绑定以及函数参数与callback/fallback完全一致。

例如,我们Ajax请求页面的是一个弹框页面,此时,当前页面是不能离开舞台的,就可以使用data-preventdefault中断。这个以后有机会再放置实例。

或者实现页面跳转效果等。

2. 更新于2015-01-11
新增2个自定义属性:data-containerdata-classpage.

都是作用在链接a元素上的,其中data-container值指的是容器元素的id, data-classpage为类名。

① 当只有data-container这个属性时候,Ajax请求装载的页面会append到此属性值对应的容器元素中;
② 当data-containerdata-classpage同时存在且合法的时候,可以实现内部过场(局部过场)效果。也就是,不是整个大的page滑来滑去,而是page里面小页面间的滑来滑去。存在这样的需求,载入一个页面,这个页面有固定头尾,然后也页面里面的content要滑来滑去。虽然之前的API也能实现类似效果,但是需要写回调之类控制,有些折腾,而有了data-containerdata-classpage,事情就简单多了,指定容器,指定切换页面的关键类名,一切都结束了!具体可参见API文档中data-classPage的示意

API文档示例截图

data-containerdata-classpage触发的过场效果是不会有history变化的,其他规则跟主page切换一致,包括各种回调接口的使用等等。

(本篇完)

分享到:


发表评论(目前291 条评论)

  1. justok说道:

    博主你好,很感谢你的辛勤工作。我很喜欢这个小快灵的框架,我也想在新的app项目中用到它。在这里想提一个小 建议,就是垃圾回收的问题。我之前用sencha做项目,是个单页app.遇到的一个问题是html过于臃肿,生成多个页面后,造成webview变得卡顿。我们的解决办法是,在点击当前页后退按钮的时候,删除当前页,重置变量值。当然,很多页面不适合后退就删除,我们会添加一个标识,具有特殊标示的页面不会后退删除。这样就解决了打开很多页面后,单页app中存在过多html造成卡顿的问题。
    我在想在mobilebone中是不是也可以这样添加一个功能,解决单页app中打开页面过多的情况,有效提高运行速度。这样,在单页app中的3个重要问题就都能够在mobiebone的框架下解决,不是很美妙?动画切换,路由,垃圾回收。不知道博主怎么看,期待和你的交流

  2. 刘逸舟说道:

    非常强大,前端新人mobilebone学习中。。。这个月发了工资就捐赠点哈哈

  3. DownUp说道:

    楼主 遇到个问题
    TypeError: target.getParentElementByTag is not a function

    …est(target.tagName) == false) && (target = target.getParentElementByTag(“a”)))

    请问是怎么回事?

    • 张 鑫旭说道:

      @DownUp 一般出现这种情况,可能由于浏览器不支持DOM3.0, 可否反馈具体使用场景。

      • DownUp说道:

        我试了下 你的demo 也有这个问题,在微信综合实例中, 是不是和isroll有关

        在火狐浏览器中,往上拖动页面 ,在firebug中 ,就可以看到这个问题

        我的场景是 页面拖到最后,有个加载更多,点击后追加html,然后往上拖动,就出现这个错误的

      • DownUp说道:

        断点调试 这里时 target = target.getParentElementByTag(“a”)

        target 为 Document #&pageHome
        target.getParentElementByTag(“a”) 为 TypeError: target.getParentElementByTag is not a function

  4. 燕不器说道:

    楼主,我是刚入门HTML5前端设计,正在学习使用mobilebone,想开发一个移动端小产品。有个问题请教,如何通过上下划屏实现页面切换?

  5. 简简单单说道:

    页面切换后Javascritp不能执行

    Main页面
    A页面

    从Main页面点击链接到A后 A页面的Javascript不能执行 A的Js在Main里也有

  6. 逆539风说道:

    楼主,我在hybrid app中使用mobilebone。因为页面是存在本地的,不需要通过ajax请求。android机器上webview测试没有问题,但是ios机器上webview加载是没有执行,但是我通过Safari去从服务器加载我的过场动画包括回调函数都执行了,请问楼主有没有遇到过这样的问题?

  7. Yowi说道:

    真希望有一天能够 href=”#test?id=142030459&name=mobilebone” 执行…. (刷新和复制链接不会丢参数:)

  8. DownUp说道:

    卤煮,例如http://localhost:8011/test/index#&/test/indexs?aid=1090 这样的地址,复制黏贴到新的选项卡, 是不是打不开,

    else if (isSimple.test(hash) == true && (ele_in = document.querySelector(hash)) && ele_in.classList.contains(this.classPage)) { // ‘ele_in’ must be a page elemen

    这句代码 报SyntaxError: An invalid or illegal string was specified

    • 张 鑫旭说道:

      @DownUp 你好,请问是哪个版本,我测试了下,并没有报错~

      • DownUp说道:

        2.4.3 我试了下2.5.0 是可以的

        在2.4.3版本我是 把 else if (isSimple.test(hash) == true && (ele_in = document.querySelector(hash)) && ele_in.classList.contains(this.classPage)) { // ‘ele_in’ must be a page element
        this.transition(ele_in);
        } 注释了

        请问下是什么问题

        是不是在判断/test/indexs?aid=1090是否为page element出错,我注释后 以后面的ajax方式访问

        • 张 鑫旭说道:

          @DownUp 以前使用的是jQuery的简单选择器,发现忽略的一些特殊字符会使 document.querySelector 报错, 2.4.4+自己写了个简单选择器,做严格判断。

      • DownUp说道:

        还有个问题 列表页为头尾固定,内容页有个头,,,两个页面的头部颜色是黑色,

        在列表页点击跳转内容页的时候,列表页头部后变灰,然后过渡到列表页,,,,,请问列表页头部的颜色变是不是我设置的问题

        能不能列表页的头部颜色不变过渡到内容页

  9. diandian说道:

    问题1:

    1、关于示例,
    <li><a href="root-reload.php?id=1">根地址不缓存1</a></li>
    <li><a href="root-reload.php?id=2">根地址不缓存2</a></li>
    实际情况大多在root-reload.php页面需要根据id,发出ajax请求数据,再组装渲染页面,处理起来就不好办了!很少直接href请求json数据,首先这不能支持浏览器刷新

    2、重复切换点击
    <li><a href="root-reload.php?id=1">根地址不缓存1</a></li>
    <li><a href="root-reload.php?id=2">根地址不缓存2</a></li>

    问题来了,此时只是切换,如何做到切换ajax根据不同id,来刷新页面!

    问题2:
    Ajax请求页面的是一个弹框页面,此时,当前页面是不能离开舞台的,就可以使用data-preventdefault中断。这个以后有机会再放置实例。这个怎么用?

    问题3:
    目前对于前后台通过数据对接的方式很不友好,比如浏览器访问直接页面,通过接口请求json数据,前台对数据处理渲染出页面。对应到mobilebone,就是ajax请求一个结构页面,该页面实际需要发起一个异步请求得到json数据,再根据数据渲染出完整页面。目前这种情况处理起来很麻烦!即便开放Mobilebone.jsonHandle(json)也是不可取的,总不能通过浏览器刷新直接访问接口!个人觉得实际开发中,接口方式方式多,比较少前台请求后台直接抛出结构完整页面,可以考虑支持前台模板加载,异步请求数据再渲染页面方式!

    • 张 鑫旭说道:

      @diandian

      问题1:
      Ajax切换的id就是url根地址。

      问题2:
      使用原理大致如下:data-preventdefault回调中负责显示弹框页面,最后加一个return true;就可以了。

      问题3:
      你好,如果你觉得Mobilebone对JSON的处理麻烦,可以使用你自己的一套方法处理,Mobilebone是支持,我在文中也是有建议的。
      “浏览器刷新直接访问接口”是什么意思,这个问题可能不在一个频道上,接口访问不需要刷新。
      你说的模板渲染是支持的。文中多次提到,测试页面有示例。

      • diandian说道:

        对于问题3,如你说“页面骨架,也就是page主体,也就是一个空div默认就载入,然后所有的切换都是基本切换,而不是Ajax切换。在切换即将开始的时候(回调),您就可以使用自己,例如Zepto的Ajax方法去请求你需要的JSON数据,做你任何想做的事情,完全没有Mobilebone的限制。”,其他方法似乎都不太可行,如果通过添加click或tap触发ajax获取json,再渲染回填进页面结构中,这样如何做到刷新页面时仍然能取得一个完整的渲染页面。这种情况脱离了Mobilebone,直接刷新浏览器,页面json数据来源不能确定!

        对于问题1:你想象的情况太单纯了,认为后台总是返回一个动态解析好的html结构,其实不然,多数情况都是接口,一个页面从多个接口一起完成最后显示,此时的页面基本可以作为静态结构页面或者就是一个空架子,左右上下等区块结构,不同请求json回来后再渲染成dom,回填进去做显示,这就是常见的一些页面显示的时候,结构是完整的,中间某些区域还在loading中。

        关键一点问题3如果处理不好,嵌入微信中,根据你的做法会由于地址不能正确显示页面,分享朋友圈啥的功能都无法正常使用。

        对于ajax请求页面中的事件处理,如果不能在请求页面中写(绑定),而通过回调,或者写在骨架页,是不是不太合理?ajax请求页面一多,如何对事件做管理是个问题,总不能没有请求前把事件全部写在骨架页吧。相应页面,相关事件处理,类似于jquery中load加载页面结构块,也是可也对应执行srcipt脚本的。

        很明显,这种自己数据请求、处理与Mobilebone完全解耦的方式,数据请求通常也是一个接受参数的动态接口,不太可能都是一个固定的接口地址。

        • 张 鑫旭说道:

          @diandian 你提的问题一个都不是问题,我就不解答了。我们可以从宏观从哲学的角度分析,Mobilebone本质是个UI层的东西,做的事情就是过场,正如其名-bone就是个骨架;至于你如何事件管理,HTML各个模块如何载入,微信分享,script脚本位置,或者哪怕10个动态请求,其实跟Mobilebone没有关系,Mobilebone几乎没有做任何限制。回想下你平时开发页面是如何处理的,Mobilebone只是将你的刷新那一步,变成了无刷新。其他都是你控制的,如有细节疑问,欢迎前往 https://github.com/zhangxinxu/mobilebone/issues 讨论~

        • diandian说道:

          平时开发页面,通常在一个页面内部对应发起多个接口请求,根据不同接口返回数据再组装相应dom,最后进行页面回填!

          直接问题在于借用Mobilebone ajax请求的页面,不能执行页面里面的接口请求,意味着页面中做不了任何事情,只是一个固定抛出的html。

          对于一个页面,多个接口,Mobilebone如何做加载,如果只是把页面加载回来,里面的那些接口请求怎么办?虽说可以在Mobilebone的回调中再做接口请求处理,但是如果要同时兼顾到url刷新,要知道接口通常不是死的,也会需要接受一定参数,什么方式处理最优,会有很多挖掘的问题,真正项目中推广使用起来目前还是有很多局限性!

  10. xuetian说道:

    一个bug,具体操作,(默认首页中设置<a href="detail.php?id=112">XXXX</a>)
    通过浏览器直接访问 http://localhost/#&amp;detail.php?id=112;
    第一次载入<div id="pageDetail" class="page in" data-title="被请求的HTML" style="display: block;">…</div>
    返回页面首页,再次通过点<a href="detail.php?id=112" rel="nofollow">XXXX</a>访问,会再次重复创建<div id="pageDetail" class="page in" data-title="被请求的HTML" style="display: block;">…</div>

    • 张 鑫旭说道:

      @xuetian 首先感谢你的积极反馈,不知是v2.4.4这个版本吗?这个我版本我前几日刚上传,就是修改重复创建的问题。如果还有这个问题,还请麻烦告知完整测试页面代码,我本地测试没有你说的问题。

  11. practicer说道:

    看到了博主的http://www.mobilebone.org/官网。 很好的框架,一个小建议,把文档好好整理下,学习和使用,以及推广就更方便了。

  12. weiew说道:

    关于你这里推荐的fastclick(要扯一个不关你mobliebone的事情,sorry),我也在用,但用了发现他有兼容性问题,网上找了一堆都是他使用方法跟点赞的文章,死活找不到兼容性问题的文章,他在一些小米手机个别浏览器(一般同款手机,有自带浏览器,安装的浏览器(chrome,QQ等),还有app里面的浏览器等),拦截了click事件,导致一些select控件无效,跟其他的。如果你有对这个库有研究,希望可以分享下,谢谢

  13. 阿黎说道:

    我尝试在类似微信切换中添加 js自动播放的幻灯片,幻灯片加载不出来是什么原因呢

  14. 新人说道:

    六、mobilebone.js与过场回调方法 这一节中讲到“JS中应该是要尽量避免不必要的全局变量的,对于实际项目而言,都是全局方法是不实际的。”,但是 Mobilebone.rootTransition = root;,这个root就是window,那 Mobilebone.rootTransition 不还是window吗? 这样写有什么好处呢?

    好吧,我错了,你在执行动画里面用到这个名字,然后我们又可以修改这么名字,增加灵活性

  15. 关于使用 data-ajax="true" 跨域访问问题说道:

    我试了下,No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://10.186.255.231:8081’ is therefore not allowed access.拒绝访问,baidu了下,说是设置dataType: ‘JSONP’,是我用错了?

  16. 吕大豹说道:

    提个问题哈。onpagefirstinto,在页面刷新的时候不会执行。
    比如当前在page5下,刷新了一下,onpagefirstinto里面定义的页面初始化的东西不能执行

  17. SamShaw说道:

    博主你好,tab选项卡这边似乎有个bug,第一次切换至其他tab以后tab1对应的容器display一直为block而不是none,直至从tabX切回tab1以后才正常。我看了一下文档的例子也是这样,不过例子是上下移动的….所以看不出来….我们这边希望做横向的滚动,所以tab1对应的classPage在第一次切换回来以前一直是block的话其他classPage就会被挤到classPage1的容器高度以下了

  18. laser_lu说道:

    是否支持现在App上比较流行的左上角一点开就会出现一个高度充满但宽度可能只有60%的浮动菜单?这个浮动菜单也可能会出现右边,甚至上边和下边。

  19. also说道:

    案例里complex
    点底部切换在iphone 6plus safari 和 微信里
    切换的slide效果在来回点的 时候会失效
    其他手机没有试过
    在chrome 的模拟环境里没有出现

  20. 搁浅被注册了说道:

    大神,我每个页面的header都使用id绑定了事件,新的page进入时就会造成id冲突,只有第一个id绑定了事件,求教。我看到data-reload=”root”这个参数可以无缓存加载,但是使用了还是有多个page同时存在,而不是只有一个,原先的page并没有remove掉。

    • 张 鑫旭说道:

      @搁浅被注册了 data-reload="root"是判断ajax的根地址是否一致做处理的,所以,您可以检查下你的请求是不是(除了查询字符串以外)都是同目录的。如果是,建议去Github项目的issuses反馈问题,我会跟进;如果确实不一致,你可以使用data-animationstart回调手动移除之前的页面。

      • 搁浅被注册了说道:

        本地环境调试,都是在当前目录下。url地址设置为http://127.0.0.1/*也还是不行 。
        http://rawgit.com/zhangxinxu/mobilebone/master/test/ajax-html/index.html#&pageHome
        大神的这个测试界面两个 “根地址不缓存” 链接,点击也只出现菊花并没有页面变化。下载下来的test目录下本地调试时两个 “根地址不缓存” 链接点击报错…

        找到原因了,问题出在 “url的根地址” 这个问题,我所指的是在同域名下的所有页面执行无缓存加载,也就是整站无缓存加载了。而您这个项目实现貌似是判断到具体页面而不是只是域名。我单纯只想要页面切换效果,保持只有一个page变动的状态。大神,这个功能后续会加入吗?感觉应该会有这种需求…

  21. Goofy说道:

    我来吐槽英文单词,Hybrid(混合)不是Hybird…

  22. sharlock说道:

    我现在在链接上加data-container时候,会直接跳转,是我忽略了什么吗?

    去掉data-container就可以了,但是又不能局部过场了

    少了个in…
    还有,局部过场动画是不会往历史栈追加记录的吗?

    • 张 鑫旭说道:

      @sharlock 对的,是不会追加的。因为,局部过场,类似于我们平常页面中的选项卡之类效果,一般页面是不会有url变化;另外就是Mobilebone没法知道你浏览器后退时候,是局部过场,还是整夜过场。

  23. aliang说道:

    请教个问题,在固定头部和底部示例中。如果有滚动下拉条,页面切换时候下拉条一起替换了,由于下拉条和页面背景颜色不同,看上去很明显。用户体验不佳。有解决方法吗?

  24. 吕大豹说道:

    鑫哥,你在“更好的JSON包括HTML加载建议”中提到可以利用slide的时间同时来异步请求数据,这样更节省等待时间。
    那mobilebone自身为什么不这么做呢?我看到是等请求完毕后才开始slide的

  25. sharlock说道:

    有个问题:页面切换之后, 点击返回, 然后点浏览器后退或者手机端后退按钮时, 没有清除并且覆盖浏览器历史栈; 比如我从主页面跳转到a页面, 再切换到b, 再从b页面点击返回切换到a的时候, 这时候点击浏览器后退按钮, 以用户思维来讲, 应该返回主页面, 但是实际效果是先切换到b页面, 再回到a, 然后才能回到主页面. 是不是实际做的时候,返回主页面不能像示例一样, 需要使用history.go(-1)之类的方法返回? 我装的版本是2.3.2;

    • sharlock说道:

      点击返回的时候,感觉应该是出栈操作,不应该再往历史栈追加了.

    • 张 鑫旭说道:

      @sharlock 你好,你这种情况是容器内slide,可以参考data-container. PS: “从用户思维讲”,是你自己思维吧,浏览器访问页面,点击链接再返回肯定是遵循你的访问历史的。

      • sharlock说道:

        我昨天仔细看了看代码,然后把示例/test/base-slide/index.html中的三个返回按钮的href改为javascript:就可以正常返回了,示例写的是’#pagehome’,这样确实应该pushstate,你说得对.如果我想直接后退,就不写hash,然后就不会再往历史栈追加了.

  26. ryan说道:

    帅哥,有个问题请教,我发现jquerymobile是一个主页面里,就是添加一个page页面,其他的给remove掉了,而mobilebone里是 把所有page都罗列出来。。。

    jquerymobile这么做是不是更合理一些

    • 张 鑫旭说道:

      @ryan mobilebone默认是缓存,但是你可以控制不缓存的,使用data-reload="true"data-reload="root", 比jquerymobile要更智能。

  27. yinting说道:

    为什么wp下头尾不能固定?

    • 张 鑫旭说道:

      @yinting 你好,可否提供简易代码,我看下?wp头尾固定是支持的。

      • yinting说道:

        哦,是这样的上午我直接用微信扫描上面打开的,头尾是不能固定,后来我又用wp自带的ie浏览http://rawgit.com/zhangxinxu/mobilebone/master/test/index.html这个地址是能固定的,是不是微信打开后下面有一节工具栏他本身占据了浏览器的高度?

  28. lastonesky说道:

    Thanks for wonderful work!说明文档一页流,真是伤不起~

  29. commy说道:

    mobilebone.js的920行的这一句当中
    external = external || (href.split(“/”)[0] !== location.href.split(“/”)[0]);
    在chrome浏览器下我发现href.split(“/”)[0]的值是”http:”这样子的话,根据域名来判断跨域就判断不了?

  30. 南派八爷说道:

    博主辛苦!
    感谢制作了这么强大的开源。
    问个问题:
    各页面之间除了点击锚点切换的功能以外,有没有API可以X轴滑动屏幕切换?
    或者是配合iscroll也可以实现这样的效果吗?
    谢谢!

  31. 无赖君子说道:

    与zepto的touch模块冲突问题解决了没?
    在引入了touch模块后,mobileone在pc端的点击事件失效了

  32. 凡羽说道:

    我小米1s手机上自带的浏览器访问微信例子,一片空白,ui,firefox没有问题

  33. liujmok说道:

    我小米1s手机上自带的浏览器访问微信例子,一片空白,ui,firefox没有问题

  34. SamShaw说道:

    近期我们做的东西因jqm太庞大,又因我前端刚入门,在框架上苦于寻觅替代的时候看到了博主的项目,万分感谢博主的分享,辛苦了

  35. 一汀烟雨说道:

    希望能增加这样的一个功能,底部的一个导航区,每次点击当前项的时候是平滑滚动到顶部,而不是无反应不发生任何变化。

  36. 小凡羽说道:

    旭哥,从github下载的demo中,ajax-html里面的ajax实现不了滑动的效果,请解答

    • 张 鑫旭说道:

      @小凡羽 fill:// 形式的访问是不支持Ajax的~

      • dcl说道:

        旭哥我想问问,本地打开静态页面,不支持ajax的加载方式,怎样实现页面切换过场效果啊,(因为页面文件太多了不适合做单页面的)
        另外,我发现用history.back()做返回会有问题啊,不能正常返回,试了一台android 4.2(用webview)的和wp的手机都有问题,不过试了两台android 4.4的两台手机没问题,不知道是不是跟系统有关。
        求指教,谢谢!

  37. solan说道:

    大大神, qq那个demo 在pc的google浏览器正常 但是到到手机上的google和safari就什么也不显示 页面一片白. 有没有 群可以加下啊 好多问题想请教!

  38. 新人说道:

    鑫哥能不能建个群啊,这样大家也可以一起讨论,问题反馈也会比较快啊

  39. 新人三说道:

    TypeError: parent is null

    var tagParent = parent.tagName.toLowerCase();
    mobilebone.js (第 807 行,第 7 列

    为什么我用这个插件在火狐里总是报错啊

  40. moodpo说道:

    我去发表不了代码,去github了

  41. mattlin说道:

    感谢mobilebone,已通过支付宝捐赠一杯咖啡。

  42. 球球说道:

    张老师你真是宋江啊,为啥我一遇到问题,一来你的博客就能找到答案呢?最近研究H5框架,结果一上来就发现你已经写好了。这个必须赞一下。
    随便问一下:你写的框架支持AJAX跨域吗?如果不支持有什么方案能快速增加这个需求。

  43. lt说道:

    如果我在点击跳转到下一个页面之前要做一些处理,比如验证登录再跳转,没有登录则不跳转,应该怎么做呢?mobilebone有没有跳转前的事件处理回调?

    • 张 鑫旭说道:

      @lt 感谢反馈,很好的建议。晚上回去处理下。

      • lt说道:

        好的,谢谢,我暂时添加了两个回调解决了问题,不知道这样行不行
        159 行: [“onpagefirstinto”, “callback”, “fallback”, “animationstart”, “animationend”, ‘transitionBefore’, ‘transitionEnd’]
        190行: // do transition before
        var transitionBefore = params_in.transitionBefore;
        if (typeof transitionBefore == “string”) transitionBefore = params_out.root[transitionBefore];
        if (typeof transitionBefore == “function”) var canTrans = transitionBefore(pageInto, pageOut, options.response);
        if(canTrans===false) return false;
        202行: // do transition end
        var transitionEnd = params_out.transitionEnd;
        if (typeof transitionEnd == “string”) transitionEnd = params_out.root[transitionEnd];
        if (typeof transitionEnd == “function”) var canTrans =transitionEnd(pageInto, pageOut, options.response);
        if(canTrans===false) return false;

        • 张 鑫旭说道:

          @lt 赞!思路是对的。

        • 前端小尚说道:

          这个问题后来更新了吗?@张 鑫旭

        • 张 鑫旭说道:

          @前端小尚 你好,还没有,因为我想和form表单提交公用API,名称什么的,好没完全想好。API设计是个耗脑子的活,你懂的!翌日更新: 此问题现已更新(v2.2.0+),专门命名一个data-preventdefaultAPI接口,专门阻断Mobilebone的默认行为(如果返回true),中文详细说明(即本文),Github项目,以及API中文文档已同步更新。

  44. hdm58说道:

    http://192.168.1.128:8882/index.php/User/add#&/index.php/User/edit_info/id/15/

    刷新这种url的时候,无法调用callback函数啊

    data-params=”animationend=editinfo”

    editinfo这个函数无法调用

    http://rawgit.com/zhangxinxu/mobilebone/master/test/callback/index.html#&page1

    好像你刷新这个范例页面也无法执行 打印出下面的时间

    页面即将进行离开动画,时间是:13:47:52
    页面离开动画完成,时间是:13:47:53

    页面即将进行进入动画,时间是:13:47:53
    页面进入动画完成,时间是:13:47:54

    • 张 鑫旭说道:

      @hdm58 是这样的,因为刷新进入没有动画效果,是直接呈现,不会触发animationend事件,你可以试试使用callback回调,或者onpagefirstinto回调。

  45. whywhy24说道:

    clone一个,看了一下午,基本看完了,列几点感受,前提我是一个有代码洁癖的人。

    1、看到 var back = false;
    if (rel == “back”) {
    back = true;
    },这一段代码的时候,表示心里很痒,var back = rel == “back”
    2、在pc端,当history和返回按钮交叉点击时,有时候并不会返回
    3、有一个方法param,我不知道为什么要写在transition,大大指教下,因为我觉得每次调用transition都会重新申明这个param方法

    • 张 鑫旭说道:

      @whywhy24 因为参数可能会被动态修改,所以,每次都会重新get一次,按照现代浏览器的性能,和这点逻辑处理,损耗可以忽略不计。

  46. yun说道:

    有木有带中文注释的mobilebone.js ,让我这个不懂英文的人也能看懂,本人在你博客中学到太多东东了,真心的感谢你的分享。现我正在学习移动端APP,正好需要这东 东,如果有份带中文版的注释,也能方便国人哇,必尽自们母语还是中文。呵呵。

  47. young说道:

    支持一下!

  48. 行者说道:

    手机访问测试了,效果比直接跳转好多了。

  49. shooke说道:

    为什么在url处理的时候使用了锚点而不是使用HTML5 history pushState/replaceState让url完全变为链接的地址呢

  50. 蒋三说道:

    就两个页面,都用了isroll滚动,第一个页面滚动正常,但切换到第二页面时,就不能滚动,要刷新一下页面,才可以滚动,这是什么原因呢?在这里调用下面的loaded()方法

    //使用iScroll局部滚动
    var myScroll_H,myScroll_p1;
    function loaded () {
    myScroll = new IScroll(‘#pageHome’, { scrollX: true, freeScroll: true });
    myScroll_p1=new IScroll(‘#page1’, { scrollX: true, freeScroll: true });
    }
    document.addEventListener(‘touchmove’, function (e) { e.preventDefault(); }, false);

    &ltbody onload=”loaded()”&gt里调用loaded()方法

    • 张 鑫旭说道:

      @蒋三 你好,事件绑定请使用Mobilebone提供的事件回调API. 你这种情况适合使用onpagefirstinto()回调方法。祝你好运~

      • dcl说道:

        使用onpagefirstinto()回调方法绑定iScroll会在ajax请求的动态数据加载之前就执行了,因为数据还没加载,页面是没有高度的,这样的话,页面的iscroll就有问题了不能滚动。这应该怎么解决啊,求指教。