浅析jQuery EasyUI响应式布局的实现方案

标签: easyui fit fluid | 发表时间:2014-08-17 20:25 | 作者:世纪之光
出处:http://www.easyui.info

首先解释一下本篇文章标题中提到的“jQuery EasyUI响应式布局”,这里是指EasyUI里布局类组件的fit属性,也就是实现自适应的属性。到了1.4版本,新增了一个宽度百分比的概念,既可以用在布局类组件上,也可用在表单类组件上,但是其实现方案跟fit是类似的。

也就是说,jQuery EasyUI的自适应布局包含两块内容:

  • 布局类和表格类组件的fit属性设置为true,也就是宽度和高度都100%;
  • 布局类组件,表格类组件和表单类组件的宽度设置为百分比;

因为EasyUI比较复杂的DOM结构设计,导致响应式布局无法使用css里原生的百分比去实现,通常宿主DOM,都会被包装得面目全非。最后组件呈现的时候,全部是以具体的像素值显示的。

1.4版本的代码来做分析,我们看看EasyUI的fluid神秘面纱后的逻辑到底是个什么样的?特别是组件多层嵌套的时候,它是如何做到每个组件都能自适应的。

追本溯源

先来追本溯源,fluid的本质是浏览器窗口调整大小的时候,页面的布局能相应的做调整,而在这个过程中,唯一能利用的事件是window的resize,所以EasyUI响应式布局的实现,一定是在window的resize事件中处理的。

我们在源码中搜索"$(window)"关键字,共搜索到20个左右,但是跟resize事件相关的只有两个地方。一个是panel组件里绑定的resize;另一个是window组件里绑定的resize事件。

window组件绑定的resize

为什么要把window组件放到前面看?那是因为window组件里绑定的resize跟fluid实在没什么关系,来看代码:

  1. $(window).resize(function () {
  2.         $("body>div.window-mask").css({width: $(window)._outerWidth(), height: $(window)._outerHeight()});
  3.         setTimeout(function () {
  4.             $("body>div.window-mask").css({width: _26d().width, height: _26d().height});
  5.         }, 50);
  6.     });

这个事件处理程序,像个一丝不挂的普通姑娘,没啥内涵,目的单纯而直白——只是为了实时调整window组件的蒙版大小,他的功能相对于fluid来讲实在微不足道,所以我们一眼嫖过去,不做过多讨论。

panel组件绑定的resize

panel组件绑定的resize事件处理程序是相当有内涵的,她绝对不是一丝不挂,而且穿了好几层情趣内衣,我们需要一层一层的扒,需要耐心。先来看事件处理程序的定义:

  1. // 定义的了一个定时器的引用,老鸟们应该都知道他的目的有两个
  2. // 一是防止短时间内多次触发window的resize事件处理程序;
  3. // 二是解决某些浏览器调整一次窗口却多次触发resize事件的问题
  4. var _23f = null;
  5. $(window).unbind(".panel").bind("resize.panel", function () {
  6.     // 100ms内触发多次的话,则终止对前一次的事件处理程序的调用
  7.     if (_23f) {
  8.         clearTimeout(_23f);
  9.     }
  10.     // 重置定时器
  11.     _23f = setTimeout(function () {
  12.         // 这里主要是看body是否是一个layout实例(是layout的话,body元素上会有layout样式)
  13.         var _240 = $("body.layout");
  14.         // 如果body是一个layout,则调用layout的resize方法,
  15.         // layout的resize方法最终调用的也是panel的resize方法
  16.         // 所以layout的resize显然是个多层情趣内裤,我们不急着扒。
  17.         if (_240.length) {
  18.             _240.layout("resize");
  19.         } else {
  20.             // 如果body不是一个layout组件,则调用panel组件的doLayout方法
  21.             // 这个层数应该少点,而且地处核心位置,我们先扒这个doLayout
  22.             $("body").panel("doLayout");
  23.         }
  24.         // 清空定时器
  25.         _23f = null;
  26.     }, 100);
  27. });

当我们的页面上引用了jquery.easyui.min.js这个伪开源的文件之后,这段代码会被执行一次。这段代码虽然不长,但是里面调用了resize和doLayout方法。

layout的resize方法,其底层调用的是panel的reszie方法(这层内衣我迅速的扒了,不信的同学可以自己看layout的代码)。所以,最后的焦点全部落到panel组件的两个方法上: doLayout和resize

doLayout方法

找到panel组件的doLayout代码(搜"doLayout"关键字即可):

  1. function doLayout (jq, all) {
  2.     return jq.each(function () {
  3.         // 缓存this
  4.         var _24a = this;
  5.         // _24b变量判断当前容器是不是body
  6.         var _24b = _24a == $("body")[0];
  7.         // find函数的选择器真的很长,最后还用了filter方法来进一步过滤find出来结果
  8.         // 这个写法看起来似乎很简洁明了,但是,他是否高效呢?这个问题先放一放
  9.         // 我们先弄清楚这个变量s到底是什么?这个必须要拿例子来说明,注释里我试了很多方式去表达,都觉得表达不清楚。
  10.         var s = $(this).find("div.panel:visible,div.accordion:visible,div.tabs-container:visible,div.layout:visible,.easyui-fluid:visible").filter(function (_24c, el) {
  11.             var p = $(el).parents("div.panel-body:first");
  12.             if (_24b) {
  13.                 return p.length == 0;
  14.             } else {
  15.                 return p[0] == _24a;
  16.             }
  17.         });
  18.         // 找到的需要做fluid布局后,触发绑定在他们DOM上自定义的的"_reszie"事件
  19.         s.trigger("_resize", [all || false]);
  20.     });
  21. }

