书名:jQuery EasyUI网站开发实战
ISBN:978-7-115-47602-9
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 周 菁
责任编辑 赵 轩
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
EasyUI是一套开源并基于jQuery的界面开发框架,它提供了窗口、菜单、树、数据网格、按钮、表单等一系列功能组件。EasyUI可以让很多并未系统学习过相关专业知识,但却拥有丰富行业经验的职场人士也能轻松开发出符合自身需要的管理系统。
尽管目前市场上各种前端框架“多如牛毛”,但EasyUI凭借其强大的数据交互能力,成为企业级B/S项目的开发首选。本书共分8章及一个快速入门实例,全面系统地介绍了58个功能组件的使用方法,非常适合网站开发者、大中专院校师生、培训班学员以及业余爱好者阅读。
这是一本全面系统介绍EasyUI的专业图书,同时也是网站开发者快速实现数据管理的得力助手和工具。
笔者在过去的近10年间一直都在从事报业数据分析方面的工作。2010年前后的10年可以说是报业发展的黄金十年,报社动辄花费数百万元采购一套软件,但他们仍然经常抱怨这些软件在使用中的各种不便,其中不乏一些大的品牌软件。我们本来的主业是报业数据咨询服务,听到他们的抱怨久了,就产生了一个想法:为什么我们就不能来开发一个更贴近报社使用实际的应用软件呢?和专业的开发人员相比,我们在代码能力上确实欠缺,但作为职场从业人员,却拥有着对行业深刻的理解和经验,而这正是我们最大的财富和竞争力!
说干就干。我们先用Excel开发了一套可以快速生成日报、周报、月报的“一键报表系统”,几家报社试用后反响非常好;后来又借助VBA和其他辅助工具正式升级为专业化的报业数据软件——“广告通”,在全国报业发达的珠三角地区一度成为覆盖面广的同类软件,服务的客户范围包括《广州日报》《南方都市报》《羊城晚报》等全国著名媒体及《佛山日报》《东莞日报》《中山日报》《惠州日报》等地方媒体。
后来,在帮助一家报社定制C/S数据库应用项目时,客户提出要同时支持浏览器远程访问。C/S毕竟只是在本地局域网中使用比较方便。为了接下这个预算达数十万元的“大单”,我们尽管还没有这方面的开发经验,但仍然硬着头皮先口头答应了下来。紧接着我们就到网上购买各种所谓的“从入门到精通”教材,这些书一般都是先从一个个的基础知识点讲起,最后再以一个或多个实例做综合讲解。由于B/S涉及的知识点是非常多的,这个学习的过程非常枯燥,坚持了两三个星期后根本看不到任何的“开发成果”。
怎么办?传统的老路走不通了!答应客户的事,必须要做到啊,不然数十万元的单子可能就会飞掉!我们开始上网查资料、找工具,想快速开发,最后发现了EasyUI这个框架,当时它最吸引我们的是:再也不用为那些多枯燥的CSS样式烦神了,依照自身带的各种应用实例就可以非常快地搭建好自己的B/S项目,而且拥有非常强大的后台数据交互能力。好在自己还有一些数据库方面的基础,最终不到一个月就拿出了测试版,客户非常满意。够拼吧?
项目做完之后,回头再看当时的开发过程,有一点触动很大:那就是越早体验到开发的成就感,这种坚持下去的动力就会越大。然后再仔细看EasyUI所提供的很多插件,越研究越觉得它是企业级项目开发的得力工具(毕竟当时做那个项目是依葫芦画瓢的,完全属于项目驱动、硬逼出来的),而这也正是我决定写这本书的原因。
自学编程难吗?说难不难,说易也不易,关键是要掌握里面的各种“套路”。是的,就是“套路”,一通百通!本书就是自己多年来的实战经验总结,希望能给新手或者尝试向IT方面转型的职场同仁们一点帮助。
尽管EasyUI是一款非常优秀的前端框架,它也确实可以帮助我们极大地提高开发效率,但随着近几年互联网技术的飞速发展,新的框架不断诞生(简直到了“多如牛毛”的地步),比较而言,EasyUI在样式表现方面确实有些欠缺。
比如,现阶段最热门的Bootstrap就非常侧重样式的表现,UI风格也比较符合目前的流行趋势,而且能够兼容移动端和PC端。好在,这些优秀的前端框架并不是二选一的,可以在项目中同时使用多个框架。当然,EasyUI强大的数据交互能力也是Bootstrap所无法比拟的,而这正是企业级项目应用的核心所在。
对于初学者来说,最忌讳的就是“这山望着那山高”,这个想看看,那个想研究研究。本来想跟着潮流走,无奈科技进步太快,最后的结果可能就是“什么都知道一点,但什么也都不精”!
本书全部实例均基于2017年8月底最新发布的EasyUI 1.5.3版本,源码稍作修改即可用于自己的项目中。
全书共系统介绍了58个核心组件的使用方法。其中,绝大部分代码都是在JavaScript中完成的。
以对话框组件为例,当在JS中使用此功能组件时,代码是这样的:
<div id="dlg"></div>
<script type="text/javascript">
// 这里的js代码也可以保存到js文件中,然后在script标签中通过src属性引用
$('#dlg').dialog({
title: '用户登录',
width: 290,
height: 176,
modal: true
})
</script>
除了该方式外,还可在页面中直接通过标记来使用组件。采用此方式时,该组件所有的属性和事件都应该写在该标签所对应的data-options属性中。例如:
<div id="dlg" class="easyui-dialog" data-options="
title: '用户登录',
width: 290,
height: 176,
modal: true
"></div>
本书示例代码所涉及的HTML、CSS、JavaScript、jQuery、PHP、正则表达式及数据库等方面的知识,在笔者编写的另一本书《B/S项目开发实战HTML+CSS+jQuery+PHP》中均有详细讲解,建议读者一起搭配使用。
本书源码请从异步社区(www.epubit.com.cn)本书页面下载。
最后感谢人民邮电出版社的赵轩老师对本书得以顺利出版所给予的大力支持与帮助,同时感谢家人的理解和包容,使得我可以有大量的时间来完成写作!在编写本书的过程中,尽管已数易其稿,并力求精益求精,但错误、疏漏之处仍在所难免,恳请广大读者批评指正。
周菁
2017年11月
0.1 下载并使用EasyUI框架
0.2 简单的登录窗口设计
0.3 完善登录窗口界面
0.4 用户输入验证
0.5 通过回车键快速移动光标
0.6 向服务器提交验证
0.7 用户会话控制
为简化JavaScript原生开发的工作量并解决不同类型浏览器之间的兼容问题,一些基于JS的程序库诞生了,其中最具代表性的就是jQuery,它甚至被称为JS的标配工具,可见其使用范围及影响力之广。在此基础上,又有一些高手和爱好者开发了基于jQuery的应用框架,它将一些常用的功能做了模块化的处理,原来可能需要数百行甚至上千行代码才能完成的功能,改用框架后也许只要一行代码或者一个命令即可解决。这些框架其实就是大家俗称的“二次开发平台”,它们不仅可以解决功能上的问题,也能解决CSS样式上的问题,尤其是为了保证所开发项目风格的统一,一般还会同时提供多个预设好的主题样式。
本书所学习的框架是EasyUI,就提供了数十个功能强大的组件,可解决日常项目开发中绝大多数的应用需求。据官网介绍,EasyUI项目开发小组于2009年成立,整个技术团队由大约10名软件开发工程师组成,其中核心成员有3人。自2010年面世至今,EasyUI已经走过了7~8个年头,每年至少都会更新两次,现在的最新版本为2017年8月底发布的1.5.3,本书的所有示例都是基于该版本编写的。
在正式学习EasyUI框架之前,本章将通过一个小实例来帮助大家了解如何使用EasyUI以及EasyUI的强大之处究竟在哪里。有了这样的总体概念之后,再根据自己的实际情况有针对性地学习其中的部分组件,即可最具效率地开发出自己所需要的项目。
尽管EasyUI是一种前端框架,但它提供了很多的接口用于和后台服务器进行交互。关于服务器搭建及数据库方面的知识并不是本书的重点,如果读者对此不太了解,建议参考同样由人民邮电出版社出版的另外一本书《B/S项目开发实战》。
进入EasyUI官网,如图所示。
单击“GET STARTED”按钮或者“Download”菜单,即可进入下载页面。自2017年9月份起,EasyUI在原来的jQuery版本的基础上又新推出了Angular版本,本书学习的是EasyUI for jQuery版本,因此只要单击“EasyUI for jQuery”下方的Download即可,如图所示。
进入“EasyUI for jQuery”下载页之后,又有两个按钮选项,如图所示。
其中,第一个按钮“Download”所对应的版本为Freeware Edition(免费版)。免费版虽然不是全部开源的,但在使用上却没有任何限制(仅仅是对源代码作了加密而已)。
第二个按钮“Purchase”所对应的版本为Commercial Edition(商业版),这个版本是需要付费的,目前售价为449美元。Purchase就是“购买”的意思,它不像免费版可以直接Download(下载)。既然是付费购买,肯定就会获得一些相应的服务,比如可以获取源代码、可以修改或删除文件中的版权声明、可以将修改后的软件或其一部分作为独立的应用程序进行分发等。
对于普通用户来说,免费版本已经足够使用!
文件下载完成后,建议解包到服务器所指定的网站文件夹中。解包后的文件夹可使用easyui或其他英文名称,但一定不能用中文。解包后的目录结构如图所示。
demo和demo-mobile为示例文件夹,仅供了解各个组件的功能。正式发布项目时,这两个文件夹可以不用上传到服务器。这些demo全部是html格式的,可在浏览器上直接访问(demo-mobile是移动端的演示文件)。
locale用于设置语言环境。例如,如果希望将界面做成简体中文效果,可以在项目中直接引用该文件夹下的easyui-lang-zh_CN.js文件。
plugins包含了全部组件的JS处理程序。
src包含了部分组件的JS开源程序。
themes包含了各种主题资源文件,如图标、主题样式等。
除了上述几个文件夹之外,解包后的easyui目录中还有以下几个重要的文件(所有的txt文件都是用来做文档说明的,可以直接删除或忽略)。
【jquery.min.js】EasyUI是一组基于jQuery的组件集合,因此,要使用EasyUI,就必须先加载jQuery。这个文件就是jQuery的核心库(版本为1.11.3)。
【jquery.easyui.min.js】这个是EasyUI的核心库文件。
【easyloader.js】这是在项目中使用EasyUI组件的另外一种加载方式:智能加载(也称为简单加载)。该加载方式在实际应用中很少使用,可以忽略。
【jquery.easyui.mobile.js】这个是移动端项目应用时的核心库文件。
新建一个页面文件(例如:test.html),在head中引入必要的EasyUI框架文件。代码如下:
<script type="text/javascript" src="easyui/jquery.min.js"></script>
<script type="text/javascript" src="easyui/jquery.easyui.min.js"></script>
<script type="text/javascript" src="easyui/locale/easyui-lang-zh_CN.js"></script>
<link rel="stylesheet" type="text/css" href="easyui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="easyui/themes/icon.css">
请注意,上述引用文件的路径默认都是easyui。如果你在解包时改变了文件路径名称,请务必以实际为准!建议和上述设置保持一致,以方便测试本书提供的各种源代码。
EasyUI提供了相当全面的主题样式及配色风格,我们可以在代码中非常方便地使用它们。这些都保存在themes文件夹中,其目录结构如下图所示。
项目主题样式是由所引用的easyui.css文件确定的,该文件保存在themes文件夹中。例如,之前代码所引用的就是default默认样式,如图所示。
如果需要更换为其他样式,修改文件夹名称即可(引用的样式文件名称无须修改)。
在themes文件夹中,除了icons保存的是各种图标文件外,其他6个子目录分别对应了以下主题样式:default、gray、metro、material、bootstrap和black。每个主题下又包含有相应的CSS文件和按钮图片。其中,easyui.css包含所有的组件样式,其他的CSS文件则仅仅是某个组件的样式。
例如,要将默认的dafault改成bootstrap样式,可将上图所示的文件夹名称改为bootstrap。
各种主题样式风格如图所示。
注意
本截图仅以一个对话窗口为例来说明不同样式之间的区别,更多的细节还会体现在菜单、表格、目录树等其他组件上。以下扩展主题样式与此同。
除了上述6种基本的主题样式外,EasyUI还提供了两大系列的扩展主题样式。
此类扩展样式共有4种,我们在源代码中已经提供,具体存放在themes中的ext_jqui文件夹,可直接引用。例如:
<link rel="stylesheet" type="text/css" href="easyui/themes/ext_jqui/ui-sunny/easyui.css">
各样式效果如图所示。
此类扩展样式共有5种,我们在源代码中已经提供,具体存放在themes中的ext_metro文件夹,可直接引用。例如:
<link rel="stylesheet" type="text/css" href="easyui/themes/ext_metro/metro-blue/easyui.css">
各样式效果如图所示。
EasyUI提供的全部主题样式如图所示。
在themes文件夹中,有3个独立的CSS样式文件:color.css、icon.css和mobile.css。其中,mobile.css是移动端的样式文件,本书侧重于传统PC端的页面开发,此文件暂用不到;color.css则是颜色样式文件,它和主题样式相配合,可打造出更具个性化的应用系统。
该样式文件提供了8种默认的颜色样式,分别为c1~c8。样式效果如图所示。
如需使用颜色样式,必须在页面文件中先引用color.css文件,然后在相关组件的属性中设置即可。
例如,先在页面中引用:
<link rel="stylesheet" type="text/css" href="easyui/themes/color.css">
然后仍然使用默认的default主题样式,但在对话框组件中通过属性设置使用了c1颜色样式,运行效果如图所示。
需要注意的是,并不是所有的组件都可以使用颜色样式,使用的方法也可能不同。这个在后面学习到每个具体的组件时会有详细说明。
颜色样式并非是必须要用的,因而在之前引入必要的EasyUI框架文件时,并未将此项列入。
注意到我们前面所发截图中的图标了么?在EasyUI中,所有的图标都是通过themes文件夹中的样式文件icon.css来进行管理的,具体的图标文件则保存在icons文件夹中。
icon.css文件在之前的示例代码中已经引入,且必须引入:
<link rel="stylesheet" type="text/css" href="easyui/themes/icon.css">
那么,该icon.css文件是如何管理图标的?先用代码编辑器打开该CSS文件来看一看。代码如下:
由此截图可以发现,图标文件都默认保存在icon.css所在文件夹的icons子目录中,每个图标文件都被重新声明了一个class样式名称。
如果对EasyUI自带的图标不满意,也可以自己扩充。例如,上述截图中的第67~72行的图标就是我们自己另行增加的:首先把要扩充的两个图标文件lock_open.png、key_go.png复制到icons文件夹,然后在这个CSS文件中设置好文件路径,重新定义样式名即可。
当然,你也可以不用把图标文件放到默认的icons中,但在这里设置样式时,就要注意图标文件的路径。例如,我们把上述两个新增的图标文件放到与easyui同级的images文件夹时,代码就要这么写:
那么,项目中如何使用图标?在EasyUI中,每个需要用到图标的组件都会自带一个iconCls属性,直接将想用的图标文件所对应的class类名称作为值赋给该属性即可。至于怎么赋值,先别着急,接下来的内容就会带你快速入门。
请注意,icon.css仅仅用来管理项目开发过程中所用到的图标,它和主题中的图标是两回事。例如,窗口的最大化、最小化、关闭图标,数据表格中的翻页图标、消息框中的警告图标、目录树中的打开节点或关闭节点图标等,这些都是由所选择的主题样式决定的,主题样式不同,这些主题类的图标也不一样,它们都保存在相应主题下的images文件夹中。
上一节中的各种效果图都是使用的登录窗口,如图所示。
在这个登录窗口中,正常的使用场景是这样的:用户先输入名称和密码,单击“登录”按钮后即向后台服务器提交这两项数据。后台程序经过数据库检索,如果这两项数据都匹配,则通过登录,然后打开项目主界面;否则给出错误提示并拒绝登录。
很显然,这个功能看起来虽小,但涉及的知识面却五脏俱全:登录界面要用HTML和CSS设计、单击按钮产生的互动动作要用JS处理、把数据发送到后台并返回结果需要用到AJAX技术、后台数据库检索匹配时则要用服务器端的语言(比如PHP)进行处理。看起来很复杂的样子,但实现起来仅需100行左右的代码而已(所有代码都有详细说明,初学阶段先依葫芦画瓢即可)。
那么,这个窗口是如何实现的呢?我们知道,HTML中凡是需要向浏览者输出的信息,都必须写在body中。body写入相应代码后的完整截图如图所示。
现对以上代码逐条解析如下。
第1行的<!DOCTYPE html>是用来声明解析类型的,它告诉浏览器这个页面是使用HTML编写的,因此必须放在所有代码的第一行。DOCTYPE可以大写,也可以小写。
第2~23行是真正的HTML页面代码,所有的代码内容都被包含在<html></html>这对标签中。其中:
第3~12行是用head标签包裹起来的头部元素,第13~22行是用body标签包裹起来的身体元素,它们都是HTML下面的子元素,是构成HTML最重要的两大组成部分。
关于头部元素中引用的各个EasyUI框架文件,上一节已经讲得很详细了,现在来重点分析身体元素中的代码。
就是代码中的14~17行。
div是一个没有任何语义的双标签,相当于一个容器,在这个双标签里可以放置任何内容。此实例代码中,就放置了两个p元素。
在这个div的开始标签中,分别设置了三个属性:id、class和style。
id属性用于设定指定标签元素的唯一标识符(在同一个页面内,不可与其他元素的id重复),这个标识符将大大方便其他程序对该元素的操作。
class属性用来给标签元素进行归类,同一个页面内的多个标签可以使用相同的class名称,它一般用于对指定标签设置CSS样式。如本例中,class的属性值为easyui-dialog,表示这个div容器将以easyui中的对话框样式进行显示。该样式都是事先定义好的,并保存在easyui.css文件中,示例代码的第9行已对此文件进行了引用,因此这里直接指定CSS的样式名称即可。
style属性和class属性的作用有点类似,也是用于设置标签元素的CSS样式,只不过这属于行内样式的写法,一般用于对样式的微调。如本例中,将div的padding(内边距)上下设置为10px、左右设置为20px,以避免容器内的两个输入框和边界靠得太紧。
在这个div中,放置了两个p元素,每个p元素里面又分别放置了一个input元素。其中,input属于表单元素,用于生成输入框。之所以在每个input外面又包裹了一个p,同样是为了避免两个输入框靠得太紧。因为p表示的是段落,段落之间会自动留有较大的间距。
这里有个关键:分别给两个input输入框设置了id属性,便于在后面的JS程序代码中对输入的内容进行验证操作!
就是代码中的18~20行。
这里除了给该div设置了一个id属性外,还设置了另外一个style属性:text-align。该属性有3个常用的可选值:left、right、center,分别表示靠左、靠右和居中对齐。该属性在这里的作用是,可以使按钮居中。
在该div中,放置了一个button标签按钮,以方便用户单击登录。
就是代码中的第21行。
该行代码通过script标签用来加载控制用户操作的JS程序文件。JS文件可以放在head、body中的任何位置,关键是看执行时机,因为页面代码都是自上而下执行的。
除了这种外部文件加载的方式外,JS程序代码也可以直接写在script双标签中。为了使程序代码更加条理和清晰,本实例就采用了外部文件加载的方式。这里的JS程序文件名称可以自定,但必须以JS为扩展名。
script虽然也是双标签,但由于这里直接通过src指定了程序文件,因此,这个双标签之间可以不用写任何内容;即使要写的话,也应该是JS程序代码。
由于JS程序代码目前尚未用到,截图中的代码已经将该行注释掉。
将HTML文件拖拽到浏览器中运行,效果如图所示。
虽然登录按钮仍然游离于窗口之外,但通过这个很小的实例可以发现,整个过程中并没有设置窗口的颜色,更没用到任何非常复杂的CSS样式代码,甚至连窗口上的关闭图标都没准备,却直接就实现了这样漂亮的窗口效果。而且,这个窗口可以拖拽移动哦!
当然,这个窗口还有很多不足,留待后面继续完善。
之前仅仅是使用HTML和一点点的CSS设计好了基本的用户登录窗口,但还有个问题没有解决—“按钮”仍然游离于登录窗口之外,现在首要的工作是将它拉回到登录窗口中。
要将“按钮”拉回到“窗口”中,必须要先指定相关的DOM元素,也就是使用选择器。
先看一下body中的代码:
<div id="dlg" class="easyui-dialog" style="padding:10px 20px">
<p>用户名称:<input id="user"></p>
<p>登录密码:<input id="password" type="password"></p>
</div>
<div id="btn" style="text-align: center;">
<button>登录</button>
</div>
上一个div元素的id属性为dlg,它对应的是“窗口”;下一个div元素的id属性为btn,它对应的是“按钮”。要将“按钮”拉回到“窗口”中,就要先选择它们。
由于它们都有id属性,要在JS程序中选择这些元素,使用id选择器是最简单的。例如:
$('#dlg')
这样一来,id为dlg的DOM元素就变成了jQuery中的对象。因为EasyUI是基于jQuery的,必须将要操作的DOM元素转为jQuery对象,才能使用EasyUI组件中的属性、方法或事件。
先将HTML中第21行的JS程序行取消注释,也就是让该行代码生效,然后再来编写JS程序代码:
$(function(){
$('#dlg').dialog();
})
其中,最关键的就是第2行代码。该代码的意思是,对id为dlg的元素对象进行操作;后面紧跟的dialog()表示对这个对象应用对话框的组件效果。dialog()也可以理解为jQuery对象的方法,该方法只有在引用了EasyUI中的核心程序库之后才会有效。
为了查看该方法作用后的效果,我们先将页面文件中对应的dialog样式去掉,也就是将第14行的代码改为:
<div id="dlg" style="padding:10px 20px">
浏览器刷新后,生成的效果与之前在页面中使用easyui-dialog样式时的效果完全相同。这就表明,dialog()方法已经起到作用了。
请注意:上述3行JS代码中,首尾两行代码的意思是:包含在其中的事件代码只有在页面文档准备好之后才会执行。关于这方面的基础知识,建议参考笔者编写的另一本书《B/S项目开发实战》。
如本例,如果将首尾两行删除,仅保留第2行,同时又将页面中的dialog样式去掉,则运行效果会出现很明显的瑕疵,如图所示。
经测试,EasyUI的数十个组件中,只有少数几个会出现这样的问题。因此,为安全和保险起见,还是将代码写在$函数中为好。
当对jQuery对象使用EasyUI框架提供的各种组件方法时,是可以带参数的。只不过这里的参数全部是数据对象,一般用于设置属性和事件代码。例如:
$('#dlg').dialog({
title: "用户登录", //对话框标题
width: 290, //宽度
height: 176, //高度
modal: true, //模式窗口
buttons: '#btn', //绑定按钮
iconCls: 'icon-open', //设置窗口图标
cls:'c1', //使用颜色样式c1
});
请注意,JS代码的写法。以上代码仅仅只是一条语句而已,虽然可以写成一行,但为了代码的易读性,一般都是换行书写。在JS中,语句请以分号结束(尽管不像PHP那样强制要求,最好还是养成良好的编码习惯)。
其中,buttons属性用于绑定指定的标签元素,属性值可以是数组(array)或选择器(selector)。这里是用选择器将按钮从外面给“绑”了回来。
如此设置之后的运行效果如图所示。
上述代码中,参数对象必须用花括号{}包起来。参数对象里面如果有多个键值对,则它们之间要用逗号分开。
用户在输入内容时,往往会进行一些限制:比如,至少需要多少个字符、不能输入哪些字符等。要给编辑框增加验证功能,就需要使用validatebox组件。
用户名称编辑框在页面中对应的标签元素id是user,要对输入的内容进行验证,依然是3步走:选择、转为对象、使用方法。使用方法的同时可以设置属性和事件:
$('#user').validatebox({
required: true,
validType : 'length[5,10]'
});
其中,属性required设置为true,表示该编辑框必须输入,不能为空;
属性validType设置为length[5,10],表示输入的字符长度在5~10之间。
例如,当没有输入内容时,将给出提示,如图所示。
同样,如果输入的字符长度不在指定的5~10之间,也会给出提示,如图所示。
密码编辑框的输入验证同理,示例代码如下:
$('#password').validatebox({
required: true,
validType : 'length[5,10]'
});
运行效果与上同。
登录验证实际上包含两个部分:一是客户端输入的用户名和密码是否符合验证规则,二是符合规则之后再提交到服务器进行后台验证(是否存在此用户名、密码是否正确)。
现在我们先来实现输入规则的验证。
由于页面中只有一个button标签元素,因此我们可以通过标签选择器来指定它,写法如下:
$('button')
在EasyUI中,有个功能强大的linkbutton组件是专门用来处理按钮的,代码如下:
$('button').linkbutton();
在使用linkbutton方法的同时,还可设置参数对象。例如:
$('button').linkbutton({
width: 60,
iconCls: 'icon-key'
});
上述代码的意思是,将该按钮的宽度设置为60,同时添加一个图标按钮。运行效果如图所示。
这样处理之后,登录按钮确实漂亮多了!
既然要单击按钮,肯定要触发单击事件。在jQuery中,单击事件名称为click,常规写法如下:
$('button').click(function(){
事件代码
});
当然,我们也可以采用jQuery所特有的链式写法,将单击事件代码直接跟在linkbutton方法的前面或后面。例如:
$('button').linkbutton({
width: 60,
iconCls: 'icon-key'
}).click(function(){
alert('您点击我了!');
});
除了使用jQuery中的单击事件外,linkbutton本身还自带了onClick事件类型,也就是在单击按钮的时候触发事件。为了让代码看起来更简洁,我们可以直接将规则验证代码写在这个onClick事件中。
如下所示:
$('button').linkbutton({
width: 60,
iconCls: 'icon-key',
onClick: function(){
if (!$('#user').validatebox('isValid')) {
$('#user').focus();
} else if (!$('#password').validatebox('isValid')) {
$('#password').focus();
} else {
$.messager.progress({
text : '正在登录中...',
});
}
}
});
请注意,参数对象中的事件代码写法:必须是function类型,其写法为function(){}。如果触发的事件带有参数,参数就写在圆括号里,否则就为空;花括号里为要执行的事件代码。
以上事件代码中,用户名和密码都是使用validatebox的isValid方法,该方法有个返回值:验证通过时返回true,未通过返回false。上述代码的意思为:当验证通过后,就继续执行后面的代码;否则将输入焦点仍然保持在当前编辑框,不得离开。
用户名和密码都符合验证规则后,将执行else中的代码,这里先临时弹出一个messager消息窗口。messager同样是EasyUI中的一个组件,用于弹出消息。该组件有多个方法,可以弹出不同风格的消息框风格,如:alert(警告框)、confirm(确认框)、prompt(提示框)、progress(进度框)等。本代码使用的是progress方法,用于弹出一个登录进度框,如图所示。
注意
如何使用EasyUI组件中的方法?上述示例代码表明,如果组件本身没有需要的事件,可以使用jQuery的;和事件一样,如果组件本身没有的方法,同样可以使用jQuery的。
例如,让某个对象获取输入焦点,EasyUI组件并没有提供相关的方法,因此就直接使用了jQuery的focus方法。如:
$('#user').focus();
其实,通过这个代码也能很容易地看出来,由于代码中没有用到相应的组件名称,所以focus肯定是jQuery中的方法。如果是组件自带的方法,必须加上组件的名称。例如:
$('#user').validatebox('isValid');
这就是调用EasyUI组件方法的方法。语法为:
$('selector').plugin('method', parameter);
其中,selector为对象选择器,plugin是组件名称,method为调用的组件方法名,parameter表示调用参数。至于哪些方法可以带参数,哪些可以不带参数,后面学习具体的组件时还会有详细的说明。
以上面的代码为例,它执行的就是validatebox中的isValid方法。
对于很少接触B/S项目应用,特别是一直钟情于单机版或C/S桌面程序的用户来说,他们一般都会想当然地认为B/S类型的软件项目都是只能用鼠标操作的,其灵活性肯定很差,尤其是在数据输入时,其效率更是低下。我们无意于在此评价孰优孰劣,毕竟都是企业级的应用,将两者有效地结合起来,针对不同的场景来部署不同的项目才是真正的提高效率之道。
现以用户登录窗口的键盘操作为例,来简单探讨一下B/S项目中的快速移动光标问题。
默认情况下,B/S项目中按Tab键可以下移光标,按Shift+Tab键上移光标,按回车键则没有反应。假如,我们希望在输入用户名之后,回车即可将光标移动到密码处;密码输入完再回车,即可将光标移动到登录按钮。如何实现呢?稍微有点编程经验的人都知道,这个功能应该可以通过触发“按键”事件来实现。
但是,我们翻遍EasyUI中所有和编辑框相关的组件资料,都没发现类似的事件名称。
其实,EasyUI是基于jQuery的;而在jQuery中,每当用户按下键盘上的任何键时,都会触发keydown事件,我们可将代码写在这个事件中。例如:
$('#user').keydown(function(e) {
if (e.keyCode == 13) {
$('#password').focus(); //如果在用户名编辑框上回车,就将焦点移动到密码上
}
});
$('#password').keydown(function(e) {
if (e.keyCode == 13) {
$('button').focus(); //如果在密码编辑框上回车,就将焦点移动到按钮上
}
});
以上代码先在用户名称编辑框上判断:如果按下的键是回车键(参数e表示事件对象,它代表着事件状态,这里表示按下了键盘中的任意键,keyCode用于返回所按下键的键值,回车键的值为13),则光标焦点移到密码上;如果在密码上按回车,则焦点移到登录按钮上。如果对组合键进行判断,代码可以这样:
if (e.keyCode == 13 && e.shiftKey) {}
这个代码判断是否在按下回车键的同时也按下了Shift键。注意,shiftKeyKey中的Key第一个字母要大写!
在jQuery中,还有很多和keydown一样的类似事件。由于这些事件很常用,也很基础,EasyUI就没有把它们一一封装进去(除非需要传递一些特别的参数)。因此,对于新手来说,千万不要把jQuery中的事件和EasyUI组件里的事件混淆了。
以EasyUI中的validatebox为例,该组件仅有两个和验证相关的事件,keydown、click之类的就完全没有涉及。当我们在项目中需要用到这些事件时,可直接使用jQuery的,因为EasyUI就是基于jQuery的应用框架,不会存在任何问题。例如,在0.4.3中,我们用到了linkbutton的onClick事件,由于这个事件就是它本身自带的,因此可以写到参数对象中;而keydown却不是,如果也将它写到validatebox的参数对象中,就是无效的,例如:
$('#user').validatebox({
required: true,
validType : 'length[5,10]',
keydown: function(e) {
…这里的代码不会被执行,因为keydown不是该组件中的事件…
},
});
如果一定要将这个keydown和组件中的其他参数代码写在一起,可以采用jQuery的链式写法直接跟在后面,代码写法如下:
$('#user').validatebox({
…该组件的参数对象代码…
}).keydown(function(e) {
…keydown事件代码…
});
其实,这样的代码看起来更清晰:validatebox是使用验证组件对用户名编辑框进行验证操作的,keydown是应用于该编辑框的键盘按下事件,两者是并列的关系。当然,将keydown事件放在validatebox前面也是可以的。
本例中,用户名输入框和密码输入框都使用了回车下移的操作,如果想再上移呢?由于这两个对象的事件代码在逻辑上都是差不多的,如果分别给它们再加上其他类似的代码,这不仅会造成程序代码的冗长,最主要的是以后维护起来不方便。
现在我们将事件代码写成一个函数,然后再调用即可,这样就可以实现同一个代码的重复利用,使程序看起来更加简洁。例如:
function keymove(e,next,up) {
if (e.keyCode == 13) {
$(next).focus();
};
if (e.keyCode == 13 && e.shiftKey) {
$(up).focus();
};
}
这个函数的名称为keymove。使用该函数时要传入3个值:e表示事件对象,next表示回车后要移动到的对象名,up表示按下Shift+Enter组合键后要移动到的对象名。
如果将之前的keydown事件代码改用函数调用的方式,代码如下:
$('#user').validatebox({ //用户名编辑框
…参数对象代码略…
}).keydown(function(e){ //这里的e是keydown传来的event参数
keymove(e,'#password','button'); //调用keymove函数,传3个值
});
$('#password').validatebox({ //密码编辑框
…参数对象代码略…
}).keydown(function(e){
keymove(e,'button','#user'); //调用keymove函数(传3个值)
});
请注意,keymove函数中的e参数是通过keydown事件传过来的,所以keydown事件中的e参数一定不能省略。
很显然,由于用户名和密码都要设置按键操作,因此使用函数方式大大减少了代码量。以用户名编辑框的keydown为例:如果按回车键,焦点将移动到password输入框;如果按“Shift+回车”组合键,焦点将移动到button也就是登录按钮。
之前学习的内容都是表现在前端的。现在到了非常关键的部分:在单击“登录”按钮并经过必须的输入规范验证之后,还要向后台服务器提交“用户名”和“密码”两项数据,后台程序再经过数据库检索,然后向前端返回一个结果,以便作进一步的处理。如果两项数据都匹配,则通过登录,然后打开项目主界面;否则给出错误提示并拒绝登录。
对于这种与服务器进行数据交互的操作,就需要用到AJAX技术(EasyUI组件本身也有一个表单提交组件,但实际更常用的是AJAX)。
请注意,由于这里需要使用AJAX访问服务器,因此用户测试代码时务必在服务器环境中测试,并使用类似于localhost或127.0.0.1的方式访问test.html文件。
用户单击“登录”按钮后的完整JS程序代码如下:
$('button').linkbutton({
width: 60,
iconCls: 'icon-key',
onClick: function(){
if (!$('#user').validatebox('isValid')) {
$('#user').focus();
} else if (!$('#password').validatebox('isValid')) {
$('#password').focus();
} else {
$.ajax({
url : 'login.php',
type : 'post',
data : {
user : $('#user').val(), //val表示用户编辑框中的值
password : $('#password').val(), //val表示密码编辑框中的值
},
beforeSend : function () {
$.messager.progress({
text : '正在登录中...',
});
},
success : function (data) {
$.messager.progress('close');
if (data == 0) {
$.messager.alert('警告', '用户名或密码错误,请重新输入!',
'warning',function (){
$('#password').select(); //未通过时默认选中密码框
});
} else {
location.href = 'main.php';
}
}
});
}
}
});
上述代码中的加粗部分,就是使用AJAX方法请求数据的完整代码。在这里共使用了5个参数,分别对请求的地址、请求类型、要发送的数据、请求前以及请求成功后要执行的回调函数进行了设置。
对于要发送的数据,这里使用了jQuery的val方法,用于获取指定对象的值。
由于在请求之前先使用messager的progress方法弹出了一个进度框,因此在请求成功之后,必须先关闭这个进度框,然后再根据服务器的返回值进行判断:如果返回值为0,则使用messager的alert方法弹出一个警告框;否则,直接通过位置对象location的href属性跳转到main.php主界面。
在用messager的alert方法弹出警告框时,可以带4个参数:警告框标题、警告内容、警告框图标类型及回调函数。比如这里的回调函数,就是关闭警告框时,自动选择“密码”以方便用户重新输入。
在上面的登录按钮代码中,AJAX请求的URL地址为login.php,这个是运行于服务器端的程序,专门用于接收客户端发来的数据并在处理后给出返回值。上述客户端的JS程序中,AJAX的success事件就可获得这个返回值。
那么,PHP程序是如何生成这个返回值的?完整代码如下:
//获取客户端数据并生成sql查询字符串
$user = (!empty($_POST['user'])) ? $_POST['user'] : '';
$password = (!empty($_POST['password'])) ? $_POST['password'] :'';
$sql = "select count(*) as hs from user where user='$user' and password='$password'";
//连接数据库
$dsn = 'Driver={Microsoft Access Driver (*.mdb)};DBQ='.realpath('test.mdb');
$link = odbc_connect($dsn,'',''); //后面两个参数为用户名和密码,都为空
//执行检索
$result = odbc_exec($link,$sql); //执行SQL语句,得到结果集
$rows = odbc_fetch_array($result); //读取结果集中的数据,返回值为数组
//将需要的值输出并返回到客户端
echo $rows['hs']; //返回值为字符串形式的数字。未指定header时,默认是text/html
这个PHP程序连接的是Access数据库,代码非常简单,无需再做什么解释。
服务器端用到的user数据表内容如下,请按该表中的内容输入用户名和密码。
当输入的用户名和密码与此不匹配时,登录窗口将弹出错误提示,如图所示。
如顺利匹配,则打开系统主页面main.php,如图所示。
到目前为止,这个用户登录的项目看起来是已经完成了。但实际上这里存在一个非常大的隐患:假如用户通过登录窗口进到系统主界面之后,浏览器的URL地址栏显示主界面所对应的文件为main.php,那么,TA以后就可以完全跳过登录窗口,直接在URL上输入该文件的地址进行访问,这样就会产生一系列的问题!
因此,对于一个完整的登录项目而言,还应该加上会话控制功能。
所谓的会话控制,简单地说就是在用户访问同一个网站的多个页面时,它能够通过一个唯一的会话ID来跟踪这个用户,从而判断在这个会话的生命周期中所访问的多个页面是否来自于同一个用户。
现在我们来通过PHP处理会话问题。
首先,将用来请求验证数据的login.php程序文件中的最后一行代码作以下修改:
$hs = $rows['hs'];
if ($hs !== 0) { //如果不为0,表示检索到此用户,则创建会话
session_start();
$_SESSION['user'] = $user;
};
echo $hs;
该代码意思为,先将要返回到客户端的值保存到变量中,然后对这个值进行判断:如果不为0,表示检索通过,接着用session_start()创建一个会话,同时将用户名数据保存在全局数组变量$_SESSION中;如果为0,表示检索未通过,不作任何处理。不论是否通过,最后仍然将值输出,并返回给客户端。
当登录窗口页面通过AJAX对login.php请求数据成功后,又对获取的返回值作如下判断:
success : function (data) {
if (data == 0) {
$.messager.alert('警告', '用户名或密码错误,请重新输入!', 'warning',function(){
$('#password').select();
});
} else {
location.href = 'main.php';
}
}
很显然,当返回值不等于0时,将执行跳转,跳转的页面为main.php。为防止用户未经登录强行进入主界面,main.php中也必须加入以下的会话控制代码:
<?php
session_start();
if (!isset($_SESSION['user'])) { //或者改用:if(empty($_SESSION['user']))
header('location:test.html'); //接着强行跳转到test.html,让用户登录
}else{
$str = '【当前登录用户】 '.$_SESSION['user'].' || 南京码上汇信息技术有限公司 · 版权所有';
}
?>
该代码的意思是,如果全局数组变量$_SESSION中不存在键名为user的值(或者为空),则表明这个页面不是通过登录窗口打开的,属于非法访问,那么就直接强制跳回到登录页面让用户重新登录;如果该值存在,说明是正常登录,那么就生成一个登录字符串,以便在页面的指定位置显示。
经过以上登录验证和主页两个方面的处理,就可有效避免用户绕过登录窗口而导致的安全问题。
既然可以允许会话,那么也应该可以结束会话。这就需要在主页面中增加一个“退出”按钮,我们接着再来修改main.php中的代码。
修改之前,先看一下源代码的内容结构:
<?php
session_start();
if (!isset($_SESSION['user'])) {
header('location:test.html');
}else{
$str = '【当前登录用户】 '.$_SESSION['user'].' || 南京码上汇信息技术有限公司 · 版权所有';
}
?>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>职场码上汇</title>
…引用的easyui文件代码略…
</head>
<body class="easyui-layout">
<div data-options="region:'north',height:75">
<img src="images/logo.png" style="height:60px;margin:6px 0 0 20px;">
</div>
<div data-options="region:'west',split:true,title:'资源导航',width: 150,"></div>
<div data-options="region:'south',height: 30," style="padding: 6px;color:#706F6F;">
<div style="float:left"><?php echo $str ?></div>
<div style="float:right">苏ICP备17025178号</div>
</div>
<div data-options="region:'center'"></div>
<script type="text/javascript" src="main.js"></script>
</body>
</html>
上述代码中,加粗的部分是php代码,其他都是html代码,PHP中的内容是可以输出并嵌入到html中的,例如:<div style="float:left"><?php echo $str ?></div>
。
重点来看body部分:整个body使用了EasyUI的layout布局组件,也就是该组件应用之后将铺满整个屏幕。按照布局中的region属性“上北、下南、左西、右东”顺序,该页面分为north(上)、west(左)、south(下)和center(中心区域),它们分别对应body里的4个父级div元素。
请注意,当使用layout布局组件时,一般都是在html标签中直接通过data-options属性来对不同方位的区域面板进行设置。这主要是因为,布局组件需要分多个区域面板,如果统一将代码写在JS程序中,那就要给每个区域(div)再设置不同的id或class属性;这样设置之后,JS中才能找到指定的div,然后再给其设置region等属性。每个div都这样设置的话,确实就会比较麻烦,远不如直接在页面中使用标签的data-options属性方便!
由此实例可知,EasyUI的组件属性设置,既可以写在页面标签的data-options属性中,也可以写在JS程序中。至于具体写在哪里,完全看便利性以及自己的编码习惯。关于这些组件的使用,后面将做专门讲解,现在重点来解决上述代码中的“退出按钮”问题。
先看一下上述代码的运行效果,如图所示。
其中,底部区域面板中的登录信息就是使用PHP输出并嵌入到页面中的。
按照常规的网页开发习惯,用户登入或登出的按钮一般放在右上角,那就先在region为north的上方区域中加上a标签元素,该元素和logo图片同在一个区域中。代码如下:
<div data-options="region:'north',height:75">
<img src="images/logo.png" style="height:60px;margin:6px 0 0 20px;">
<a>退出登录</a>
</div>
浏览器试运行,变成这副模样,如下图所示,这个肯定不是我们需要的。
现在我们在页面所引用的mian.js程序中对它应用linkbutton组件效果并设置样式,代码如下:
$('a').linkbutton({
width: 80,
iconCls: 'icon-man', //给按钮加个小人图标
plain: true,
onClick: function(){
$.messager.confirm('退出','您确定要退出登录吗?',function (r) {
if (r) location.href = 'logout.php';
});
},
}).css({
margin:'30px 18px 0 0', //外边距
color:'#1072F7', //颜色
float:'right' //靠右
});
上述代码的意思是,先对a元素应用linkbutton组件效果,让它变成一个按钮;然后使用jQuery中的CSS方法设置它的样式。其中,在初始化按钮时,单击事件中再次使用了messager组件,这次使用的是confirm方法,可弹出确认框让用户做出选择。如果确定退出,就跳转到logout.php页面。
运行效果如图所示。
当单击“取消”或对话框关闭按钮时,不做任何操作直接返回主界面;单击“确定”时,将跳转到logout.php页面执行会话销毁操作。
logout.php的代码非常简单,具体如下:
<?php
session_start();
session_unset(); //注销会话
session_destroy(); //销毁会话
setcookie(session_name(),'',time()-1,'/'); //客户浏览器端的Cookie标识立即失效
header('location:test.html'); //重新跳转到登录窗口
?>
之前所采用的用户登录或退出方式,都是企业级项目应用的典型做法。但对于门户或资源型的网站,一般都不是先弹出登录窗口的,而是直接打开主页。对于一些公开的新闻或信息,任何人都可直接访问;只有在浏览受限制的内容时,才会要求用户登录。
对于这种类型网站的用户登入、登出设计,其实也不复杂。以main.php为例,同样也要先判断会话,代码如下:
if (!isset($_SESSION['user'])) {
$user = '请登录';
}else{
$user = '当前登录用户:'.$_SESSION['user'];
}
$str = '【'.$user.'】 || 南京码上汇信息技术有限公司 · 版权所有';
上述代码先获取登录的用户名。如果没登录,就设置为“请登录”。
然后,将原来单一的“退出登录”按钮改成动态的(原来是用户必须先登录才能打开主页面,而现在没这个要求,因此,用户可能登录了,也可能没登录)。
原来只是一个简单的a标签:<a>
退出登录</a>;
要改成动态的,则需要用PHP输出,代码如下:
<a><?php
echo ($user=='请登录') ? '用户' : '退出';
?>登录</a>
其中,文字加粗的部分是PHP代码,该代码使用echo输出内容:如果用户名为“请登录”,则值为“用户”;否则,值为“退出”。再加上后面固定的“登录”二字,以上代码的意思如下:
当用户没有登录时,按钮文字显示为“用户登录”;
当用户已经登录时,按钮文字显示为“退出登录”。
当然,仅仅将按钮显示的内容变成动态的还不行,具体操作也要修改。以下是修改后的单击事件代码:
onClick: function(){
var str = $(this).text(); //获取标签元素内容,赋值给变量str
if ($.trim(str) == '用户登录') {
location.href = 'test.html';
}else{
$.messager.confirm('退出','您确定要退出登录吗?',function (r) {
if (r) location.href = 'logout.php';
});
}
},
以上代码的意思是,当用户单击按钮时,首先使用jQuery的text方法获取按钮的内容,然后再根据内容决定弹出“登录”页面还是“退出”窗口。
当然,为了项目的整体效果和良好体验,采用这种方法对会话进行控制时,登录窗口不能是一个单独的页面,应将其放到项目首页中。关于这方面的修改就不讲了,无非是将原来HTML、JS中用到的代码分别复制过来再适当修改一下而已,具体请查看源文件代码。
运行效果如图所示。
1.1 panel(面板)
1.2 Tabs(选项卡)
1.3 accordion(分类选项卡)
1.4 layout(布局)
面板作为承载其他内容的容器,是构建其他组件的基础,它可以很容易地嵌入到Web页面的任何位置,同时还提供了折叠、关闭、最大化、最小化等各种行为。
panel中的属性非常多,本着由浅入深的原则,分别介绍如下。
属性名 |
值类型 |
描述 |
默认值 |
---|---|---|---|
collapsed |
boolean |
是否在初始化的时候折叠面板 |
false |
minimized |
boolean |
是否在初始化的时候最小化面板 |
false |
maximized |
boolean |
是否在初始化的时候最大化面板 |
false |
closed |
boolean |
是否在初始化的时候关闭面板 |
false |
width |
number |
面板宽度 |
auto |
height |
number |
面板高度 |
auto |
id |
string |
一般无需重新设置 |
null |
title |
string |
在面板头部显示的标题文本。 |
null |
noheader |
boolean |
如果设置为true,将不会创建面板标题 |
false |
collapsible |
boolean |
是否显示可折叠按钮 |
false |
minimizable |
boolean |
是否显示最小化按钮 |
false |
maximizable |
boolean |
是否显示最大化按钮 |
false |
closable |
boolean |
是否显示关闭按钮 |
false |
header |
selector |
指定标题内容。注意: |
null |
footer |
selector |
指定页脚内容。当面板折叠时,页脚仍能正常显示 |
null |
halign |
string |
允许指定面板头部放置在左侧还是右侧。 |
null |
titleDirection |
string |
当面板头部放置在左侧或右侧时,标题文本的显示方向。可选值:up、down |
down |
例如,页面中的body只有如下代码:
<body>
<div id="p" class="easyui-panel">
这里可以添加任何内容。
</div>
<script type="text/javascript" src="test.js"></script>
</body>
该代码中引用的test.js程序设置了以下属性:
$('#p').panel({
title: '面板功能演示',
collapsible: true,
minimizable: true,
maximizable: true,
closable: true,
width: 300,
height: 100,
});
则浏览器运行效果如图所示。
如果在JS文件中加上以下属性:
halign: 'right',
则面板头部放置在右侧。如图所示。
如果在页面中加入两行代码:
<body>
<div id="p" class="easyui-panel">
这里可以添加任何内容。
</div>
<div id="tt">头部标题</div>
<div id="ft">底部标题</div>
<script type="text/javascript" src="test.js"></script>
</body>
那么,在JS程序中还可以直接以选择器的方式,来引用指定DOM元素作为面板头部或页脚的内容:
header: '#tt',
footer: '#ft',
代码运行效果如下(采用这种方式定义面板头部时,不能使用halign属性,头部原有的各种按钮也会自动隐藏)。
这种方式的好处是,指定元素中所设置的样式在这里依然生效。如,将页面中的两个div改为:
<div id="tt" style="color: red;">头部标题</div>
<div id="ft" style="color: #6A6A6A;text-align: center;">底部标题</div>
则运行效果如下。
由于页脚内容在面板折叠时依然能正常显示,而面板头部在使用这种方式后所有按钮都会自动隐藏,因此,实际应用中很少使用这种方法来指定面板头部,即使使用也是用来指定页脚。
属性名 |
值类型 |
描述 |
默认值 |
---|---|---|---|
border |
boolean |
是否显示面板边框 |
true |
iconCls |
string |
图标的CSS类名 |
null |
cls |
string |
CSS类名,对整个面板有效 |
null |
headerCls |
string |
CSS类名,仅对面板头部有效 |
null |
bodyCls |
string |
CSS类名,仅对面板正文部分有效 |
null |
style |
object |
style样式 |
{} |
left |
number |
面板距离左边的位置(即X轴位置) |
null |
top |
number |
面板距离顶部的位置(即Y轴位置) |
null |
fit |
boolean |
面板大小是否自适应父容器 |
false |
doSize |
boolean |
创建面板时是否重置大小和重新布局 |
true |
tools |
array或selector |
通过数组或选择器创建自定义工具菜单 |
[] |
openAnimation |
string |
打开面板时的动画,可用值: |
|
openDuration |
number |
打开面板的持续时间 |
400 |
closeAnimation |
string |
关闭面板时的动画,可用值:slide、fade、show |
|
closeDuration |
number |
关闭面板的持续时间 |
400 |
有几类属性重点说明如下。
该属性用来指定一个图标显示在面板头部的左侧。注意,这里指定的并不是一个图标文件的名称,而是在页面中引用的icon.css样式文件所定义的类名称。在icon.css中,每个class类名称都对应一个icon图标文件。
如,在js中增加以下代码:
iconCls: 'icon-open',
运行效果如图所示。
前3个属性都是指定一个具体的class类样式名称,分别对面板整体、面板头部或面板主体内容进行样式的调整。由于EasyUI针对不同的主题样式都已经做了比较完备的设置,因此不建议对背景色、字体大小等再重新做样式设置。一方面没必要,另一方面也很难起到作用(样式的优先级不够)。
但是,关于布局方面的样式设置是有效的,也是比较常用的。
例如,要将面板中的所有内容都水平居中显示,可以先在页面中的头部定义样式,代码如下:
<style type="text/css">
.ct {
text-align: center;
}
</style>
该class样式的名称为ct,然后再在JS程序中设置属性,代码如下:
cls: 'ct',
由于cls是对整个面板的头部和主体内容都生效的,因此运行效果如图所示。
如果不想在页面中设置样式,也可以先单独保存到一个CSS文件中,再在页面的头部引用该文件;或者在JS程序中使用style属性直接设置CSS代码。
例如,要实现与上面相同的效果,用style属性的设置方法如下:
style: {
'text-align': 'center',
},
在JS中设置CSS样式时,属性值和属性名都必须用引号括起来!
除了可以使用自定义样式,也可以直接调用系统默认设置的8种颜色样式,分别为c1~c8,这些样式都定义在themes文件夹下的color.css中。如需使用这些样式,必须先引用这个CSS文件,然后在cls、headerCls或bodyCls属性中直接引用这些样式。例如:
headerCls:'c7';
这3个属性都是相对于父元素的。为方便说明问题,我们重新修改页面中的body,代码如下:
<body>
<div id="p" class="easyui-panel">
<div id="sub">这里可以添加任何内容.</div>
</div>
<script type="text/javascript" src="test.js"></script>
</body>
该代码最主要的变化是将原来div中的文字重新用div标签进行包裹,并给它设置id名称为sub,使之成为父元素div中的一个子元素。
现在在JS程序中设置如下代码:
$('#sub').panel({
title:'子面板',
width:130,
height:50,
top:10,
left:10,
});
其中,top和left都设置为10。我们的本意是想使这个子面板在距离父面板左上角的右下方各10px处显示。但实际的效果并不是这样,子面板仍然在父面板主体内容的左上角处开始显示,如图所示。
为什么会出现这种情况?这是因为,这里的top和left都是CSS中的定位布局属性,仅用这两个值是不够的,还需要指定使用哪种定位方法。例如:
$('#sub').panel({
title:'子面板',
width:130,
height:50,
top:10,
left:10,
style: {
'position':'relative',
}
});
运行效果如图所示。
fit属性设置为true时,将自动适应父容器大小。以本代码为例,如果将父元素div的fit设置为true,该面板将铺满整个屏幕;如果将子元素div的fit设置为true,这个子元素将从定位位置开始铺满剩余面积。
该属性用于自定义工具菜单。该菜单可通过以下两种方式创建:数组(array)或选择器(selector)。其中,数组方式只需在JS代码中创建即可,选择器方式则要在页面文件中编写相应的代码。
数组方式创建。数组必须用[ ]括起来,每个数组元素则是对象,每个对象都包含iconCls和handler属性。其中,iconCls表示要使用的图标CSS类名,handler为单击后执行的事件代码。例如,给父元素div添加以下代码:
$('#p').panel({
title: '面板功能演示',
width: 300,
height: 100,
iconCls: 'icon-open',
tools: [
{
iconCls:'icon-add',
handler:function(){alert('这是增加按钮')}
},
{
iconCls:'icon-edit',
handler:function(){alert('这是编辑按钮')}
},
],
});
运行效果如图所示。
单击工具菜单按钮,将弹出相应的提示信息。
如果改用选择器方式,可在页面代码中添加类似于以下这样的内容:
<body>
<div id="p" class="easyui-panel">
<div id="sub">这里可以添加任何内容.</div>
</div>
<div id="bt">
<a class="icon-add" onclick="alert('这是增加按钮')"></a>
<a class="icon-edit" onclick="alert('这是编辑按钮')"></a>
</div>
<script type="text/javascript" src="test.js"></script>
</body>
采用选择器方式时,必须用a标签创建按钮,所使用的按钮图标class样式名称请参考icon.css文件。页面中加了以上代码后,只需在JS程序中引用如下这个id即可:
tools: '#bt',
运行效果与数组方式创建的工具菜单完全相同。
这方面的属性共有4个,留待后面面板方法与事件中一并学习。
面板组件可以同时加载数据。与数据相关的属性如下表所示。
属性名 |
值类型 |
描述 |
默认值 |
---|---|---|---|
content |
string |
设置面板主体内容 |
null |
href |
string |
从URL读取远程数据并且显示到面板主体 |
null |
loader |
function |
以自定义AJAX方式从远程服务器加载内容页 |
|
cache |
boolean |
在超链接首次载入时是否缓存面板内容 |
true |
loadingMessage |
string |
在加载远程数据的时候在面板内显示一条消息 |
Loading… |
extractor |
function |
如何从应答数据中提取内容,返回提取数据 |
|
method |
string |
使用哪种方法读取内容。可用值:get、post |
get |
queryParams |
object |
在加载内容时添加的请求参数 |
{} |
上述属性中,cache、loadingMessage和method都很好理解,现重点讲解其他几个属性。
如果该面板原来有内容,将会被替换。其中,content直接指定内容,可通过HTML代码进行拼接;href和loader则用于远程加载。当对同一个面板、同时使用上述属性时,content属性会自动失效。
例如,如果在上例的父面板中加入以下代码:
content: '<p style="color: blue;">我是指定内容</p>',
则子面板自动消失,且仅在父面板的主体内容部分显示下图所示的内容。
如果再同时指定href属性,则面板主体部分仅显示来自指定页面文件中的内容。代码写法如下:
href: 'test.html',
该test.html中的body部分内容如下所示:
运行效果如图所示。
注意
content的值也可以来自页面中的某个元素。如:
$('#bt').html();
该方式可以直接读取其他页面文件body中的内容,代码写法如下:
href: 'test.html',
也可从其他服务器程序中获取数据。例如,服务器端的test.php程序代码如下:
<?php
echo 'abcdefg';
?>
如果在JS中将href属性设置为:
href: 'test.php',
则运行效果如图所示。
这种方式其实就是自定义如何通过AJAX从远程服务器加载数据。该属性值为事件函数,可接受以下3个参数。
param:发送给远程服务器的参数对象(关于该参数对象的说明,详见后面的queryParams属性)。
success(data):在检索数据成功的时候调用的回调函数。
error():在检索数据失败的时候调用的回调函数。
例如,我们将服务器端的test.php程序内容修改为如下内容:
<?php
$name = (!empty($_GET['name'])) ? $_GET['name'] : '';
$subject = (!empty($_GET['subject'])) ? $_GET['subject'] : '';
$str = $name.$subject.',欢迎!';
echo '<span style="color:red;">'.$str.'</span>';
?>
上述代码的意思是,将客户端传过来的参数值连接成一个字符串,再用HTML代码设置颜色后返回。
现在我们在子面板设置loader属性代码如下:
href: '#',
loader: function(param,success,error){
$.ajax({
url: 'test.php',
data: {
name: '职场',
subject: '码上汇'
},
success: function(data){
success(data);
},
});
},
由于AJAX请求数据时默认使用get方式,因此,服务器端的test.php程序使用$_GET全局数组变量来获取客户端传来的参数值。
客户端浏览器的运行效果如图所示。
注意
即使使用loader远程加载方式,href的属性也不能为空,否则将无法启动远程访问,loader属性自然无效;或者在页面的相应DOM元素中加上一个不为空的href属性也是可以的。
该属性用于定义如何在应答的远程数据中提取数据。该属性值为事件,并自带一个参数用于获取服务器端返回的数据。
假如,我们在子面板中设置extractor属性的事件代码如下:
extractor: function (data) {
return data + '!';
},
浏览器运行时,不论是href还是loader,都会在服务器返回的内容基础上加一个感叹号。
该属性用于设置请求远程数据时的附加参数,属性值为对象。
在请求远程数据的两种方式中,loader属性可以在AJAX中发送请求参数,但href却不可以。有了queryParams后,href也可以在远程请求时发送附加参数了。例如:
href: 'test.php',
queryParams: {
name: '职场',
subject: '码上汇'
}
运行效果与loader属性完全相同。
事实上,不论是href还是loader,也不论它们请求的是HTML格式还是PHP格式的文件,只要使用了其中任意一种方式加载数据,queryParams属性的值都是可用的。其中,loader属性中传入的第一个参数就是queryParams属性的值。
因此,如果使用loader方式请求数据,那么,在使用AJAX发送data数据前,可以先做一下判断:如果在queryParams中已经定义了相应属性的值,就直接使用queryParams中的;否则再重新定义。这样就可以保证附加参数的统一,代码如下:
href: '#',
loader: function(param,success,error){ //这里的param就是queryParams参数对象的值
var name = param.name || '职场'; //如果queryParams中没定义name,就用"职场”
var subject = param.subject || '码上汇'; //如果queryParams中没定义subject,就用"码上汇”
$.ajax({
url: 'test.php',
data: {
name: name,
subject: subject
},
success: function(data){
success(data);
},
});
},
面板提供的全部方法如下表。
方法名 |
参数 |
描述 |
---|---|---|
options |
none |
返回属性及事件对象。例如,获取子面板的标题:
|
panel |
none |
返回整个面板对象。通过该方法获取对象后,可对之进行各种操作。 例如,将面板设置成弧度为5的圆角边框:
|
header |
none |
返回面板头对象 |
footer |
none |
返回面板页脚对象 |
body |
none |
返回面板主体对象 |
setTitle |
title |
设置面板头的标题文本。如:
|
open |
forceOpen |
参数设置为true,打开面板时将跳过onBeforeOpen事件函数 |
close |
forceClose |
参数设置为true,关闭面板时将跳过onBeforeClose事件函数 |
destroy |
forceDestroy |
参数设置为true,销毁面板时将跳过onBeforeDestory事件函数 |
clear |
none |
清除面板内容 |
refresh |
href |
刷新面板来装载远程数据。如果设置了新的参数,将自动重写旧的href属性。例如,打开面板且刷新面板内容:
打开面板的同时用一个新的URL地址来刷新内容:
|
resize |
options |
重新设置面板大小和布局。不带参数时仅起到界面刷新效果
注意:left和top必须设置定位方式才能有效 |
doLayout |
none |
重新绘制面板内子组件的大小。当使用resize方法重置面板大小或布局时,也会自动调用此方法。因此,一般直接使用resize方法 |
move |
options |
移动面板到一个新位置。参数对象包含以下属性:left、top。 |
maximize |
none |
最大化面板到父容器大小 |
minimize |
none |
最小化面板 |
restore |
none |
恢复最大化面板回到原来的大小和位置 |
collapse |
animate |
折叠面板主体 |
expand |
animate |
展开面板主体 |
其中,panel方法的示例代码如下:
$('#sub').panel('panel').css('borderRadius',5);
可能有的读者会说,$('#sub')本来就是jQuery对象,直接用CSS方法不就行了?经测试,如果直接使用下面的代码:
$('#sub').css('borderRadius',5);
其作用效果仅限于面板的身体部分。只有加上panel方法才能返回,并作用于整个面板。
正是由于这个原因,才会有panel、header、footer、body这4个方法,分别用于返回面板的不同区域对象。
至于其他方法在项目中究竟如何运用,我们将结合事件示例一起讲述。
全部事件列表如下所示。
事件名 |
参数 |
描述 |
---|---|---|
onBeforeOpen |
none |
打开面板之前触发。返回false可取消打开操作 |
onOpen |
none |
打开面板时触发 |
onBeforeClose |
none |
关闭面板之前触发。返回false可取消关闭操作 |
onClose |
none |
面板关闭时触发 |
onBeforeDestroy |
none |
面板销毁之前触发。返回false可取消销毁操作 |
onDestroy |
none |
面板销毁时触发 |
onBeforeCollapse |
none |
面板折叠之前触发。返回false可取消折叠操作 |
onCollapse |
none |
面板折叠时触发 |
onBeforeExpand |
none |
面板展开之前触发。返回false可取消展开操作 |
onExpand |
none |
面板展开时触发 |
onResize |
width,height |
面板改变大小时触发 |
onMove |
left,top |
面板移动时触发 |
onMaximize |
none |
面板最大化时触发 |
onRestore |
none |
面板恢复到原始大小时触发 |
onMinimize |
none |
面板最小化时触发 |
onBeforeLoad |
param |
加载数据内容之前触发,参数为queryParams属性的值 |
onLoad |
none |
加载数据内容时触发 |
onLoadError |
none |
加载数据内容发生错误时触发 |
例如,在子面板中设置以下属性和事件代码:
closable: true, //在面板标题显示关闭按钮
onBeforeClose: function () { //关闭面板前触发的事件
return false;
},
则在客户端浏览器运行后,即使单击关闭按钮也是无效的,也就是面板无法关闭,如图所示。
如果在子面板中再加入以下事件代码:
closed: true,
onBeforeOpen: function () {
return false;
},
则重新刷新页面后,这个子面板就无法打开了。怎么办?
正常情况下,方法、事件等操作都应该是通过按钮进行的。假如我们希望通过按钮来执行子面板的关闭和打开操作,可以在tools属性中设置handler事件代码。例如,以下就是新增加的工具栏按钮代码:
iconCls:'icon-reload', //指定图标
handler:function(){ //单击后要执行的事件代码
var p = $('#sub'); //指定子面板dom元素对象,并赋给变量p
if (p.panel('options').closed){ //通过options方法得到属性closed的值
p.panel('open',true);
}else{
p.panel('close',true);
};
p.panel('resize');
},
上述代码的关键在于,根据子面板的closed属性值进行判断:如果处于关闭状态,就执行open方法,执行该方法时同时带了一个true参数,表示跳过onBeforeOpen事件代码;执行close方法时同理。
由于反复的关闭、打开操作可能会造成界面凌乱,因此最后又使用resize方法对该面板进行了重置。运行效果如图所示。
上述代码是通过options方法返回的属性对象,然后再从这个对象中获取closed属性的值来判断子面板是否打开。事实上,options是个通用的方法,EasyUI中几乎所有的组件都有这样的方法,使用频率非常高。我们知道,属性值的类型多种多样,如果通过options返回的内容是数字、字符串、布尔值或者是对象、数组,这些都很好理解,得到了它们的返回值以后可以继续用到其他的代码中,但如果是事件呢?例如,extractor属性的值就是一个事件函数,代码如下:
extractor: function (data) {
return data + '!';
},
这个事件函数也能通过options返回吗?返回后又有什么作用?
答案当然是肯定的。假如这个属性中定义的函数还要重用的话,一样可以返回。例如,在之前的图标按钮代码中增加这样两行:
handler:function(){
var p = $('#sub');
if (p.panel('options').closed){
p.panel('open',true);
var str = p.panel('options').extractor('我的文本');
alert(str);
}else{
p.panel('close',true);
};
p.panel('resize');
},
这里就是调用了extractor属性中的函数,传入的值为“我的文本”,弹出的结果就是在后面加了一个感叹号。当然,也可以将这些函数赋给一个变量,需要使用时直接用变量名即可。
假如希望在打开或关闭子面板时带有动画效果,则可以使用openAnimation、closeAnimation等属性。例如,给子面板加上下列属性值:
openAnimation:'show',
openDuration:800,
closeAnimation:'fade',
closeDuration:800,
再次打开该面板时,是从左上到右下的逐步显示效果;关闭时则是逐渐变淡的动画效果。仔细观察这个面板的打开过程我们还会发现:远程加载的数据是在面板完全打开后才开始载入的,因此在创建延迟加载面板时,href和loader属性非常有用。
总之,panel作为本书第一个系统学习的EasyUI组件,读者一定要认真体会并掌握属性和事件的写法及其方法的作用,尤其是要多研习本书所提供的示例源码。
选项卡的作用就是显示一批面板,但在同一个时间它只会显示一个面板。每个选项卡面板还可以设置一些小的按钮工具菜单,如关闭按钮或其他自定义按钮。
例如,下面的页面代码,只需要指定easyui-tabs的class样式,并未使用任何的JS代码,即可直接生成选项卡:
<div id="t" class="easyui-tabs">
<div title="选项卡1">内容1</div>
<div title="选项卡2">内容2</div>
<div title="选项卡3">内容3</div>
<div title="选项卡4">内容4</div>
</div>
浏览器运行效果如下。
当然,如果要使选项卡发挥更好的性能,肯定还要用到属性、方法和事件。
属性名 |
值类型 |
描述 |
默认值 |
---|---|---|---|
width |
number |
选项卡容器宽度 |
auto |
height |
number |
选项卡容器高度 |
auto |
plain |
boolean |
为true时,将不显示控制面板背景 |
false |
fit |
boolean |
是否铺满所在容器 |
false |
tabWidth |
number |
标签条的宽度 |
auto |
tabHeight |
number |
标签条的高度 |
27 |
scrollIncrement |
number |
选项卡滚动条每次滚动的像素值 |
100 |
scrollDuration |
number |
每次滚动动画持续的时间。单位:毫秒 |
400 |
justified |
boolean |
是否生成等宽标题选项卡 |
false |
narrow |
boolean |
是否删除选项卡标题之间的空间 |
false |
pill |
boolean |
是否将选项卡标题样式改为气泡状 |
false |
border |
boolean |
是否显示容器边框(选项卡头部不受影响) |
true |
tabPosition |
string |
选项卡位置。 |
top |
headerWidth |
number |
选项卡标题宽度。 |
150 |
selected |
number |
默认选中的标签页 |
0 |
showHeader |
boolean |
是否显示标签页标题 |
true |
tools |
array,selector |
在选项卡头部设置工具栏 |
null |
toolPosition |
string |
工具栏位置。可用值:left、right |
right |
例如,在页面中引用test.js文件,然后在该文件中设置以下代码:
$('#t').tabs({
width: 300,
height: 100,
plain: true,
tabWidth: 60,
tabHeigh: 20,
});
浏览器运行效果如图所示。
当tabWidth属性值设置的比较大、多个选项卡宽度之和超过选项卡容器的总宽度时,头部将出现左右按钮。如,将tabWidth设置为100,运行效果如图所示。
这时可以另外设置scrollIncrement和scrollDuration属性,以控制单击左右按钮时的移动速度。实际上,tabWidth基本上是不用重新设置的,直接使用默认的auto就好。如果实在觉得默认的宽度窄了,可以考虑使用justified(等宽)、narrow(去除相邻空间)等属性,代码如下:
justified: true,
narrow: true,
pill: true,
运行效果如图所示。
如果将border属性设置为false,容器边框将不显示。注意,这里仅仅是指选项卡所对应的面板容器边框,选项卡头部不受影响,运行效果如下图所示。
如果在页面中引用的EasyUI主题是bootstrap,即使将border设置为true,选项卡容器边框仍然不显示!bootstrap就是这样的主题效果,其他主题样式正常。
tabPosition属性用于设置选项卡位置,默认为top,也就是在顶部。当位置为left或right时,还可以使用headerWidth属性设置宽度,代码写法如下:
tabPosition: 'left',
headerWidth: 65,
运行效果如图所示。
很显然,“选项卡4”没有正常显示出来,这就需要再调整选项卡容器的height属性。因此,当使用tabPosition时,务必要事先考虑好容器的尺寸。
除此之外,还有selected属性用于初始化选中一个标签页,默认为0,也就是第一个标签页;如果将其设置为2,将默认选中第三个标签页。showHeader用于设置是否显示标签页标题,当设置为false时,标签页标题将全部隐藏,这样就只能通过其他按钮的事件代码来控制选择不同的选项卡了。
现在重点来学习一下如何在选项卡头部设置工具栏。这个工具栏的设置方法与panel面板大致相同,一样可以通过数组或者选择器的方式设置。但由于这个工具栏中的选项是基于EasyUI中的另外一个组件linkbutton的,因而功能更加强大,linkbutton组件支持的所有属性和方法在这里都可以使用,事件则通过handler属性触发。
数组定义方式的示例代码如下:
tools: [
{
iconCls:'icon-add',
text: '增加',
handler:function(){alert('这是增加按钮')}
},
{
iconCls:'icon-edit',
text: '编辑',
handler:function(){alert('这是编辑按钮')}
},
],
运行效果如图所示。
如果用选择器的方式定义,需要在页面中增加以下代码:
<div id="bt">
<a class="easyui-linkbutton" data-options="plain:true,iconCls:'icon-add'" onclick="alert('这是增加按钮')"></a>
<a class="easyui-linkbutton" data-options="plain:true,iconCls:'icon-edit'" onclick="alert('这是编辑按钮')"></a>
</div>
需要注意的是,这里的a标签写法与panel面板中所引用的元素写法有所不同:面板里引用的class是直接调用的图标ID,而这里是linkbutton组件。虽然面板也可以使用linkbutton定义(事件能正常触发),但由于受面板头部各种默认样式的限制,工具栏的显示效果会非常差,而tabs选项卡的头部不受此影响。
为避免影响选项卡知识的学习,这里的linkbutton属性直接写在标签元素的data-options中,没有使用JS代码创建。页面中增加以上代码之后,JS程序再使用以下一行代码即可:
tools: '#bt',
工具栏定义好之后,还可以使用toolPosition属性设置工具栏显示在左边或右边。
方法名 |
参数 |
描述 |
---|---|---|
options |
none |
返回选项卡属性对象。如,返回选项卡容器的宽度:
|
resize |
none |
重置(刷新)选项卡容器大小和布局 |
showHeader |
none |
显示选项卡的标签头 |
hideHeader |
none |
隐藏选项卡的标签头。例如: |
showTool |
none |
显示工具栏 |
hideTool |
none |
隐藏工具栏 |
tabs |
none |
返回所有选项卡面板的对象 |
getTab |
which |
获取指定的选项卡面板对象,which参数可以是选项卡面板的标题或者索引(其他指定which参数的方法,使用规则均与此相同)。
|
getSelected |
none |
获取当前选择的选项卡面板对象 |
getTabIndex |
tab |
获取指定选项卡面板的索引号。
|
select |
which |
选择指定的选项卡面板,which参数可以是选项卡面板的标题或者索引。 例如,选择第3个面板:
也可以这样:
|
unselect |
which |
取消选择指定的选项卡面板。此方法很少使用,因为在选择某个选项卡面板之后,原来的会自动取消 |
exists |
which |
表明指定的面板是否存在。如: |
enableTab |
which |
启用指定的选项卡面板 |
disableTab |
which |
禁用指定的选项卡面板。例如,禁用第3个选项卡面板:
|
close |
which |
关闭指定的选项卡面板 |
scrollBy |
deltaX |
滚动选项卡标题指定的像素数量,负值向右滚动,正值向左滚动 |
add |
options |
添加新的选项卡面板 |
update |
param |
更新指定的选项卡面板 |
这些方法中,有几个需要特别做出说明。
该方法用于返回所有选项卡面板对象。由于这是一个数组,就会涉及遍历的问题。比如,我们希望关闭所有的选项卡面板,代码如下:
var obj = $('#t').tabs('tabs'); //获取所有的选项卡面板对象
var i = obj.length-1; //使用length得到数组内的元素个数
for(i;i>=0;i--){ //使用倒序循环
$('#t').tabs('close',i); //对指定的选项卡面板执行关闭操作
};
还可以使用each进行循环操作。例如,以下代码将输出所有的选项卡面板标题:
var obj = $('#t').tabs('tabs');
$.each(obj,function() {
var title = this.panel('options').title;
alert(title);
});
上述代码中,this表示当前遍历到的对象。由于它们都是选项卡面板,因此可以使用panel的options方法获得指定的属性值。
该方法用于添加新的选项卡面板。选项参数是一个配置对象,在这里不仅可以使用panel中的所有属性(部分属性可能没有实际效果),而且还多了以下几个选项卡面板的独有属性。
selected:设置为true的时候,选项卡面板会被选中。实际上,新增面板默认就是排列在最后且被选中的。如果不需要选中,可将其设为false(注意,tabs组件本身也有selected属性,它用来指定默认选中的标签页,不要混淆了)。
disabled:设置为true的时候,选项卡面板会被禁用。
index:用于指定新增面板的排列位置。如要将新增面板排在第一个,只需将index的属性值设置为0即可。例如,以下代码将新增一个选项卡面板:
$('#t').tabs('add',{
title:'新面板',
iconCls: 'icon-open',
closable: true,
collapsible: true,
content: '这是新增加的面板',
style: {
'text-align': 'center',
'margin':'20px 0',
},
tools:[{
iconCls:'icon-mini-refresh',
handler:function(){
alert('refresh');
}
}],
});
浏览器运行效果如图所示。
显而易见,配置对象中所用到的都是panel中的属性,但collapsible并没有显示出效果,因为页标签上无法生成折叠按钮。当然,由于这是往选项卡中添加面板,有些功能受到限制也是很正常的。
对于任何一个可以获取到的选项卡面板,都能使用panel中的方法对其进行操作。例如,对已经选择的面板执行数据刷新,代码如下:
var tab = $('#t').tabs('getSelected');
tab.panel('refresh');
该方法用于更新指定的选项卡面板。param参数包含3个属性。
tab:要更新的选项卡面板。
type:更新类型。可选值有header、body、all,此参数的作用在于限制更新范围,一般很少使用。
options:要更新的配置对象参数。该参数的设置方法和add一致,同样可以使用panel面板中的所有属性及新增的selected和disabled属性,但不可以使用index改变面板位置。
例如下面的代码,就是对索引号为2的选项卡面板进行更新:
var tab = $('#t').tabs('getTab',2);
$('#t').tabs('update',{
tab: tab,
// type: 'body',
options: {
title: '新的标题',
selected: true,
style: {
'padding': '10px',
},
href: 'test.php',
queryParams: {
name: '职场',
subject: '码上汇'
},
extractor: function (data) {
return data;
},
}
})
运行效果如图所示。
如果将上述代码中被注释掉的“type: 'body',
”启用,则该面板的标题不会被更新。因为type指定的是body,而标题属于header,自然不会被更新。
请注意,如果不在以上代码中使用update方法,而是直接对变量tab使用panel属性进行设置,那么就只能更新面板内容,标题无法更新。例如:
var tab = $('#t').tabs('getTab',2);
tab.panel({
title: '新的标题',
href: 'test.html',
});
有了update方法之后,其实页面中的代码可以继续简化,把相应的操作全部集中到JS程序中来处理。例如,将页面代码中的title属性全部删除,只保留4个选项卡面板所对应的4个空div标签,代码如下:
<body>
<div id="t" class="easyui-tabs">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<script type="text/javascript" src="test.js"></script>
</body>
然后在JS程序中使用以下代码,一样可以正常生成选项卡,代码如下:
var obj = $('#t').tabs('tabs');
$.each(obj,function (i) {
$('#t').tabs('update',{
tab: this,
options: {
title: '选项卡'+i,
content: '内容'+i,
},
});
});
当然,如果你愿意,连页面中的4个空div标签都不用写,直接在JS程序中通过循环方式添加选项卡面板就行。例如:
for (var i = 0; i < 4; i++) {
$('#t').tabs('add',{
title:'新面板' + i,
content: '这是新增加的面板' + i
})
}
事件名 |
事件参数 |
描述 |
---|---|---|
onSelect |
title,index |
选择选项卡面板时触发 |
onUnselect |
title,index |
取消选择选项卡面板时触发 |
onBeforeClose |
title,index |
关闭选项卡面板之前触发,返回false时取消关闭操作 |
onClose |
title,index |
关闭选项卡面板时触发 |
onAdd |
title,index |
添加新选项卡面板时触发 |
onUpdate |
title,index |
更新选项卡面板时触发 |
onLoad |
panel |
加载完成远程数据且选择该选项卡的时候触发 |
onContextMenu |
e,title,index |
右键单击选项卡页签时触发,一般用于弹出右键菜单 |
例如,下面的代码,当选择某个选项卡面板的时候,自动对该面板数据执行远程刷新。这个示例用的是onSelect事件,表中的前6个事件用法基本与此相同:
onSelect: function(title,index){
var tab = $('#t').tabs('getSelected',index);
tab.panel('refresh');
},
再如,以下代码在选择有远程数据加载的面板,而且在加载完成时,将给出提示信息(后续只要不做数据刷新,此信息就不再提示)。本示例使用的是onLoad事件:
onLoad : function (panel) {
var title = panel.panel('options').title;
alert('【'+title+'】数据加载完毕!');
}
至于onContextMenu事件,是在右键单击选项卡页签的时候触发,一般用于弹出右键菜单。由于菜单方面的知识暂时还未涉及,这里仅举个例子简单了解一下。以下是JS事件代码:
onContextMenu:function(e, title,index){
e.preventDefault(); //阻止默认菜单
$('#mm').menu('show', {
left: e.pageX,
top: e.pageY
});
},
很显然,这个代码用到了menu菜单组件,它操作的DOM元素对象id名称为mm。
假如页面中的mm内容如下:
<div id="mm" class="easyui-menu">
<div>菜单1</div>
<div>菜单2</div>
</div>
则浏览器运行后,在选项卡页签上单击鼠标右键时的效果如图所示。
分类选项卡和tabs有点类似,都是允许用户使用多个面板。但从展示效果上来说,tabs是横向展示,而accordion侧重于纵向展示。因此,accordion和tabs相比,其最大的变化就是每个面板都内建支持了展开和折叠的功能。
同样的,只需在页面中指定accordion组件的class样式,无需任何JS代码,即可自动生成分类选项卡效果,代码如下:
<body>
<div id="a" class="easyui-accordion">
<div title="选项卡1">内容1</div>
<div title="选项卡2">内容2</div>
<div title="选项卡3">内容3</div>
<div title="选项卡4">内容4</div>
</div>
</body>
浏览器运行效果如图所示。
属性名 |
值类型 |
描述 |
默认值 |
---|---|---|---|
width |
number |
分类容器的宽度 |
auto |
height |
number |
分类容器的高度 |
auto |
fit |
boolean |
分类容器大小是否自适应父容器 |
false |
border |
boolean |
是否显示边框 |
true |
halign |
string |
将选项卡头部垂直显示,可选值:left、right |
null |
animate |
boolean |
展开和折叠面板时是否显示动画效果 |
true |
multiple |
boolean |
是否允许同时展开多个面板 |
false |
selected |
number |
初始化时默认选中的面板索引号 |
0 |
这些属性都非常好理解,但有两点需要注意。
❶ 这里的宽度和高度实际上是指打开某个面板时所固定显示的宽度和高度,因此,当允许同时展开多个面板时,经常会因为高度的问题而导致余下的面板无法完整显示。例如,以下JS代码:
$('#a').accordion({
width: 200,
height: 300,
multiple: true,
});
我们依次展开“选项卡1”和“选项卡2”时,由于“选项卡1”打开在先,因此它会立即把300的固定高度给占用了;“选项卡2”没有其他高度可用,只好占用“选项卡3”和“选项卡4”的标题高度,这就直接导致“选项卡3”和“选项卡4”被挤出可视空间之外。如果想将它们再“请”回来,只能选择将1或者2折叠起来一个。
浏览器运行效果如图所示。
因此,如果一定要使用multiple属性,可以不用设置高度。
❷ 如果将halign属性设置为left或right,则选项卡头部将垂直显示。使用此属性时,必须设置高度,否则将造成页面效果的混乱。如下面的代码:
$('#a').accordion({
width: 200,
height: 100,
halign: 'left',
});
运行效果如图所示。
我们知道,与halign属性配套使用的还有titleDirection属性,该属性可以用来指定标题内容的显示方向。这个属性只能用在面板中,写在accordion中无效。
方法名 |
方法参数 |
描述 |
---|---|---|
options |
none |
返回分类组件的属性 |
panels |
none |
获取所有面板 |
resize |
none |
调整分类组件大小 |
getSelected |
none |
获取选中的面板 |
getSelections |
none |
获取所有选中的面板 |
getPanel |
which |
获取指定的面板,which参数可以是面板的标题或者索引 |
getPanelIndex |
panel |
获取指定面板的索引 |
select |
which |
选择指定面板,which参数可以是面板标题或者索引 |
unselect |
which |
取消选择指定面板,which参数可以是面板标题或者索引 |
add |
options |
添加一个新面板 |
remove |
which |
移除指定面板,which参数可以使面板的标题或者索引 |
和tabs相比,accordion的方法少了很多。比如,允许面板可用还是不可用、显示或隐藏面板头部、判断指定面板是否存在、更新面板的配置属性对象等。那是不是就意味着accordion组件功能相对较弱?肯定不能简单地这样说。由于这两种选项卡的应用场景不同,因此功能上有一些差异是很正常的。实际上,accordion在某些方面用起来更加简单、方便。
比如,tabs有个exists方法用于判断指定的选项卡面板是否存在。accordion虽然没有这个方法,但采用下面这样的示例代码来做判断一样简单:
var a = $('#a').accordion('getPanel',2);
if (a) {
alert('面板存在!');
}else{
alert('面板不存在!');
}
当然,tabs用这种方法也是可以判断的。
请注意,尽管tabs和accordion都可以通过getSelected、getTab或getPanel等方法来获得面板对象,但tabs必须使用update方法才能更改标题信息,而accordion就不需要,可以直接使用panel中的所有属性和方法来对它进行操作。而且,它也同样扩展了一个selected属性,如果设置为true,将直接展开面板。
例如,下面的代码,就是对第3个面板进行操作的:
var a = $('#a').accordion('getPanel',2);
a.panel({
title:'新标题',
iconCls: 'icon-open',
selected: true,
tools: [
{
iconCls:'icon-remove',
handler:function(){
$('#a').accordion('remove',3);
},
},
{
iconCls:'icon-edit',
handler:function(){alert('这是编辑按钮')},
},
],
});
浏览器运行结果如图所示(单击左边的工具栏按钮时,将移除第4个面板)。
同样的道理,panels方法返回的是所有面板对象,我们可以使用each或for对这个数组进行遍历。例如,将所有面板的标题统一重新设置为“菜单项X”字样,代码如下:
var obj = $('#a').accordion('panels');
for(var i=0;i<obj.length;i++){
$('#a').accordion('getPanel',i).panel({
title: '菜单项'+i,
});
};
至于新增面板的add方法,它所用到的options参数完全参照panel面板中的属性来进行设置即可。默认情况下,新增的面板会自动变成当前展开的面板。如果要添加一个非选中面板,可以将selected属性设置为false。
注意
本组件对面板进行更新或添加时,只扩展了一个selected属性,在tabs中扩展的disabled和index属性不能在本组件中使用。
例如:
$('#a').accordion('add',{
title:'新增面板',
iconCls: 'icon-reload',
selected: false,
closable: true,
href: 'test.html',
style: {
'text-align': 'center',
},
extractor: function (data) {
return '<br>我是后加上去的<br>' + data;
},
});
由于上述代码将新增面板的closable属性设置为真,因此新增的面板将有一个关闭按钮;又因为设置了文字居中的style样式,且给获取的内容又加了一段文字,因此面板标题和内容都将水平居中,而且显示的内容比加载的test.html内容更丰富。
浏览器运行效果如图所示。
展开新增面板,显示内容如图所示。
事件名 |
事件参数 |
描述 |
---|---|---|
onSelect |
title,index |
面板被选中时触发 |
onUnselect |
title,index |
面板取消选中时触发 |
onAdd |
title,index |
添加新面板时触发 |
onBeforeRemove |
title,index |
移除面板之前触发。返回false可以取消移除操作 |
onRemove |
title,index |
面板被移除时触发 |
这些事件都非常简单,在之前panel和tabs组件的学习中都举了多个例子,这里不再赘述。
布局容器用于展示完整的页面,按照上北、下南、左西、右东的方位顺序,共由这4个边缘面板及中间的1块内容面板组成。每个边缘区域面板都可以通过拖拽其边框来改变大小,也可以单击折叠按钮将面板折叠起来。
例如,使用下面的页面代码即可创建一个标准结构的布局页面:
<body>
<div id="l" class="easyui-layout">
<div data-options="region:'north',title:'上面标题'">上面内容</div>
<div data-options="region:'south',title:'下面标题',">下面内容</div>
<div data-options="region:'west',title:'左边标题',">左边内容</div>
<div data-options="region:'east',title:'右边标题',">右边内容</div>
<div data-options="region:'center',title:'内容标题',">内容区域</div>
</div>
</body>
其中,外层的div是布局容器,这个div必须用class指定easyui-layout;里面的5个div分别对应4个边缘面板和中间的1块内容区域面板,这些div都通过data-options属性来指定具体的面板归属区域(region)和标题(title)。
此时我们发现,将该页面代码拖拽到浏览器运行,却看不到任何的输出内容!这是因为,布局容器还必须设置要输出的宽度或高度。例如,我们可以通过style属性给外围的div设置宽或高。
如果只设置宽度,仍不会有任何输出,代码如下:
<div id="l" class="easyui-layout" style="width: 400px">
如果只设置高度,可以输出,宽度会自适应,代码如下:
<div id="l" class="easyui-layout" style="height: 200px">
如果同时设置宽度和高度,则按指定的宽高输出布局效果,代码如下:
<div id="l" class="easyui-layout" style="width: 400px;height: 200px">
浏览器运行效果如图所示。
很显然,这种布局效果并不完美:首先,上、下两个区域的内容无法显示,因为没有指定高度;其次,左、右两个区域的面板标题也无法完整显示,因为没有指定宽度。
因此,完整的页面代码应该给外围的div同时指定高和宽,内部的上下两个边缘面板要指定高,左右两个要指定宽(中间的内容区域可以自动适应,无需指定)。例如:
<body>
<div id="l" class="easyui-layout" style="width: 400px;height: 200px">
<div data-options="region:'north',title:'上面标题'" style="height: 60px;">上面内容</div>
<div data-options="region:'south',title:'下面标题'," style="height: 60px;">下面内容</div>
<div data-options="region:'west',title:'左边标题'," style="width: 80px;">左边内容</div>
<div data-options="region:'east',title:'右边标题'," style="width: 80px;">右边内容</div>
<div data-options="region:'center',title:'内容标题',">内容区域</div>
</div>
</body>
运行效果如图所示。
这样的页面布局默认显示在浏览器的左上角,如果要让它水平居中,只需再加上一个水平居中的样式即可,代码如下:
<div id="l" class="easyui-layout" style="width: 400px;height: 200px;margin: 0 auto;">
上述5个区域面板中,除了中间的内容区域面板是必须的,其他4块边缘面板都是可选的。而且,每个区域都可以再次嵌套布局,以构建任意复杂的页面结构。
例如,我们在原有的内容区域再嵌套一个layout,这个嵌套的布局只有左、中、右3块面板,代码如下:
<body>
<div id="l" class="easyui-layout" style="width: 400px;height: 200px;margin: 0 auto;">
<div data-options="region:'north',title:'上面标题'" style="height: 60px;">上面内容</div>
<div data-options="region:'south',title:'下面标题'," style="height: 60px;">下面内容</div>
<div data-options="region:'west',title:'左边标题'," style="width: 80px;">左边内容</div>
<div data-options="region:'east',title:'右边标题'," style="width: 80px;">右边内容</div>
<div data-options="region:'center'">
<div id="sub" class="easyui-layout" data-options="fit:true">
<div data-options="region:'west',title:'嵌套左边'," style="width:
80px;">左边内容</div>
<div data-options="region:'east',title:'嵌套右边'," style="width:
80px;">右边内容</div>
<div data-options="region:'center',title:'嵌套内容',"></div>
</div>
</div>
</div>
</body>
请注意,这里用到了layout仅有的一个属性fit(其他如region等是区域面板的属性,不是layout的属性)。该属性设置为true时,将自动适应父容器的大小。运行效果如图所示。
fit属性在layout中是很常用的。当创建嵌套布局时,使用该属性可以使嵌套的layout自动适应父容器的大小;当创建需要铺满整个屏幕的布局时,同样只要将外围div中的fit属性设置为true即可,代码如下:
<div id="l" class="easyui-layout" data-options="fit:true,">
实际上,当需要将布局页面满屏显示时,还有一种更简便的方法:将外围的div去掉,直接将body的class设置为layout。例如:
<body id="l" class="easyui-layout">
如果觉得嵌套的布局和原来的布局贴得太近(边线重合变粗了),想在它们之间添加一点距离的话,只需要在相应的div上增加样式即可。如,下面的代码就将内边距的4条边都设置了5px的距离:
<div data-options="region:'center'" style="padding: 5px">
运行效果如图所示。
通过页面标签创建布局的示例代码可以看出,layout只有一个属性,就是fit。
layout有几种方法如表所示。
方法名 |
参数 |
描述 |
---|---|---|
resize |
param |
重新设置布局大小,不带参数时,将刷新布局页面。
|
collapse |
region |
折叠指定面板,参数可用值:north、south、east、west。 |
expand |
region |
展开指定面板,参数可用值:north、south、east、west |
split |
region |
给指定的区域面板增加分割线,参数可用值:north、south、east、west。例如: |
unsplit |
region |
移除指定区域面板分割线,参数可用值:north、south、east、west |
add |
options |
添加指定面板,属性参数是一个配置对象 |
remove |
region |
移除指定面板,参数可用值:north、south、east、west |
panel |
region |
返回指定面板,参数可用值:north、south、east、west、center |
上述所有带region参数的方法中,只有panel方法的参数可用值为5个,其他都是只能使用4个边缘方向值。由于panel方法的返回值是面板对象,因此它可以有5个(包括中心内容区域面板),现在重点学习此方法以及相关的add方法。
该方法得到的返回值是面板对象,与面板相关的所有属性、方法在这里都可以使用,从而可以对指定的区域面板进行各种操作。例如:
var p = $('#l').layout('panel','north');
p.panel({
noheader: true,
});
以上代码就将顶部面板的头部给取消了。除panel中的属性外,区域面板还扩展增加了一些属性,如下表所示:
属性名 |
值类型 |
描述 |
默认值 |
---|---|---|---|
region |
string |
定义布局面板位置,可用值: |
|
split |
boolean |
是否可以通过分割栏改变面板大小 |
false |
minWidth |
number |
可拖拽到的最小面板宽度 |
10 |
minHeight |
number |
可拖拽到的最小面板高度 |
10 |
maxWidth |
number |
可拖拽到的最大面板宽度 |
10000 |
maxHeight |
number |
可拖拽到的最大面板高度 |
10000 |
expandMode |
string |
在单击折叠面板时候的展开模式 |
float |
collapsedSize |
number |
折叠后的面板大小 |
28 |
collapsedContent |
string, function |
设置面板折叠后需要显示的内容信息 |
|
hideCollapsedContent |
boolean |
是否隐藏面板折叠后的指定内容信息 |
true |
hideExpandTool |
boolean |
是否隐藏面板上的折叠按钮扩展工具栏 |
false |
该属性是指单击折叠面板时候的展开模式,可用值有float、dock和null。这3种模式的作用包括以下几点。
float:区域面板展开并浮动在顶部,当鼠标焦点离开面板时会自动隐藏。采用此模式时,必须先单击折叠一次面板,然后鼠标焦点离开时才会自动隐藏。
dock:区域面板展开并钉在面板上,在鼠标焦点离开面板时不会自动隐藏。
null:什么也不会发生。
该属性是指折叠后的面板大小。对于上、下两种面板来说,该值指的是折叠后的高度;对于左、右两种面板来说,该值指的是折叠后的宽度。
collapsedContent用于指定面板折叠后需要显示的内容信息;hideCollapsedContent则用于设置是否隐藏折叠后的指定内容信息,由于其默认值为true,要显示相关内容的话,必须将其设置为false。
例如,下面的代码中,未标粗的部分使用的都是panel中的属性,标粗的则是新增属性:
$('#l').layout('split','south'); //给下方的边缘面板增加分割线
$('#l').layout('panel','south').panel({ //对下方的边缘面板设置属性
iconCls:'icon-add', //添加面板图标
title: '新标题', //修改面板标题
closable: true, //增加面板关闭按钮
content: '这是新内容', //指定面板内容
tools: [ //指定面板工具栏
{
iconCls:'icon-remove',
handler:function(){
$('#l').layout('remove','east');
},
},
{
iconCls:'icon-edit',
handler:function(){alert('这是编辑按钮')},
},
],
expandMode: 'float', //指定面板展开方式
collapsedSize: 28, //指定折叠后的面板大小
collapsedContent: '面板已经折叠,请点击此处展开', //指定折叠后的面板显示内容
hideCollapsedContent: false, //不要隐藏折叠后的指定内容
});
浏览器运行后,下方的边缘面板显示效果如图所示。
单击该面板的折叠按钮,则折叠后的效果如图所示。
上述代码中,如果没有使用collapsedContent属性重新指定内容,但同时又将hideCollapsedContent设置为false,则面板折叠后自动以面板标题作为显示内容。
除了可以直接为collapsedContent属性设置字符串内容外,也可以通过函数指定返回一个jQuery对象,从而实现更加复杂的功能。例如:
$('#l').layout('split','west'); //在左侧边缘面板增加分割条
$('#l').layout('panel','west').panel({ //设置左侧边缘面板
expandMode: null, //折叠展开方式为null
hideCollapsedContent: false, //不要隐藏折叠后的指定内容
collapsedSize: 68, //折叠后的面板宽度为68
collapsedContent: function(){ //通过函数指定折叠后的显示内容为jq对象
return $('#titlebar');
},
});
这个jQuery对象使用的选择器是#titlebar,它在页面中对应的DOM元素代码如下:
很显然,这个id为titlebar的div元素就是一组linkbutton按钮,关于该按钮组件,后面还将系统学习。
浏览器运行效果如图所示。
由于我们已经在id为titlebar的div元素中,为第一个按钮设置了onclick事件,也就是单击后可以再次展开左侧面板,因此,该面板折叠后显示的“折叠按钮”图标就显得多余了,其位置如图所示。
如何处理?这就需要用到另外一个属性:hideExpandTool。
该属性用于隐藏面板上的折叠按钮扩展工具栏。默认为false,如要隐藏,只需设置为true即可,代码如下:
hideExpandTool: true,
该方法用于增加区域面板。具体用法和之前学习的tabs、accordion等组件增加面板的方法完全相同,可用的属性参数不仅包括panel中的所有属性,同时也包括上表所列出的全部扩展属性。例如:
$('#l').layout('remove','east'); //先删除右侧区域面板
$('#l').layout('add',{
region: 'east', //添加面板指定为右侧
title: '新增面板', //标题
width: 80, //面板宽度
collapsed: true, //默认折叠
hideCollapsedContent: false, //折叠后的面板内容不隐藏
});
上述代码中,由于将hideCollapsedContent属性值设置为false,但同时又未设置CollapsedContent属性值,因此该面板折叠后会仍然显示面板标题。运行效果如图所示。
布局方面的事件比较简单,只有4个,如下表所示。
事件名 |
事件参数 |
描述 |
---|---|---|
onCollapse |
region |
折叠区域面板时触发 |
onExpand |
region |
展开区域面板时触发 |
onAdd |
region |
新增区域面板时触发 |
onRemove |
region |
移除区域面板时触发 |
例如:
$('#l').layout({
onAdd: function(region){
alert('刚刚新增的区域面板是:' + region);
}
});