2011
李晓肆 leexiaosi@gmail.com
[ JQUERY MOBILE 进阶] 通过对 jQuery Mobile 源代码分析,详细介绍了 jQuery Mobile 的主要 API 和实现原理, 并从开发实践角度,提供了一些参考意见和实现方式。
Advance jQuery Mobile
前言 ——谢一位新交的老朋友 首先要感谢 Eddy 老兄,我们共事方才一个多月,却像一对老搭档一样,默契十足。很 多时候,都是他鼓励我去做自己喜欢的事情,一句句“你可以。。。” ,常常使我茅塞顿开,同 时他涉猎广趣,很多奇思妙想和新颖的力作,常常令我着迷,有时我不禁感叹,他心态比我 年轻,而事实上,他在 80 的开始,我在 80 的末梢。 李开复曾在微博写道: 有一本书的作者访问了一百位名人,问他们希望 25 年岁最需要知道的一件事?最多的 回答是“希望有前辈告诉我,鼓励我去追寻自己的梦想。” 而我按照家乡虚岁的计算,恰是 25 岁,正处在理想和现实的坡道上,很多时候都是一 次次拼命向理想去爬,又一次次下滑到现实的坎坷路上。 截止到今天,3 月 14 日,也恰是我来北京一年整,去年的今天,我下了火车看到的北 京是白茫茫一片,恰如我当时的心情,对北京一片陌生,既充满着期待,也加杂着迷茫。 而今,随仍处于濒临“颠沛流离”的处境,但是终究是落下脚,基本懂得了这里的生成 规则。 现实的残酷常常使我们垂头丧气,不知所措。可是北京这样的城市,一旦缺失了梦想, 支撑的身体的宛如一尊行尸走肉。与其唾骂世态,不如梦想燃烧。。 。 这篇文档的整理和归纳,也是起因于 Eddy 老兄的“怂恿” 。 于是,奋笔疾书,以谢知遇之恩。 李晓肆 2010-03-14
1. jQMobile 实现原理 1.1. 组件老祖 Widget jQuery Moile 从代码实现角度来看,与 jQuery UI 的实现源于一脉,底层都是依赖 jQuery 核心库,Widget(组件或 UI 部件)的实现都是使用了 jQuery.ui.widget.js 这个包(此处的包指的 是 jQuery UI 或 jQuery Mobile 拆解包)。强烈建议大家阅读一下这个包的内容,未压缩版只有 7K 左右, 不到 300 行代码, 通过这个包,大家基本可以了解整个 jQuery UI 或者是 jQuery Mobile 的组件实现原理。下面只做简要的描述。 jQuery.ui.widget.js 包中,实现了一个 Widget 的基本类,这个类的基本形式图 1-1
$.Widget.prototype + widgetName : String + widgetEventPrefix : String + options : Object + + + + + -
= "widget" = ""
_createWidget (Obj ect options, $Obj ect element) _getCreateOptions () _create () _init () destroy () w idget () option () _setOptions () _setOption (String key, Obj ect v alue) enable () disable () _trigger ()
: v oid : Obj ect : v oid : v oid : v oid : DomObj ect : $Widgetprototype : $Widgetprototype : $Widgetprototype : $Widgetprototype : $Widgetprototype : $Widgetprototype
图 1-1$.Widget.prototype
表 1-1Widget 类的定义
Widget options
Object { disabled=false}
配置项
widgetEventPrefix
""
未知(?)
widgetName
"widget"
控件名
_create
function()
创建方法
_createWidget
function()
创建组件的构造函数
_getCreateOptions
function()
获取创建的配置项
_init
function()
初始化
_setOption
function()
设置单个配置项
_setOptions
function()
设置多个配置项
_trigger
function()
触发事件方法
destroy
function()
销毁方法
disable
function()
使不可用
enable
function()
使可用
option
function()
配置方法
widget
function()
返回真实的 DOM 元素
附:以上是我个人翻译的,缺乏严谨性
更好的建议是, 在火狐浏览器的 firbug 或者是谷歌浏览器或者是 Safari 浏览器的 console 中输入 >>>$.Widget.prototype jQuery 中使用了很多 javascript 原型继承的方式,上面的内容如果无法理解,可以购买 一本道格拉斯(Douglas Crockford)的《JavaScript 语言精粹》,有关 javascript 的基础知识,本 文不展开讲解。 这里需要注意一点,以”_”标记的方法,表示是私有方法,最好不要尝试使用和修改。 这个组件类 Widget,就是 jQuery UI 和 jQuery Mobile 的开山老祖。几乎所有的 jQuery Widget(UI 组件或者部件,包括 jQuery UI 和 Mobile)都是直接或间接继承了$.Widget.prototype。 从 OO(面向对象)的角度上,$.Widget.prototype 是所有 UI 组件的基类。 jQuery.ui.widget.js 闭包中提供了一个$.widget 组件扩展函数,函数的定义形式如下 $.widget(name,base,prototype) name:ui 组件全称 命名空间加名成,形如”mobile.button” base:基类 protoype:扩展的原型 $.widget 会将通过$.Widget 或其子类的扩展的新 UI 组件类收归到相应的空间下。jQuery Mobile 中几乎所有的组件都是在$.Mobile 这个命名空间下。 如果你熟悉 jQuery 中插件编写常用的两个函数,$.extend 或$.fn.extend 函数,你将会非 常容易的理解$.widget,同$.extend 和$.fn.extend 的扩展形式一样,$.widget 只是提供了一种 更好的组件扩展方式。我这里就做个模拟演示。 $.widget(“猫科.老虎", ”猫” ,{“特征”:”脑袋有个王”}) 于是在 jQuery 中就会出现这样一个“$.猫科.老虎”的类,它将继承猫的特性,同时具 有值为“脑袋有个王”的“特征” 。 当然,上面纯属一种形象地演示。 真正的实现,例如在 jQuery Mobile 增加个组件叫做”leexiaosi”,可以这样扩展 $.widget(“mobile.leexiaosi” ,{email:”leexiaosi@gmail.com”,warn:”非诚勿扰!”}) (使用了$.widget 的另一种方式,直接继承自$.Widget)
于是,如图$.mobile.leexiaosi 将会继承$.Widget 的特征,此外还扩展了个属性 email, warn。
$.mobile.leexiaosi + email : String + warn : String
= "leexiaosi@gmail.com" = "非诚勿扰!"
图 1-2$.mobile.leexiaosi
熟悉 jQuery 的人都懂得,$(即 jQuery 工厂函数)上的方法都是公共方法,而非 jQuery 对 象上的方法。为了可以让 jQuery 对象可以调用方法,需要在$.fn(即 jQuery 的原型)上扩展方 法。所以 jQuery.ui.widget.js 还定义了一个函数$.widget.bridge,这个函数的作用就是往 jQuery.fn 上发布组件工厂函数的同名调用方法,函数的定义形式如下: $.widget.bridge(name,object) name:表示代理调用方法的名称,一般与组件的工厂函数相同 object:即组件对象
例如通过$.widget.bridge 可以将$.mobile.leexiaosi 发布到 jQuery.fn 上, $.widget.bridge(“leexiaosi”, $.mobile.leexiaos) 这样就会存在一个$.fn. leexiaosi,可以使用 jQuery 对象调用 leexiaosi $(“div”).leexiaosi() 好了,关于 jQueryUI 和 Mobile 的老祖宗 Widget 的实现,就讲解到这里,上面只做概要 的演示,细节上可能存在争议,敬请指正。
1.2. 组件之母$.mobile.widget 上一小节介绍的是 jQ Mobile 的老祖宗,那么现在介绍的就是 jQ Mobile 的老母亲 $.mobile.widget,jQuery Mobile 的所有 UI 组件几乎都是通过$.widget 对$.mobile.widget 的扩 展。 首先$.mobile.widget 自身就继承自$.Widget,只是重写了一个_getCreateOptions 方法, 从而支持从元素的 data 上去配置数据。jQuery Mobile 有别与 jQuery UI 的重要一点就是,组 件的生成使用 HTML 5 标记的形式,由 jQuery Mobile 自动生成。例如 <div data-role=”page”></div> 即可生成一个$.mobile.page 渲染的 ui 组件,具体的实现形式,后面的章节将会详细展 开。
1.3. 组件关系图 jQuery Mobile 通过$.widget 扩展的组件如图 1-3 $.Widget
$.mobile.widget
navbar
page
button
checkboxradio
collapsible
dialog
textinput
listview
slider
selectmenu
图 1-3jQuery Mobile 组件关系图
此外,jQuery Mobile 还有一些组件是通过插件的形式直接扩展到了 jQuery 的原型上 包括 buttonMarkup controlGroup fieldContain fixHeaderFooter grid 这五个组件的构建有别于$.Widget 的子孙类。
1.4. jQ Mobile 配置项 与 jQuery 的插件一样,jQuery Mobile 本身也提供一些可选配置,可以更改 jQuery Mobile
的一些默认配置,其命名空间都在$.mobile 下。下面主要简单介绍一下 名称
类型
默认值
简要说明
activeBtnClass
字符串
ui-btn-active
默认的 Button 激活样式
activePageClass
字符串
ui-page-active
获取当前活动的 page 页面样 式
ajaxEnabled
布尔值
true
页面是否默认启动 ajax 调用
ajaxFormsEnabled
布尔值
true
表单提交是否默认启动 ajax
ajaxLinksEnabled
布尔值
true
A 标记链接是否默认启动 ajax
autoInitialize
布尔值
true
是否自动进行初始化
defaultTransition
字符串
slide
默认的切换方式
hashListeningEnabled
布尔值
true
浏览器哈希表监听是否开启
loadingMessage
字符串
loading
Ajax 加载提示信息
metaViewportContent
字符串
width=device-width, minimum-scale=1, maximum-scale=1"
设置 viewprort 视窗的一些属 性
nonHistorySelectors
字符串
dialog
默认不记录到浏览器哈希表的 ui 组件
subPageUrlKey
字符串
ui-page
url 的子页面关键字
这里简单解释一些内容: ajaxEnabled,ajaxFormsEnabled,ajaxLinksEnabled 是设置默认启动 ajax 调用的,也就是 说页面之间的切换,以及表单的提交和 a 标记中的链接,都是采用 ajax 调用。 页面之间,已经通过 a 标记(jQuery Mobile 中很多的 a 标记被封装成了 button)单击, 都会启动 ajax 调用,这可能会使得后来者的脚本和样式失效。所以,最简单的方式是,将 一组相互直接链接或者切换的页面的脚本、样式分别提取到一个共同的 js 和 css 文件中,各 个页面都同时引用该脚本和样式文件。同事,各个页面直接的命名需要进行隔离,可以设置 主题相关的虚拟命名空间, 例如两个页面 a,b 都有 id 相同的”btn1”, 可以使用”a_btn1”,”b_btn1” 用以隔离不同的控件。避免在 jQuery 中查询出错。 loadingMessage 是 页 面 加 载 中 的 加 载 提 示 信 息 , 可 以 进 行 相 应 的 配 置 。 下 面 以 loadingMessage 的配置修改为例 默认配置的修改需要使用”mobileinit”事件,其执行是早于 ready 的。修改方法如下 $(document).bind(“mobileinit”,function(){ $.mobile.loadingMessage=”加载中”; })
1.5. jQ Mobile 的公共属性和方法 除了上面的配置项以外,在 jQuery Mobile 中还有几个重要的公共属性和方法 activePage 类型:jQuery 对象 例如:[div#mypage.ui-page] 获取当前的激活页面 这是个很好的的属性,通过这个属性,可以快速定位到激活页面,同时,有时后在前端 编程中,需要对当前页进行操作,activePage 无疑成为最快速获取的捷径
firstPage
类型:jQuery 对象 例如:[div#mypage.ui-page] 获取当前页面的第一个 page keyCode 类型:对象 例如: >>>$.mobile.keyCode[“ALT”] 8 jQuery Mobile 对键盘按键做了映射,在相应键盘事件中,可以使用这个对象。 pageContainer 类型:jQuery 对象 例如:[body.ui-mobile-viewport] 获取页面的容器 path
类型:对象 记录着路径对象 urlHistory
类型:对象 记录着访问历史的页面对象 urlstack 类型:数组 记录着历史 url 对象 addResolutionBreakpoints()
参数:数组或者单值 增加屏幕的分辨率信息 changePage()
页面切换 这是非常重要的一个函数,可以实现自定义的页面切换。 主要的定义形式如下: $.mobile.changePage(to,transition,reverse,changHash) transtiton 类型是字符串,默认是”slide”,其含义是页面的切换方式。 reverse 类型是布尔值,默认是 false,即是否可以返回 changHash 类型是布尔值,默认是 false,即是否记录到历史记录哈希表中 to 类型有多种实现方式 方式一:字符串 如:$.mobile.changePage(“about/us.html”,”slide”,true,true) 方式二: jQuery 对象(即 page 对象) 如:$.mobile.changePage($(“#myPage”),”slide”,true,true) 方式三:对象 如:$.mobile.changePage({to:”default.aspx”,data:{name:”leexiaosi”},type:”post”},”slide”, true,true) 还有第四种是数组参数,不做介绍 这四种中,第一种适合不含数据交互的页面切换,两个页面间没有数据交互。第二种适
合在本地页面中进行 page 切换。第三种适合含有数据交互的页面切换。 gradeA() 用来做设备的支持等级 initializePage() 初始化页面,对页面 data-role 为 page 和 dialog 的 div 进行初始化。这个方法可以 用在动态加载 dom 的方法中,例如某些 dom 是通过 ajax 加载生成,则可以使用该方法 对也没再次进行初始化,以构建出 jQuery Mobile ui 效果 pageLoading()
参数:布尔值 如果值为真,则取消加载提示,如果为空或为假,则显示提示信息。 这个可以在自定义的 ajax 事件中调用,以增加友好的页面交互。 media()
css 支持程度检测 silentScroll()
参数:数值 正数表示使页面向上滑动,负数表示使页面向下滑动。数值表示滑动到纵坐标位置 的像素 updateHash() 更新历史记录哈希表
小结 本章对 jQuery Mobile 的实现原理做了简单的介绍。介于目前 jQuery mobile 尚且没有出 正式版,其官方的 api 参考文档介绍相对精简,缺乏深入的可参考内容。本章很多细节都是 我个人通过阅读源码理解写成,存在理解的偏差和个人主管臆断,实际开发中,还需要自己 动手,在实践中摸索。 要点: 理解 jQuery Mobile 的 UI 组件的实现方式 整体熟悉 jQuery Mobile 的配置项、公共属性和方法。