jquery插件Asgrid开发小记
最近做的一个项目中用到EasyUI,在开发起来还是非常方便的,而且文档齐全,看了两天文档,对照官方的例子,基本上都能找到相应的解决方法。但是easyUI的datagrid控件在加载数据过多的时候就会非常慢,特别是在IE上的时候,同事测试的时候500条记录从返回数据给前端到前端显示出来需要10秒左右,对于用的人来说这个等待时间太长了。是不可以接受的。有人会说为什么一定要加载这么多数据呢,可以分页嘛。其实分页每页的显示是可以选择,可以设置每页显示50或500等等。那为什么要显示500条呢,不为什么,因为需要需要显示这么多。
好关于jquery插件开发可以看上一篇我转载过来的文章,已经非常详细了 :jQuery插件开发指南
作为这个控件主要是为了替换easyui的datagrid控件太慢的问题。所以主要功能为一下几点:
插件定位
主要是为了解决加载慢的问题
1.尽量保持easyui中datagrid控件api保持一致
2.只做数据显示,去掉编辑功能保持轻量级
基本格式
(function($){
})(jquery);
如果有向我一样js是半吊子的话,可以看看
Javascript中的自执行匿名函数
所谓知其然而知其所以然
插件结构
(function ($) { //默认的参数 var defaults = { //key value的参数 } $.fn.AsGrid = function (options, param) { if (typeof options == 'string') { return methods[options](this, param); } var def = $.extend(defaults, options); return this.each(function () { }); } //实现的方法 var methods = { } }
首先我们定义了默认的参数 defaults ,它将在function (options, param)主入口函数的如下代码中体现出来,将传入的参数和默认参数合并,$.extend()参考 http://www.ostools.net/apidocs/apidoc?api=jquery
var def = $.extend(defaults, options);
主函数中的
if (typeof options == 'string') { return methods[options](this, param); }
表示传入的第一个参数如果是字符串,也就是要实现的方法名,那么就执行methods里面的方法
最后是一个返回
return this.each(function () { });
这样形成了一个闭包 ,不太明白的可以看javascript的闭包的形成
为什么这里是this .each 而不是直接返回呢,这个去看看jquery对象是怎么回事。
好这样就形成了基本的插件结构 。
默认参数
//默认的参数 var defaults = { columns: {}, method: "post", idField: "id", singleSelect: false, pageNumber: 0, pageSize: 10, rowStyler: function (index, row) { }, data: {}, pagination: true, paginationHeight: 30, paginationControl: null, headHeight: 30, width: -1, height: -1, pageNumber: 1, pageSize: 10, pageList: [10, 20, 30], queryParams: {}, onLoading: function () { } //在数据加载完成后执行 };
以上是默认参数设置,基本上是参考easyUI的一部分内容 。
创建插件的Dom
return this.each(function () { // 初始化 构建表格对象 var tb = $(this); var iscreate = tb.closest(".AsGridcs"); if (iscreate.length == 0) { // if (def.IsCreate) { //创建是判断是否存在url if (options.url == undefined) { def.url = undefined; } //创建表头 var hd = $("<div class='AsGrid_head' style='height:" + def.headHight + "px'></div>"); var th = $("<div class='AsGrid_head_move'></div>"); var tr = "<table><tr>"; for (var i in def.columns) { var col = def.columns[i]; if (col.checkbox) { tr += "<td class='AsGrid_head_col ' style='width:" + col.width + "px'><input id='col_All_" + i + "' type='checkbox' /></td>"; } else { if (col.title == undefined) col.title = ''; tr += "<td class='AsGrid_head_col' style='width:" + col.width + "px'>" + col.title + "</td>"; } } tr += "</tr></table>"; th.append(tr); hd.append(th); //创建主容器 var parentdiv = $("<div class='AsGridcs' ></div>"); var contrentdiv = $("<div class='AsGrid_Content'></div>"); tb.before(parentdiv).appendTo(parentdiv).before(hd); var grid_loading = $("<div class='AsGrid_loading'><div>正在加载,请稍后……</div></div>"); parentdiv.append(grid_loading); var pagination = $("<div class='AsGrid_pagination'></div>"); var pa_control = $("<div class='AsGrid_pagination_control'></div>"); if (!def.singleSelect) { CheckboxClick(tb); } //设置容器的区域大小 var w = 20; for (var x in def.columns) { var colen = def.columns[x]; w += colen.width; } if (def.width != undefined) { parentdiv.css("width", def.width); contrentdiv.css("width", def.width); } if (def.height != undefined) { parentdiv.css("height", def.height); contrentdiv.css("height", def.height - def.headHeight - def.paginationHeight - 10); } contrentdiv.css({ overflow: "auto" }); hd.css({ "overflow": "hidden", width: def.width, "position": "relative" }); tb.addClass("AsGrid_Table"); tb.css("width", w); th.css({ "width": 10000, "position": "relative" }); pagination.css("width", def.paginatinoHeight); //设置容器滚动条偏移 contrentdiv.scroll(function () { th.css("left", -$(this).scrollLeft()); // th.offset({ left: -$(this).scrollLeft() }); // th.offset({ left: this.scrollLeft }); }); //判断是否存储url 如果存在 直接加载 } if (def.url != undefined) { var v = $.extend({ rows: def.pageSize, page: def.pageNumber }, def.queryParams); $(".AsGrid_loading").css({ "display": "block" }); $.post(def.url, v, function (data) { if (data.rows == undefined) { tb.AsGrid("loadData", data); } else { def.paginationControl.pagination({ total: data.total, pageNumber: def.pageNumber }); tb.AsGrid("loadData", data.rows); } }, "json"); } tb.data("def", def); });
首先我们获取当前对象,并查看是否有.AsGridcs的div,如果有就根据传入的值没有就开始创建dom,并设置了一些css样式
var tb = $(this); var iscreate = tb.closest(".AsGridcs");
if (iscreate.length == 0)
{//创建dom
}
当传入存在url的时候需要加载数据,这里使用了等一下要说道的 loadData来绑定数据
if (def.url != undefined) { var v = $.extend({ rows: def.pageSize, page: def.pageNumber }, def.queryParams); $(".AsGrid_loading").css({ "display": "block" }); $.post(def.url, v, function (data) { if (data.rows == undefined) { tb.AsGrid("loadData", data); } else { def.paginationControl.pagination({ total: data.total, pageNumber: def.pageNumber }); tb.AsGrid("loadData", data.rows); } }, "json"); } tb.data("def", def);
最主要的一句是最后一句 tb.data(“def”,def);
将所有的数据保存了起来,方便后面调用
实现方法
这里我实现了一个loadData的方法 ,其实就是将数据转换为dom然后插入 。这里将data保存在了def.data中
var methods = { loadData: function (obj, data) { //删除所有节点 并重新加载 var def = obj.data("def"); def.data = data; obj.children().empty(); var a = new Array(); var j = 0; var k = 0;
//创建数据 for (var d in data) { if (k++ % 2 == 1) { a[j++] = "<tr class='rowColorOdd AsGridRow'>"; } else { a[j++] = "<tr class='rowColorEve AsGridRow'>"; } for (var i in def.columns) { var col = def.columns[i]; if (col.checkbox) { a[j++] = "<td class='AsGrid_content_ckbox' width='" + col.width + "px'><input class='col_All_" + i + "' type='checkbox' /></td>" } else { // if (data[d][col.formatter] == undefined) { var s = ""; if (!(data[d][col.field] == null || data[d][col.field] == undefined)) { s = data[d][col.field]; } if (col.formatter == undefined) { a[j++] = "<td width='" + col.width + "px'>" + s + "</td>"; } else { a[j++] = "<td width='" + col.width + "px'>" + col.formatter(s, data[d], d) + "</td>"; } } } a[j++] = "</tr>"; } $(".AsGrid_loading").css({ "display": "none" }); obj.append(a.join('')); a = null; //数据加载完后发生 def.onLoading(); //鼠标移动事件 mouseMove(obj) //初始化事件 obj.find("tr").click(function () { var ev = $(this); var classname = ev.attr("class"); var classlist = classname.split(" "); var flag = false; for (var o in classlist) { if (classlist[o] == "selectColor") { flag = true; break; } } if (flag) { unselectRow(ev); } else { //执行全部取消 if (def.singleSelect) { methods.unselectAll(obj); } selectRow(ev); } }); return obj; } }
最后我们将obj返回,这是为了支持jquery的链式操作 ,其他方法实现起来的方法都大致相同,基本就是获取数据,执行操作 ,返回值。如果方法不需要返回值可以返回自己来支持链式操作,如下根据编号选中行
selectRow: function (obj, index) { var def = obj.data("def");//获取数据 selectRow(obj.children('tr').eq(index));//执行操作 return obj;//返回值 },
小结
由于项目前期写了很多代码,要是替换成别的表格控件,很多代码都不可用了,在不需要编辑只有浏览的页面实现一个尽量简洁的控件,找到定位很重要,最开始还想着要让表格支持翻页,最后使用了easyUI里的翻页控件集成了进去。然后加控制联动参数 。
作为一个js还是个半吊子,前期做个简单的demo来验证想法很重要 ,原理弄清楚后,后面就简单了。现在应该比半吊子进步一点点了。