使用jstree自定义下拉菜单树组件

来源:互联网 发布:社交软件 英语 编辑:程序博客网 时间:2024/06/13 18:32

开发背景:

        每次都很啰嗦,要先陈述一下开发背景。不过,这样做的目的一来是让自己能够快速回忆起自己的实战背景,二来是类似引子,能够阐述这样处理的实际用途,不仅仅是用于理论学习。

        今天记录下的是前几天开发的一个下拉框组件,该组件内容是一棵菜单树。本来系统开发的时候使用的是BUI,BUI本身也有下拉树组件。只是因为我们使用的BUI版本比较早,今年的版本和去年的在某些方面不兼容,如果一次性替换,会引起很多js报错。又因为近期使用过就是jstree,测试过几万条节点数据的加载性能,相比BUI,渲染速度有优秀很多(BUI并不能胜任这个数量级)。所以,干脆自己封装一个组件。具体要求,我总结了一下:

自定义下拉框,该下拉框内容为菜单树。下拉框操作规则同其他BUI下拉框组件,但是封装方式并非使用BUI封装方式,而是自定义封装。所以需要测试如下功能 ①菜单树节点被选中,会将值显示在输入框,满足多选功能,用逗号分隔 ②菜单树初始化会将存储的节点名称显示在输入框,并且菜单树相应节点会被触发选中 ③提供清空功能,点击清空按钮,下拉框所有选中节点都会清空,并且输入框值也清空;清空按钮在菜单树被选中的情况下显示,在没有选中的情况下隐藏 ④下拉框在单击输入框和下拉标签的情况下会显示出来,在单击下拉框区域外的所有位置都会自动收缩 ⑤查询界面的下拉框的清空功能和“重置”按钮联动 

实战结果:

        这里,有使用到很多封装细节,因为时间问题,初稿不做太多细节表述,重点在代码中有做注释。直接上代码:

首先前端html:

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><%-- 显示输入框 --%><div class="rmp-span"><label class="rmp-control-label">部门归属</label><div class="rmp-control-text"><div id="department" class="bui-cncselectfilter bui-suggest bui-combox bui-select" aria-disabled="false" tabindex="0" hidefocus="true" aria-pressed="false"><input type="text" class="bui-select-input" /><span class="icon-delelte-input"><i class="icon-remove-sign-select"></i></span><span class="x-icon x-icon-normal"><span class="x-caret x-caret-down"></span></span></div></div></div><%-- 隐藏的存值输入框 --%><input name="qo.department" id="rdepartment" type="hidden"/><%-- 隐藏的下拉框 --%><div id='jstree_around' class="hide"><div class="apm-treeselect-header"><div class="apm-treeselect-filter"><input id="search_input" class="filter"/></div></div><div id="jstree_div"></div></div><link rel="stylesheet" href="${uics}/jstree/themes/default/style.css" /><script type="text/javascript" src="${uics}/jstree/jstree.js"></script><script type="text/javascript">// AJAX异步拉取数据var treeData = null;$.ajax({url : ctx + "/common/organization-tree.action",type : "post", async: false,success : function(data) {treeData = data.tree;}}); $(function(){//树主体初始化$('#jstree_div').jstree({"core" : {"multiple" : true, // 允许多选'animation' : false,'data' : treeData,    },  'expand_selected_onload' : true, //选中项蓝色底显示    'checkbox' : {    // 禁用级联选中    'three_state' : false,         'cascade' : 'undetermined' //有三个选项,up, down, undetermined; 使用前需要先禁用three_state    },  'plugins' : ['checkbox', 'search']  //如果使用checkbox效率会降低, 'wholerow'会把线隐藏掉});         //绑定到自定义的组件上ApmJstreeUtil.bindJstree({render : 'jstree_div',showField : 'department',saveField : 'rdepartment',picker : 'jstree_around',searchField : 'search_input',width : 250,height : 300});})</script>


js封装方式:

var ApmJstreeUtil = {/** * @param  render-渲染id,saveFiled-存储的input id,showField-展示的input id, * picker-下拉div的id */bindJstree : function(obj) { //输入对象//初始化下拉隐藏域的弹出位置和宽高var picker = $('#' + obj['picker']);picker.addClass('apm-tree-picker');picker.css('width', obj['width']);picker.css('height', obj['height']);var inputDiv = $('#' + obj['showField']);var top = inputDiv.offset().top + inputDiv.outerHeight(); //获取偏移位置var left = inputDiv.offset().left;picker.css('top', top); //设置绝对位置picker.css('left', left);var treeObj = $('#' + obj['render']);treeObj.css("text-align", "left");var deleteIcon = inputDiv.find("i"); //通过find查找子元素var inputShow = inputDiv.find("input");
                var saveInput = $('#' + obj['saveField']);//默认查询框,可以外部自定义,从而覆盖该触发方式if (obj['searchField'] && $('#' + obj['searchField'])) {var searchFieldObj = $('#' + obj['searchField']);var to = false;searchFieldObj.keyup(function() { //绑定按键事件,也可以绑定特定按键if (to) {clearTimeout(to);}to = setTimeout(function() {var v = searchFieldObj.val();treeObj.jstree(true).search(v);}, 250);});}//将选择值显示在输入input和隐藏inputtreeObj.on("changed.jstree", function(e, data) {if (data && data.selected.length > 0) {$('#' + obj['saveField']).val(data.selected.join(","));var i, j, r = [];    for(i = 0, j = data.selected.length; i < j; i++) {       r.push(data.instance.get_node(data.selected[i]).text);    }    deleteIcon.show();    inputShow.val(r.join(","));} else {deleteIcon.hide();inputShow.val("");
                                saveInput.val("");}});// 绑定load时间,初始化数据显示treeObj.on("loaded.jstree", function(e, data) {treeObj.jstree("open_all");  //展开全部var saveValue = $('#' + obj['saveField']).val();var checkNodeIds = saveValue.split(",");if (!saveValue || !checkNodeIds || checkNodeIds.length === 0) {deleteIcon.hide();return ;}var r = [];treeObj.find("li").each(function() {for (var i = 0; i < checkNodeIds.length; i++) {if ($(this).attr("id") == checkNodeIds[i]) { //如果节点的ID等于checkNodeIds[i],表示要选中r.push(data.instance.get_node(checkNodeIds[i]).text);treeObj.jstree("select_node", $(this)); //选中的节点,不是check_node//$(this).children("a").addClass("jstree-clicked");break;}}})deleteIcon.show();inputShow.val(r.join(","));});//隐藏和展示绑定inputDiv.on('click', function() {picker.show();});$('body').click(function(evt) {if ($(evt.target).parents('#' + obj['showField']).length == 0 && $(evt.target).parents('#' + obj['picker']).length == 0 //判断鼠标点击的上层是否是#jstree_div&& evt.target.id != obj['showField']&& evt.target.id != obj['picker']&& evt.target.className.indexOf("jstree") == -1) { //防止点击展开节点前面值为truepicker.hide();}});//清空按钮deleteIcon.on('click', function() {ApmJstreeUtil.deselectJstree(obj);deleteIcon.hide();});},/** * 清楚被选中的项 * @param render-渲染id,saveFiled-存储的input id,showField-展示的input id, * picker-下拉div的id */deselectJstree : function(obj) {var treeObj = $('#' + obj['render']);var saveField = $('#' + obj['saveField']);var checkNodeIds = saveField.val().split(",");if (!checkNodeIds || checkNodeIds.length === 0) {return ;}treeObj.find("li").each(function() {for (var i = 0; i < checkNodeIds.length; i++) {if ($(this).attr("id") == checkNodeIds[i]) {treeObj.jstree("deselect_node", $(this)); //取消选中的节点break;}}})
                saveInput.val("");}};<strong></strong>

效果展示:

单击输入框,弹出下拉框,使用jstree生成的树                  选中数据后,会显示叉形删除标示,可以清空数据,包括清空隐藏域

                      


待改进

可以发现,前端html引用输入框和隐藏下拉框代码太长,不便他人二次使用。应该考虑进一步封装到ApmJstreeUtils中。


小结:

        这应该可以算是我第一次比较大的js封装实战。以前对于事件的绑定、DOM树的细节操作都不是很清楚,实战后对这些有了进一步的理解。js是个非常灵活的弱语言,开发方式非常多,从而对于初学者而言,有弊有利——弊是别人封装的很多代码不是那么好理解,除非一步步深究,还有就是很多细节用得不是很合理,(详见《JavaScript语言精粹》),容易留下鲁棒性不足的隐患;利,最明显的就是,不管你是不是菜鸟,了解一点js,都能搞点可以显摆给外行人看的交互界面。


2 0