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

工程开发 专栏收录该内容
23 篇文章 0 订阅

开发背景:

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

        今天记录下的是前几天开发的一个下拉框组件,该组件内容是一棵菜单树。本来系统开发的时候使用的是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和隐藏input
		treeObj.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) { //防止点击展开节点前面值为true
				picker.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,都能搞点可以显摆给外行人看的交互界面。

 

  • 7
    点赞
  • 14
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值