对于doLayout函数中的s变量,分两种情况举例子。

如果当前容器是body:

  1. <!-- 当前容器 -->
  2. <body>
  3.     <div id="a1">
  4.         <div id="a21" class="accordion">
  5.             <div id="a3">
  6.                 <div id="a4" class="accordion">...</div>
  7.             </div>
  8.         </div>
  9.         <div id="a22" class="accordion">...</div>
  10.     </div>
  11. </body>

s只包含"a21"和 "a22",其实也就是离当前容器最近的包含特征样式的子孙级元素。

如果当前元素是div.panel-body:

  1. <div class="panel-body">
  2.     <div id="a1">
  3.         <div id="a21" class="accordion">
  4.             <div id="a3">
  5.                 <div id="a4" class="accordion">...</div>
  6.             </div>
  7.         </div>
  8.         <div id="a22" class="accordion">...</div>
  9.     </div>
  10. </div>

s也只包含"a21"和 "a22",跟body情况是一样的,只是寻找方式不一样。

这个doLayout函数的目的就比较清楚了: 它负责寻找当前容器的下一级(不一定是子级别,也可能是孙子,重孙子等)需要做响应式布局的组件,然后触发绑定在这些组件上自定义的"_reszie"事件。

由此,我们也可以推断: 每一个含有响应式特性的组件,其DOM结构里面肯定存在一个绑定了自定义的"_resize"事件的事件处理程序。

到这里, “下一级需要做响应式布局的组件”完成了自适应,那么 “下下级需要做响应式布局的组件”(响应式组件多层嵌套)是怎么完成自适应的呢?。

不急,我们还是拿panel组件自定义的"_resize"来看:

  1. function _13(_14) {
  2.     $(_14).addClass("panel-body")._size("clear");
  3.     var _15 = $("<div class=\"panel\"></div>").insertBefore(_14);
  4.     _15[0].appendChild(_14);
  5.     _15.bind("_resize", function (e, _16) {
  6.         if ($(this).hasClass("easyui-fluid") || _16) {
  7.             // _3函数就是panel组件resize方法的实现
  8.             _3(_14);
  9.         }
  10.         return false;
  11.     });
  12.     return _15;
  13. };

也就是说自定义的"_resize"事件处理程序里,调用组件自身的resize方法,看来想知道“下下级”的响应式布局是如何完成的,必须还是去问resize方法。

resize方法

我们直接看代码:

  1. // panel组件resize方法的实现
  2. function _3(_4, _5) {
  3.     var _6 = $.data(_4, "panel");
  4.     var _7 = _6.options;
  5.     var _8 = _6.panel;
  6.     var _9 = _8.children("div.panel-header");
  7.     var _a = _8.children("div.panel-body");
  8.     if (_5) {
  9.         $.extend(_7, {width: _5.width, height: _5.height, minWidth: _5.minWidth, maxWidth: _5.maxWidth, minHeight: _5.minHeight, maxHeight: _5.maxHeight, left: _5.left, top: _5.top});
  10.     }
  11.     _8._size(_7);
  12.     _9.add(_a)._outerWidth(_8.width());
  13.     if (!isNaN(parseInt(_7.height))) {
  14.         _a._outerHeight(_8.height() - _9._outerHeight());
  15.     } else {
  16.         _a.css("height", "");
  17.         var _b = $.parser.parseValue("minHeight", _7.minHeight, _8.parent());
  18.         var _c = $.parser.parseValue("maxHeight", _7.maxHeight, _8.parent());
  19.         var _d = _9._outerHeight() + _8._outerHeight() - _8.height();
  20.         _a._size("minHeight", _b ? (_b - _d) : "");
  21.         _a._size("maxHeight", _c ? (_c - _d) : "");
  22.     }
  23.     _8.css({height: "", minHeight: "", maxHeight: "", left: _7.left, top: _7.top});
  24.     _7.onResize.apply(_4, [_7.width, _7.height]);
  25.     // 关键代码只有这一行,瞧,它又调用了panel组件的doLayout方法!
  26.     $(_4).panel("doLayout");
  27. };

结论

  • 步骤一:window的resize事件触发doLayout方法,当前容器(上下文)是body;
  • 步骤二:doLayout方法搜索“下一级响应式组件”,触发“下一级响应式组件”的"resize"方法;
  • 步骤三:“下一级响应式组件”的"resize"方法调用doLayout方法,也就是回到“步骤一”只不过当前容器(上下文)换成“下一级响应式组件”,它再次执行的时候,找的就是“下下级响应式组件”。

