在Web中实现C/S模式的Tab

在探讨C/S模式的Tab之前,我们先总结一下B/S模式的Tab通常是什么样的。web中常见的tab设计通常是用于分节展示大量信息以提高页面空间的利用率,而且这些信息通常是静态的,或者交互比较简单。通过ajax动态加载tab内容的技术也是为这种应用场景设计的。
随着Web技术的发展,越来越多的桌面应用都在向Web应用转型。在这个过程中,很多C/S模式的应用场景不可避免的会被移植到Web中。但是B/S模式相对较弱的交互性决定了某些移植很难实现或者效果不好。C/S模式的Tab设计就是其中之一。
所谓C/S模式的Tab实际上是为了在不需要打开多个应用的情况下,可以同时对多个对象进行操作,以提高工作效率。这个应用场景在往B/S移植的过程中,如果沿用B/S常用的tab技术,通过ajax动态加载tab内容,就会有一些技术难点:
- 在B/S的架构中,同一类型的tab页通常是对应的同一个物理页面,例如订单A和订单B的的tab页实际上都是对应的order.php这个页面,只不过请求的GET参数不一样而已。这样如果同时加载了订单A和订单B的tab页,那么这两个页面的html和js很可能发生冲突。
- 我们需要用一种标识来区别不同的tab页,并且避免重复打开同一个tab页。例如,在订单A已经被打开的情况下,如果试图再打开订单A,那么订单A的tab应该被激活,而不是再打开一个订单A。
- 如果打开的tab数量较多,或者tab页中的交互复杂、内容很多,浏览器就很容易出现性能问题,速度变的很慢。
- 还有很多潜在的、琐碎的问题随时可能让我们抓狂。在这点上,我是有惨痛教训的……
其实解决上面这些问题的方式是很多的。经过一些尝试、失败和总结,我选择了一种现代结合复古的方式:动态生成iframe,并通过iframe的src属性来区分不同的tab。采用这种方案之后,上面难题都迎刃而解,唯一的不足就是tab子页和主页间的交互会变得比较复杂。请猛击这里看demo。这个demo模拟了一个查看、修改订单的应用场景,主要实现了这些功能:
- 添加tab的时候,动态生成一个iframe。并且在tab切换的时候,所有iframe都不会刷新。
- iframe的高度自适应内容的高度。
- 在尝试打开一个已经存在的tab的时候,会激活这个tab,不会重复打开相同的tab。
- tab可以被关闭,并且关闭的时候tab对应的iframe会被移除。
下面我们就一步一步的实现上面的功能。
第一步:编写demo的框架
这个demo用到了jquery和jquery的ui库,文件结构如图:

其中:
- index.html是demo的首页。
- order.php就是订单的详细页面,每个订单详情tab都是加载的这个页面。
- index.css是首页的样式表。
- index.js是首页的js脚本。
由于篇幅的限制,这里就不贴代码了,demo的源代码在文章最后可以下载。
第二步:初始化jquery tab
现在我们可以开始编写index.js了。首先是初始化tab:
// 初始化tab
$( "#tabs" ).tabs({
tabTemplate: '<li><a href="#{href}">#{label}</a><a class="close" href="#">x</a></li>',
cache: true
})
.bind( "tabsadd", function( event, ui ) {
$( this ).tabs( "select", "#" + ui.panel.id );
});
初始化参数中的tabTemplate是为后面的关闭tab做准备。这里还绑定了一个tabsadd的事件处理函数,作用是在添加tab之后立即选中新增的tab。
第三步:动态生成iframe
如果现在打开页面,只能看到订单列表一个tab,下面我们让列表中的连接被点击之后,添加一个新tab显示对应订单的详情:
// 点击添加tab页
$( ".list a" ).click( function( e ) {
e.preventDefault();
var href = $( this ).attr( "href" );
var orderid = href.substring( href.indexOf( "-" ) + 1 );
var tabid = "order-" + orderid;
var url = "order.php?orderid=" + orderid;
var label = "订单详情-" + orderid;
addTab( tabid, url, label );
}); // 添加tab的接口
function addTab( id, url, label ) {
var mainTab = $( "#tabs" );
var panel = $( "<div/>" ).attr({
"id": id
}).appendTo( mainTab ); mainTab.tabs( "add", "#" + id, label ); $( "<iframe/>" ).attr({
"frameBorder": "0",
"scrolling": "no",
"allowTransparency": "true",
"src": url
}).css({
"width": "100%",
"height": "100px"
}).load( function() {
var iframe = $( this );
iframe.height( iframe.contents().find( "body" ).height());
}).appendTo( panel );
}
上面的代码中,我们编写了一个方法作为添加tab的通用接口。在点击某一个订单链接的时候,我们会调用这个添加tab的接口,按顺序做这几件事:
- 生成一个div,作为jquery tab的panel,也就是iframe的容器。
- 调用jquery tab的接口新增一个tab指向刚才的div,并选中这个tab(刚才绑定的tabsadd事件)。
- 创建并初始化一个iframe,然后将它添加到刚生成的div里面。其中,在初始化的时候我们给iframe添加了一个load事件的处理函数,用来让iframe自适应内容的高度。
第四步:防止重复打开tab
现在如果多次点击订单列表中的同一个链接,我们会打开多个相同的tab。为了避免这种情况发生,我们需要在添加tab之前,通过iframe的src判断这个tab是否已经被打开,如果已经被打开,则选中这个tab(4行至15行):
// 添加tab的接口
function addTab( id, url, label ) {
var mainTab = $( "#tabs" );
var added = false;
$( "iframe", mainTab ).each( function( i ) {
var src = this.src.substring( this.src.lastIndexOf( "/" ) + 1 );
if ( src == url ) {
added = $( this );
}
}); if ( added ) {
mainTab.tabs( "select", "#" + added.parent().attr( "id" ));
return;
} var panel = $( "<div/>" ).attr({
"id": id
}).appendTo( mainTab ); mainTab.tabs( "add", "#" + id, label ); $( "<iframe/>" ).attr({
"frameBorder": "0",
"scrolling": "no",
"allowTransparency": "true",
"src": url
}).css({
"width": "100%",
"height": "100px"
}).load( function() {
var iframe = $( this );
iframe.height( iframe.contents().find( "body" ).height());
}).appendTo( panel );
}
第五步:关闭tab
最后我们让tab上的关闭链接发挥作用。实现这个功能通常的思路是,在每次添加tab的时候(tabsadd事件)绑定关闭tab的处理函数。但是这里我们会使用jquery 1.3中的一个新特性——live event来实现这个功能:
// 动态绑定关闭tab的事件
$( ".ui-tabs-nav a.close" ).live( "click", function( e ) {
e.preventDefault();
var index = $( ".ui-tabs-nav li" ).index( $( this ).parent());
$( "#tabs" ).tabs( "remove", index );
});
简单的说,live event就是预先给某些目前DOM中可能不存在,但是未来会被动态加入到DOM中的元素绑定事件处理函数。想了解更多关于live event的信息,请参考jquery的文档。
至此,demo中的所有功能我们都已经实现了。这种实现C/S模式tab的方案,就目前来说,我觉得不是完美的,但却是可行性最强的。现实的开发中可能出现的一些其他常用需求,比如加载iframe的loading效果,实现起来都会比较容易。
在Web中实现C/S模式的Tab的更多相关文章
- Web中的积累:外观模式 Facade
摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆木匠 希望转载,保留摘要,谢谢! 壹 前言 目测好久没写文章了,距离上一篇文章也有二十多天.我是怎么了?哈 ...
- 6.在MVC中使用泛型仓储模式和依赖注入实现增删查改
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pat ...
- 数据库设计中的Soft Delete模式
最近几天有点忙,所以我们今天来一篇短的,简单地介绍一下数据库设计中的一种模式——Soft Delete. 可以说,该模式毁誉参半,甚至有非常多的人认为该模式是一个Anti-Pattern.因此在本篇文 ...
- 5.在MVC中使用泛型仓储模式和工作单元来进行增删查改
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pat ...
- 在C#代码中应用Log4Net(四)在Winform和Web中捕获全局异常
毕竟人不是神,谁写的程序都会有bug,有了bug不可怕,可怕的是出错了,你却不知道错误在哪里.所以我们需要将应用程序中抛出的所有异常都记录起来,不然出了错,找问题就能要了你的命.下面我们主要讨论的是如 ...
- java中的23中设计模式(转)
设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...
- java开发中的23中设计模式详解--大话设计模式
设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...
- Java Web的两种开发模式
参考文献:http://www.cnblogs.com/xdp-gacl/p/3908610.html 一.Jsp+JavaBean 此模式如下图所示:
- HTML5 UI框架Kendo UI Web中如何创建自定义组件(二)
在前面的文章<HTML5 UI框架Kendo UI Web自定义组件(一)>中,对在Kendo UI Web中如何创建自定义组件作出了一些基础讲解,下面将继续前面的内容. 使用一个数据源 ...
随机推荐
- activiti自定义流程之自定义表单(一):环境配置
先补充说一下自定义流程整个的思路,自定义流程的目的就是为了让一套代码解决多种业务流程,比如请假单.报销单.采购单.协作单等等,用户自己来设计流程图. 这里要涉及到这样几个基本问题,一是不同的业务需求, ...
- PLSQL_基础系列07_插入方式Pivoting / Unconditional / Conditional ALL / Conditional FIRST INSERT(案例)
2014-12-08 Created By BaoXinjian
- python (2)xpath与定向爬虫
内容来自:极客学院,教学视频: 写在前面: 提取Item 选择器介绍 我们有很多方法从网站中提取数据.Scrapy 使用一种叫做 XPath selectors的机制,它基于 XPath表达式. 这是 ...
- SteamVR Unity工具包(VRTK)之激光和移动
简单激光指针(VRTK_ SimplePointer) 简单指针(Simple Pointer)脚本从控制器尾部发出一个有色光束来模拟激光束.这在场景中指向对象很有用,它能判断所指向的对象以及对象距控 ...
- 转_Java中常用的设计模式总结
1.工厂模式:客户类和工厂类分开.消费者任何时候需要某种产品,只需向工厂请求即可.消费者无须修改就可以接纳新产品.缺点是当产品修改时,工厂类也要做相应的修改.如:如何创建及如何向客户端提供. 2.建造 ...
- 将博客搬至51CTO
为了统一博客文章,将文章搬至51cto个人博客
- web.xml配置
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" ...
- ubuntu 修改静态IP和DNS
1.修改配置文件/etc/network/interfacesroot@ubuntu:~# sudo vi /etc/network/interfaces 添加以下内容:auto eth0 ...
- 20145305 《Java程序设计》第4周学习总结
教材学习内容总结 1.子类只能继承一个父类 2.检查多态语法逻辑是否正确,方式是从=号右边往左读:右边是不是一种左边(右边类型是不是左边类型的一种子类) 3.可以使用abstract标示该方法为抽象方 ...
- C++学习44 格式化输出,C++输出格式控制
在输出数据时,为简便起见,往往不指定输出的格式,由系统根据数据的类型采取默认的格式,但有时希望数据按指定的格式输出,如要求以十六进制或八进制形式输出一个 整数,对输出的小数只保留两位小数等.有两种方法 ...