由来
一直以来做开发,尤其是web开发,少不了和JavaScript打交道,自认为js脚本还可以吧!
有一天,我发现 select控件、下拉框、ajax请求、文件上传控件等等
大多地方都要使用的到,
有些虽然用的也是别人的写好的控件,可是根据项目的要求,自己想在封装一下,来满足
自己的需求!
可是写了很久,以失败而告终!常用的,说实话,大家都会,可是要深入研究久逊色点了。
所以,不能放弃治疗,哪里有病就要治疗哪里,绝不能怠慢!
示例: 封装select_tree.js
- 效果图:
- 封装代码缩略图:
- html调用
引入样式: <link href="/js/component/selectTree/selecttree.css" rel="stylesheet" />
引入脚本: <script src="/js/component/selectTree/selectTree.js"></script>
控件:<input id="equ_category_small" name="equ_category_small" type="text" class="form-control" />
- js调用
$("#equ_category_small").selectTree({
url: "/equipment/GetCategoryList",
isRadio: true,
title: "请选择设备",
requestCallBack: function (data) {
var treedata = [];
if (data.code == 0) {
$(data.data.CategoryBig).each(function (i, o) {
var node = {};
var id = o.categoryId;
var pid = o.pCategoryId;
node.id = id;
node.pId = pid;
node.name = o.categoryName;
node.isParent = true;
node.open = true;
treedata.push(node);
});
$(data.data.CategorySmall).each(function (i, o) {
var node = {};
var id = o.categoryId;
var pid = o.pCategoryId;
node.id = id;
node.pId = pid;
node.name = o.categoryName;
treedata.push(node);
});
}
return treedata;
},
selected: function (node) {
$("#equ_category_small").val(node[0].id);
//$("#equ_category_small").valid();
}
});
- 后台数据
@RequestMapping("GetCategoryList")
@ResponseBody
public String GetCategoryList()
{
List<BasEquType> ListSmall =basEquTypeService.categoryListSmall(0);
List<BasEquType> ListBig = basEquTypeService.categoryListBig(0);
Dictionary<String,List<BasEquType>> map=new Hashtable<>();
map.put("CategoryBig",ListBig);
map.put("CategorySmall",ListSmall);
Result result=new Result();
result.setCode("0");
result.setData(map);
return JSON.toJSONString(result) ;
}
如何封装自己js库
上面只是提供了一个完整示例,并没有介绍如何封装js组件,好了,接下重点讲接一下。
- 创建匿名函数
Q: 什么是匿名函数?
A: 顾名思义就是没有名称的函数
Q: 匿名函数作用是什么?
A: 匿名函数的作用主要是实现自己定义内部中的函数,从而扩大函数的使用功能
Q: 为什么要使用匿名函数?
A: 因为页面的脚本会越来越多,全局变量和和方法会越来越多,主要是为了避免重名,还有就是模块化。
匿名函数:
匿名函数
(function ($) {
//....封装组件逻辑
})(jQuery);
--------------------------------------------------------------------
拆分来看
var fn = function($){
//.....组件封装逻辑
};
fn(jQuery);
立即执行的函数
- 创建模块内容
模块就是实现特定功能的一组方法。
a. 一般写法 :只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。
例如:
function fun1(){
//...
}
function fun2(){
//...
}
上面的函数fun1()和fun2(),组成一个模块。使用的时候,直接调用就行了。
b. 为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面
var module1 = new Object({
_count : 0,
fun1: function (){
//...
},
fun2: function (){
//...
}
});
上面的函数fun1()和fun2(),都封装在module1对象里。使用的时候,就是调用这个对象的属性。
module1.fun1();
但是,这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。
module1._count = 100;
c. 为了解决这个问题,我们使用”立即执行函数”(Immediately-Invoked Function Expression,IIFE)也就是匿名函数,可以达到不暴露私有成员的目的。
var module1 = (function(){
var _count = 0;
var fun1= function(){
//...
};
var fun2= function(){
//...
};
return {
fun1: fun1,
fun2: fun2
};
})();
使用上面的写法,外部代码无法读取内部的_count变量。
console.info(module1._count); //undefined
d.一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用”放大模式”(augmentation)。
var module1 = (function (mod){
mod.fun3= function () {
//...
};
return mod;
})(module1);
上面的代码为module1模块添加了一个新方法fun3(),然后返回新的module1模块。
e.在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上一节的写法,第一个执行的部分有可能加载一个不存在空对象,这时就要采用”宽放大模式”。(Loose augmentation)。
var module1 = ( function (mod){
//...
return mod;
})(window.module1 || {});
模块的写法就介绍到这里了,在我们创建的匿名函数里面写我们需要的函数和属性,如上图封装代码缩略图所示
重点知识
- JQuery 的扩展方法$.extend()的用法和含义:
extend()方法作用就是合并另个对象,有相同的则覆盖,没有相同的则添加
例子1:
var settings = { url: '#', title: 'xxx', param: "ok" };
var options = { title: 'abc', param: "no" };
$.extend(settings, options);
结果1:
settings == { url: '#', title: 'abc', param: "no" }
例子2:
var empty = {};
var defaults = { url: '#', title: 'xxx', param: "ok" };
var options = { title: 'abc', param: "no" };
var settings = $.extend(empty, defaults, options);
结果:
settings == { url: '#', title: 'abc', param: "no" }
empty == { url: '#', title: 'abc', param: "no" }
- prototype 属性的用法和含义:
prototype 属性使您有能力向对象添加属性和方法
例如:
SelectTree.prototype.initDom = function ()
SelectTree.prototype.setCheckNode = function ()
.......................................
- $.fn.selectTree = function (options) {//…}
这个就表示向jquery对象添加自定义方法
$.fn.selectTree = function (options, param) {
//...
};
调用:
$("#equ_category_small").selectTree({
//...
});
js脚本和样式
脚本 selecttree.js
(function ($) {
"use strict";
var CON_TYPE_NULL = "请选择";
var BASEHtml = "<div class=\"dropdown\"><button class=\"btn dropdown-toggle form-control\" type=\"button\" data-toggle=\"dropdown\" data-ztree=\"check\" id=\"btnType\"> <span >请选择类别</span> <span class=\"caret\"></span></button> <div class=\"panel dropdown-menu selecttree\"> <div class=\"panel-body\"> <ul class=\"ztree\" ></ul></div><div class=\"panel-footer\"> <div class=\"btn-group btn-group-justified\"><a class=\"btn\" id=\"btnCancel\">取消</a><a class=\"btn btn-success\" id=\"btnOK\">确定</a></div></div></div></div>";
var TreeIndex = 1;
/**
* selectTree插件基于jquery,bootstrap,ztree,使用时要引用相应的js
* param: {
* NullText:默认提示信息
* objTitle: "btnTitle", 选择后需要显示的控件I
* isRadio: false, 是否是单选
* url: "/Machine/KBS/GetContentTypes" 数据请求的URL 暂时基于post方式请求
* requestCallBack:数据获取后的回调方法
* succes:点击确定按钮的处理事件
* cancel:点击取消按钮的处理事件
* selected:单选选定回调
* isParentSelect:父级可选
* },
* 使用:$("#type").selectTree();
*/
function SelectTree(obj, params) {
this.tiggerObj = obj;
this.param = this.param || {};
if (!this.param.NullText || this.param.NullText == "") {
this.param.NullText = CON_TYPE_NULL;
}
this.param = $.extend(this.param, params);
this.initDom();
this.objTitle = $(this.tiggerObj).prev().children("button").children("span").eq(0);
this.ishide = true;
this.isCancel = true;
if (this.param.title) {
this.objTitle.text(this.param.title);
}
this.getContent();
this.initValue();
this.initEvent();
}
SelectTree.prototype.reload = function (params) {
this.param = $.extend(this.param, params);
//this.objTitle = $(this.tiggerObj).prev().children("button").children("span").eq(0);
this.ishide = true;
this.isCancel = true;
if (this.param.title) {
this.objTitle.text(this.param.title);
}
this.getContent();
this.initValue();
this.initEvent();
};
//初始化组件DOM元素
SelectTree.prototype.initDom = function () {
this.tiggerObj.css({ "height": "0.5px", "width": "0px", "padding": "0px", "margin": "0px" });
this.tiggerObj.before(BASEHtml);
if (this.param.isRadio) {
$(".selecttree .panel-footer .btn").hide();
}
};
//初始化事件定义
SelectTree.prototype.initEvent = function () {
var self = this;
var isthis = false;
//点击组件内部触发动作
$(".dropdown .panel-body").click(function () {
self.ishide = false;
if (self.param.isRadio) {
self.isCancel = false;
}
});
if (!self.param.isRadio) {
var btnCancel = $(this.tiggerObj).parent().find("#btnCancel");
$(btnCancel).click(function () {
//console.log("btnCancel");
self.ishide = true;
if (self.param.cancel) {
self.param.cancel();
}
});
var btnOk = $(this.tiggerObj).parent().find("#btnOK");
$(btnOk).click(function () {
// console.log("btnOk");
self.ishide = true;
self.isCancel = false;
if (self.param.succes) {
self.param.succes();
}
});
}
//组件消失时触发
$(this.tiggerObj).parent().on("hide.bs.dropdown", function (e) {
var node = self.TreeObj.getSelectedNodes()[0];
if (self.param.isRadio && self.param.isParentSelect) {
var selectedNodes = self.TreeObj.getSelectedNodes();
if (self.param.selected) {
self.param.selected(selectedNodes);
}
var names = [];
var id = [];
var tid = [];
var pname = "";
var pid = "";
$(selectedNodes).each(function (i, o) {
names.push(o.name);
id.push(o.id);
tid.push(o.tId);
var pnode = o.getParentNode();
if (pnode && pnode != null) {
pname += pnode.name;
if (pnode.value) {
pid += pnode.value;
} else {
pid += pnode.id;
}
}
});
var name = names.join(",");
if (name == "")
name = self.param.NullText;
$(self.objTitle).text(name);
if (self.param.name) {
$("#" + self.param.name).val(name);
}
if (self.param.parentName) {
$("#" + self.param.parentName).val(pname);
}
if (self.param.parentValue) {
$("#" + self.param.parentValue).val(pid);
}
$(self.tiggerObj).val(id.join(","));
$(self.tiggerObj).data("tids", tid.join(","));
return;
}
if ((!self.ishide && !self.param.isRadio) || (self.param.isRadio && !self.ishide && node && node.isParent)) {
self.ishide = true;
if (self.param.isRadio) {
self.isCancel = true;
}
e.preventDefault();
} else {
if (!self.isCancel) {
var treeObj = self.TreeObj;
var selectedNodes;
if (self.param.isRadio) {
selectedNodes = treeObj.getSelectedNodes();
} else {
selectedNodes = treeObj.getCheckedNodes(true);
}
if (self.param.selected) {
self.param.selected(selectedNodes);
}
var names = [];
var id = [];
var tid = [];
var pname = "";
var pid = "";
$(selectedNodes).each(function (i, o) {
if (!o.isParent) {
names.push(o.name);
id.push(o.id);
tid.push(o.tId);
var pnode = o.getParentNode();
if (pnode && pnode != null) {
pname += pnode.name;
if (pnode.value) {
pid += pnode.value;
} else {
pid += pnode.id;
}
}
}
});
var name = names.join(",");
if (name == "")
name = self.param.NullText;
$(self.objTitle).text(name);
if (self.param.name) {
$("#" + self.param.name).val(name);
}
if (self.param.parentName) {
$("#" + self.param.parentName).val(pname);
}
if (self.param.parentValue) {
$("#" + self.param.parentValue).val(pid);
}
$(self.tiggerObj).val(id.join(","));
$(self.tiggerObj).data("tids", tid.join(","));
}
}
});
//组件显示时触发
$(this.tiggerObj).parent().on("show.bs.dropdown", function (e) {
// console.log($(self.tiggerObj).data("tids"));
self.setCheckNode();
self.isCancel = true;
});
};
//设置选择node
SelectTree.prototype.setCheckNode = function () {
var self = this;
if (self.param.isRadio) {
var selectnode = self.TreeObj.getSelectedNodes();
if (selectnode.length > 0) {
self.TreeObj.cancelSelectedNode(selectnode[0]);
}
} else {
self.TreeObj.checkAllNodes(false);
}
var check = $(self.tiggerObj).data("tids");
if (check) {
var tids = check.split(",");
$(tids).each(function (i, o) {
var node = self.TreeObj.getNodeByTId(o);
if (self.param.isRadio) {
self.TreeObj.selectNode(node);
} else {
self.TreeObj.checkNode(node, true, true);
}
});
}
};
//获取树内容
SelectTree.prototype.getContent = function () {
var url = this.param.url;
var self = this;
$.ajax({
"type": "Get",
//"contentType": "application/json",
"url": url,
"async": false,
//"dataType": "json",
"success": function (resp) {
var zNodes = [];
var setting = treeSetting(self.param.isRadio);
if (self.param.requestCallBack) {
zNodes = self.param.requestCallBack($.parseJSON(resp));
} else {
zNodes = $.parseJSON(resp).data;
}
var treeContent = $(self.tiggerObj).prev().children().children(".panel-body").children(".ztree").eq(0);
$(treeContent)[0].id = "tree" + TreeIndex;
self.TreeObj = $.fn.zTree.init(treeContent, setting, zNodes);
TreeIndex++;
// self.initValue();
}
});
};
//初始化组件值
SelectTree.prototype.initValue = function () {
var value = $(this.tiggerObj).val();
var self = this;
if (value && value != "") {
var ids = value.split(',');
//var nodes = [];
var tids = [];
var name = [];
$(ids).each(function (i, o) {
var node = self.TreeObj.getNodesByParam("id", o, null)[0];
if (node && node != null) {
//nodes.push(node);
name.push(node.name);
tids.push(node.tId);
}
});
if (tids.length > 0) {
this.objTitle.text(name.join(','));
$(this.tiggerObj).data("tids", tids.join(','))
}
} else {
this.objTitle.text(this.param.NullText);
}
};
//获取ztree设置
function treeSetting(isradio) {
if (isradio) {
return {
data: {
simpleData: {
enable: true
}
}
};
} else {
return {
check: {
enable: true,
nocheckInherit: false
},
data: {
simpleData: {
enable: true
}
}
};
}
}
$.fn.selectTree = function (options) {
var select = new SelectTree(this, options);
return select;
};
})(jQuery);
样式 selecttree.css
.input-group .dropdown {
display: table-cell;
}
.selecttree {
padding-left: 0;
padding-right: 0;
border: solid 1px #ccc;
}
.ztree * {
font-size:1em;
}
.dropdown {
display: inline-block;
}
.atct .dropdown {
max-width: 530px;
}
.atct .dropdown button:first-child {
overflow: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
white-space: nowrap;
width: 490px;
display: block;
}
.dropdown-menu {
padding: 0;
}
.panel-footer {
width: 100%;
padding: 0;
}
.selecttree ul.ztree {
max-height: 200px;
overflow-y: auto;
overflow-x: auto;
}
.btn {
border: 1px solid #e6e6e6;
}