如此循环调用,一层一层顺序作响应式处理,直到找不到为止。值得注意的是,响应式处理不一定是从body开始,比如tabs组件在切换标签页的时候。

 性能问题

doLayout对“下一级响应式布局组件”的搜索代码是有很大优化空间的,当页面DOM结构很复杂的时候,特别是有大数据量表格的时候,在IE下的doLayout的搜索效率惨不忍睹,可以使用“children方法+递归调用+对普通表格不搜索”的方案优化。

相关 [jquery easyui 响应式] 推荐:

浅析jQuery EasyUI响应式布局的实现方案

- - WebUI框架使用参考
首先解释一下本篇文章标题中提到的“jQuery EasyUI响应式布局”,这里是指EasyUI里布局类组件的fit属性,也就是实现自适应的属性. 到了1.4版本,新增了一个宽度百分比的概念,既可以用在布局类组件上,也可用在表单类组件上,但是其实现方案跟fit是类似的. 也就是说,jQuery EasyUI的自适应布局包含两块内容:.

jQuery EasyUI 1.3.5 发布

- - 开源中国社区最新新闻
jQuery EasyUI 1.3.5 发布,此版本修复了bugs和增加了一些改进. 下载: jquery-easyui-1.3.5.zip.

Jquery easyui 下loaing效果

- - CSDN博客推荐文章
beforeSend:ajaxLoading,\\发送请求前打开进度条. ajaxLoadEnd();\\任务执行成功,关闭进度条. 作者:songhfu 发表于2013-8-10 17:24:30 原文链接. 阅读:15 评论:0 查看评论.

JQuery EasyUi之界面设计——前言与界面效果(一)

- - 博客园_首页
如果冯巩的开场白是“观众朋友们,我想死你们了”,那么我的开场白是“最近一直很忙,很久没有发文了”. 前面说过了EXT.NET,这里顺便再说说JQuery EasyUI. 为啥我会选择JQuery EasyUI呢. 基本的组件都用,即“麻雀虽小五脏俱全”. 使用简洁方便,比如支持html+js. 世上没有完美的产品,而且其对IE6的兼容性还存在一些问题,但相比extjs,其还是很方便阅读和修改的.

基于jquery-easyui的仓库管理系统

- - CSDN博客Web前端推荐文章
使用jQuery EasyUI创建的仓库管理系统包括系统管理、数据维护、业务单据管理等,有兴趣可以对其进行修改扩展. 数据库采用MYSQL, 帐号/密码:root/root,演示登录帐号/密码:admin/admin. 数据库配置信息可以修改配置文件:/WEB-INF/classes/activerecord.properties.

一个关于jquery easyui crud demo 的一个例子

- - Web前端 - ITeye博客
注:这个程序jsp的源代码在这个http://www.jeasyui.com/demo/main/index.php网址的basic crud里面下载它用的是html + php 我们今天要把他改成 Java后台 struts + hibernate + ibatis. 这里是jsp代码easyui.jsp.

15 个响应式的 jQuery 图像滑块插件

- - 博客 - 伯乐在线
设计师和开发人员总是试图使用新技术让网站更智能,而我们发现在许多网站上 jQuery 的图像滑块插件是非常受欢迎的. 本文继续介绍 15 个 jQuery 图像滑块插件以供您选择. ELASTISLIDE – 响应式的 jQuery 传送带插件. 很帮的全响应式的 jQuery 滑块插件. 用户创建相册展示并自动缩放到其容器大小的 jQuery 插件.

优秀的响应式 jQuery 滑块插件 – iView Slider

- - 博客园_梦想天空
  iView Slider 是一款优秀的 jQuery 滑动插件,用于实现文本、图片、视频等各种网页内容的滑动功能. iView Slider 支持响应式布局,能够很好的运行于触屏设备中. iView Slider 内置35种很炫的过渡效果(Transition Effects),能够帮助你制作出各种各样的滑动效果.

8款非常棒的响应式 jQuery 幻灯片插件推荐

- - 博客园_梦想天空
这篇文章收集了8款优秀的响应式. jQuery 幻灯片插件,它们能够非常容易的集成到 Web 项目中. 响应式(Responsive)设计的目标是要让产品界面能够响应用户的行为,根据不同终端设备自动调整尺寸,带给用户良好的使用体验. FlexSlider 是一款非常棒的响应式 jQuery 幻灯片插件,能够自适应屏幕尺寸,呈现漂亮的外观.

10款非常有效的帮助你设计超酷响应式布局的jQuery插件

- - 博客园_首页
日期:2012/02/24  来源: GBin1.com. 如 果你关注最近几年的web设计,那么响应式布局(responsive layout)设计对于你来说肯定不是一个陌生的词汇. 相对于传统的页面设计来说,今天的设计者需要考虑的用户来源和用户体验对于设计者来说是一个非常大的挑战,因为随着硬件的更新,新设备的出现,你需要能够 使得你的网站能够针对不同的设置做出最好显示响应.