bootstrap select2插件整合ztree实现树形下拉框

最近在开发管理后台时,需要用到树形下拉框组件。第一反应当然是从网上找,但是结果让人很失望,所以决定自己动手整合一个。

技术需求点:①样式需要和下拉框相同;②下拉选项区域展示树形结构

以上两点需求,独立的每个点都有现成的插件可以使用,即基于bootstrap的select2和基于jquery的ztree;所以,我要做的事,就是扩展select2来支持ztree,这样就基本达到了我的技术需求;

我们要扩展某个插件,那么,必须要先对该插件有一定的了解,至少需要弄清楚该插件的实现流程。对于本次的扩展来说,我需要了解select2是怎样初始化的,什么时候触发下拉列表,下拉列表选项是何时添加到下拉面板中的,点击某个选项之后又是如何保存选项的。

想要得到以上问题的答案,最有效且快速的方式当然是通过select2官网了解其核心实现逻辑,以及它是否对扩展有较好的支持。在

select2高级功能中有对扩展开发的详细介绍

通过了解,我需要扩展4个对象,即可达到目的:BaseAdapter(进行数据处理:当前选择的,选择,取消选择,查询等)、Observable(用于处理下拉面板的展示等)、BaseSelection(选择操作的处理)

详细实现见如下代码:

​
/**
 * 基于select2和ztree的树形下拉框
 * 
 * @param options
 *            object类型的select2配置参数{...,ztree:{setting:{},zNodes:[]},valueField:值属性名称,textField:文本属性名称}
 * @version: v1.0.0
 */
!function($) {
	'use strict';
	function print(obj) {
		console.dir('------------');
		console.dir(obj);
	}
	// 定义数据适配器
	$.fn.select2.amd.define('select2/data/ztree', [ './base', '../utils', 'jquery' ], function(BaseAdapter, Utils, $) {
		function ZtreeSelectAdapter($element, options) {
			this.$element = $element;
			this.options = options;
			ZtreeSelectAdapter.__super__.constructor.call(this);
		}
		Utils.Extend(ZtreeSelectAdapter, BaseAdapter);
		//当前选中的选项:通过选中的option找到对应的ztreenode,并将znode设置为选中状态
		ZtreeSelectAdapter.prototype.current = function(callback) {
			var data = [];
			var self = this;
			this.$element.find(':selected').each(function() {
				var $option = $(this);
				var option = self.item($option);
				option.tId = $option.data('tId');
				data.push(option);
			});

			// 从ztree获取当前选中的数据
			var $ztree = this.container.results.$ztree;
			for (var i = 0; i < data.length; i++) {
				var node = $ztree.getNodeByTId(data[i].tId);
				if (!node) {
					continue;
				}
				data[i] = node;
				$ztree.selectNode(node, false, false);
			}
			callback(data);
		};

		ZtreeSelectAdapter.prototype.select = function(data) {
			var self = this;
			data.selected = true;
			var val = data.id;
			this.$element.val(val);
			this.$element.trigger('change');
		};

		ZtreeSelectAdapter.prototype.unselect = function(data) {};

		ZtreeSelectAdapter.prototype.bind = function(container, $container) {
			var self = this;
			this.container = container;
			container.on('select', function(params) {
				self.select(params.data);
			});
		};

		ZtreeSelectAdapter.prototype.destroy = function() {
			this.$element.find('*').each(function() {
				Utils.RemoveData(this);
			});
		};

		//搜索
		ZtreeSelectAdapter.prototype.query = function(params, callback) {
			var preText = this.$element.data('ztree-search');
			var text = $.trim(params.term);
			this.$element.data('ztree-search', text);
			//首次搜索,并且本次搜索的内容是空字符,不作处理
			if (!preText && !text) {
				callback({
					results : []
				});
				return;
			}
			
			var data = [];
			var $ztree = this.container.results.$ztree;
			//无ztree对象,不作处理
			if (!$ztree) {
				callback({
					results : []
				});
				return;
			}
			
			//ztree模糊搜索
			data = $ztree.fuzzySearch(text);

			callback({
				results : data
			});
		};
		ZtreeSelectAdapter.prototype.addOptions = function($options) {
			Utils.appendMany(this.$element, $options);
		};
		ZtreeSelectAdapter.prototype.option = function(data) {};

		ZtreeSelectAdapter.prototype.item = function($option) {
			var data = {};
			data = Utils.GetData($option[0], 'data');
			if (data != null) {
				return data;
			}
			if ($option.is('option')) {
				data = {
					id : $option.val(),
					text : $option.text(),
					disabled : $option.prop('disabled'),
					selected : $option.prop('selected'),
					title : $option.prop('title')
				};
			} else if ($option.is('optgroup')) {
				data = {
					text : $option.prop('label'),
					children : [],
					title : $option.prop('title')
				};
				var $children = $option.children('option');
				var children = [];
				for (var c = 0; c < $children.length; c++) {
					var $child = $($children[c]);
					var child = this.item($child);
					children.push(child);
				}
				data.children = children;
			}
			data = this._normalizeItem(data);
			data.element = $option[0];
			Utils.StoreData($option[0], 'data', data);
			return data;
		};

		ZtreeSelectAdapter.prototype._normalizeItem = function(item) {
			if (item !== Object(item)) {
				item = {
					id : item,
					text : item
				};
			}
			item = $.extend({}, {
				text : ''
			}, item);

			var defaults = {
				selected : false,
				disabled : false
			};

			if (item.id != null) {
				item.id = item.id.toString();
			}

			if (item.text != null) {
				item.text = item.text.toString();
			}

			if (item._resultId == null && item.id && this.container != null) {
				item._resultId = this.generateResultId(this.container, item);
			}

			return $.extend({}, defaults, item);
		};

		ZtreeSelectAdapter.prototype.matches = function(params, data) {
			var matcher = this.options.get('matcher');

			return matcher(params, data);
		};

		return ZtreeSelectAdapter;
	});
	// 单选适配器
	$.fn.select2.amd.define('select2/selection/ztreeSingle', [ 'jquery', './base', '../utils' ], function($, BaseSelection, Utils) {
		function ZtreeSingleSelection() {
			ZtreeSingleSelection.__super__.constructor.apply(this, arguments);
		}
		Utils.Extend(ZtreeSingleSelection, BaseSelection);
		ZtreeSingleSelection.prototype.render = function() {
			var $selection = ZtreeSingleSelection.__super__.render.call(this);
			$selection.addClass('select2-selection--single');
			$selection.html('' + '' + ''
					+ '');
			return $selection;
		};
		ZtreeSingleSelection.prototype.bind = function(container, $container) {
			var self = this;
			ZtreeSingleSelection.__super__.bind.apply(this, arguments);
			var id = container.id + '-container';
			this.$selection.find('.select2-selection__rendered').attr('id', id).attr('role', 'textbox').attr('aria-readonly', 'true');
			this.$selection.attr('aria-labelledby', id);
			this.$selection.on('mousedown', function(evt) {
				// 只响应鼠标左键事件
				if (evt.which !== 1) {
					return;
				}
				self.trigger('toggle', {
					originalEvent : evt
				});
			});
			this.$selection.on('focus', function(evt) {

			});
			this.$selection.on('blur', function(evt) {
			});
			container.on('focus', function(evt) {
				if (!container.isOpen()) {
					self.$selection.trigger('focus');
				}
			});
		};
		ZtreeSingleSelection.prototype.clear = function() {
			var $rendered = this.$selection.find('.select2-selection__rendered');
			$rendered.empty();
			$rendered.removeAttr('title'); // clear tooltip on
			// empty
		};
		ZtreeSingleSelection.prototype.display = function(data, container) {
			var template = this.options.get('templateSelection');
			var escapeMarkup = this.options.get('escapeMarkup');
			var str = template(data, container);
			return escapeMarkup(str);
		};

		ZtreeSingleSelection.prototype.selectionContainer = function() {
			return $('');
		};

		ZtreeSingleSelection.prototype.update = function(data) {
			if (data.length === 0) {
				this.clear();
				return;
			}
			var selection = data[0];
			var $rendered = this.$selection.find('.select2-selection__rendered');
			var formatted = this.display(selection, $rendered);
			$rendered.empty().append(formatted);
			$rendered.attr('title', selection[this.options.get('textField')]);
		};

		return ZtreeSingleSelection;
	});
	// 定义结果展示适配器
	$.fn.select2.amd.define('select2/ztreeresults', [ 'jquery', './utils' ], function($, Utils) {
		function ZtreeResults($element, options, dataAdapter) {
			this.$element = $element;
			this.data = dataAdapter;
			this.options = options;
			ZtreeResults.__super__.constructor.call(this);
		}
		Utils.Extend(ZtreeResults, Utils.Observable);
		//初始化ztree
		ZtreeResults.prototype.render = function() {
			var $results = $('
    '); if (this.options.get('multiple')) { $results.attr('aria-multiselectable', 'true'); } this.$results = $results; // 初始化ztree和下拉选项,以便通过select元素的option找到对应的znode if (!this.$ztree) { var config = this.options.get('ztree'); this.$ztree = $.fn.zTree.init(this.$results, config.setting, config.zNodes); var data = this.$ztree.transformToArray(this.$ztree.getNodes()); // 添加1个空元素 var valueField = this.options.get('valueField'); var textField = this.options.get('textField'); // 利用节点数据生成select-->option for (var i = 0; i < data.length; i++) { var id = data[i][valueField]; var $option = $(''); $option.data('tId', data[i].tId); this.$element.append($option); } } return $results; }; ZtreeResults.prototype.clear = function() {}; ZtreeResults.prototype.displayMessage = function(params) { var escapeMarkup = this.options.get('escapeMarkup'); this.clear(); this.hideLoading(); var $message = $('
  • '); var message = this.options.get('translations').get(params.message); $message.append(escapeMarkup(message(params.args))); $message[0].className += ' select2-results__message'; this.$results.append($message); }; ZtreeResults.prototype.hideMessages = function() { this.$results.find('.select2-results__message').remove(); }; // 展示下拉选项 ZtreeResults.prototype.append = function(data) { this.hideLoading(); }; ZtreeResults.prototype.position = function($results, $dropdown) { var $resultsContainer = $dropdown.find('.select2-results'); $resultsContainer.append($results); }; ZtreeResults.prototype.sort = function(data) { return data; }; //不作处理 ZtreeResults.prototype.highlightFirstItem = function(){}; //不作处理 ZtreeResults.prototype.setClasses = function() {}; ZtreeResults.prototype.showLoading = function(params) { this.hideLoading(); var loadingMore = this.options.get('translations').get('searching'); var loading = { disabled : true, loading : true, text : loadingMore(params) }; var $loading = this.option(loading); $loading.className += ' loading-results'; this.$results.prepend($loading); }; ZtreeResults.prototype.hideLoading = function() { this.$results.find('.loading-results').remove(); }; ZtreeResults.prototype.option = function(data) { var option = document.createElement('li'); option.className = 'select2-results__option'; var attrs = { 'role' : 'treeitem', 'aria-selected' : 'false' }; var matches = window.Element.prototype.matches || window.Element.prototype.msMatchesSelector || window.Element.prototype.webkitMatchesSelector; if (data.title) { option.title = data.title; } this.template(data, option); Utils.StoreData(option, 'data', data); return option; }; ZtreeResults.prototype.bind = function(container, $container) { var self = this; var id = container.id + '-results'; this.$results.attr('id', id); container.on('results:all', function(params) { self.clear(); self.append(params.data); }); container.on('results:append', function(params) { self.append(params.data); if (container.isOpen()) { self.setClasses(); } }); container.on('query', function(params) { self.hideMessages(); self.showLoading(params); }); container.on('select', function() { if (!container.isOpen()) { return; } self.setClasses(); }); container.on('open', function() { self.$results.attr('aria-expanded', 'true'); self.$results.attr('aria-hidden', 'false'); self.setClasses(); self.ensureHighlightVisible(); }); container.on('close', function() { self.$results.attr('aria-expanded', 'false'); self.$results.attr('aria-hidden', 'true'); self.$results.removeAttr('aria-activedescendant'); }); container.on('results:toggle', function() { var $highlighted = self.getHighlightedResults(); if ($highlighted.length === 0) { return; } $highlighted.trigger('mouseup'); }); container.on('results:select', function() { var $highlighted = self.getHighlightedResults(); if ($highlighted.length === 0) { return; } var data = Utils.GetData($highlighted[0], 'data'); if ($highlighted.attr('aria-selected') == 'true') { self.trigger('close', {}); } else { self.trigger('select', { data : data }); } }); if ($.fn.mousewheel) { this.$results.on('mousewheel', function(e) { var top = self.$results.scrollTop(); var bottom = self.$results.get(0).scrollHeight - top + e.deltaY; var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0; var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height(); if (isAtTop) { self.$results.scrollTop(0); e.preventDefault(); e.stopPropagation(); } else if (isAtBottom) { self.$results.scrollTop(self.$results.get(0).scrollHeight - self.$results.height()); e.preventDefault(); e.stopPropagation(); } }); } this.$results.on('mouseup', '.select2-results__option[aria-selected]', function(evt) { var $this = $(this); var data = Utils.GetData(this, 'data'); if ($this.attr('aria-selected') === 'true') { if (self.options.get('multiple')) { self.trigger('unselect', { originalEvent : evt, data : data }); } else { self.trigger('close', {}); } return; } self.trigger('select', { originalEvent : evt, data : data }); }); }; ZtreeResults.prototype.getHighlightedResults = function() { return []; }; ZtreeResults.prototype.destroy = function() { this.$results.remove(); }; ZtreeResults.prototype.ensureHighlightVisible = function() {}; ZtreeResults.prototype.template = function(result, container) { var template = this.options.get('templateResult'); var escapeMarkup = this.options.get('escapeMarkup'); var content = template(result, container); if (content == null) { container.style.display = 'none'; } else if (typeof content === 'string') { container.innerHTML = escapeMarkup(content); } else { $(container).append(content); } }; return ZtreeResults; }); // 定义组件 $.fn.select2.amd.define("jquery.select2ztree", [ 'jquery', 'jquery-mousewheel', './select2/core', './select2/defaults', './select2/utils', "./select2/ztreeresults", "select2/selection/ztreeSingle", "select2/data/ztree" ], function($, _, Select2, Defaults, Utils, ZtreeResults, ZtreeSingleSelection, ZtreeSelectAdapter) { if ($.fn.select2ztree) { return Select2; } var thisMethods = [ 'open', 'close', 'destroy' ]; // 定义jquery对象名称 $.fn.select2ztree = function(options) { options = options || {}; if (typeof options === 'object') { // 初始化属性 // 值属性名称 options.valueField = options.valueField || 'id'; // 文本属性名称 options.textField = options.textField || 'text'; options.resultsAdapter = ZtreeResults; if ($(this).attr('multiple')) { options.selectionAdapter = ZtreeMultiSelection; } else { options.selectionAdapter = ZtreeSingleSelection; } options.dataAdapter = ZtreeSelectAdapter; options.templateSelection = function(selection) { // 模糊搜索控件会将节点名称的实际值保存在oldname属性中 if (selection.oldname) { return selection.oldname; } return selection[options.textField]; }; if (!options.ztree || !options.ztree.setting) { throw new Error('缺少ztree配置: {ztree:{setting:{},zNodes:[]}}'); } if(options.ztree.zNodes){ //禁止节点进行url跳转 $.each(options.ztree.zNodes,function(idx,ele){ ele.url = ''; }); } //覆盖视图配置 options.ztree.setting.view={ selectedMulti : false, dblClickExpand : false } var self = this; // 重写ztree点击事件:点击之后用于触发select2相关操作 options.ztree.setting.callback = { onClick : function(event, treeId, treeNode, clickFlag) { // 触发select2的选择操作 var data = []; data.push(treeNode[options.valueField]); self.select2ztree('trigger', 'select', { data : treeNode, originalEvent : event }); } }; this.each(function() { var instanceOptions = $.extend(true, {}, options); new Select2($(this), instanceOptions); }); return this; } else if (typeof options === 'string') { var ret; var args = Array.prototype.slice.call(arguments, 1); this.each(function() { var instance = Utils.GetData(this, 'select2'); if (instance == null && window.console && console.error) { console.error('select2ztree(\'' + options + '\') method was called on an ' + 'element that is not using Select2.'); } ret = instance[options].apply(instance, args); }); if ($.inArray(options, thisMethods) > -1) { return this; } return ret; } else { throw new Error('错误的配置参数:' + options); } }; $.fn.select2ztree.defaults = Defaults; return Select2; }); jQuery.fn.select2.amd.require('jquery.select2ztree'); }(jQuery); ​

    css依赖

    bootstrap.min.css、select2.min.css、bootstrapStyle.css

    bootstrapStyle.css代码如下:

    .ztree * {
        padding: 0;
        margin: 0;
        font-size: 14px;
        font-family: Verdana,Arial,Helvetica,AppleGothic,sans-serif
    }
    
    .ztree {
        margin: 0;
        padding: 5px;
        color: #333
    }
    
    .ztree li {
        padding: 0;
        margin: 0;
        list-style: none;
        line-height: 17px;
        text-align: left;
        white-space: nowrap;
        outline: 0
    }
    
    .ztree li ul {
        margin: 0;
        padding: 0 0 0 18px
    }
    
    .ztree li ul.line {
        background: url(./img/line_conn.png) 0 0 repeat-y
    }
    
    .ztree li a {
        padding-right: 3px;
        margin: 0;
        cursor: pointer;
        height: 21px;
        color: #333;
        background-color: transparent;
        text-decoration: none;
        vertical-align: top;
        display: inline-block
    }
    
    .ztree li a:hover {
        text-decoration: underline
    }
    
    .ztree li a.curSelectedNode {
        padding-top: 0;
        background-color: #e5e5e5;
        color: #000;
        height: 21px;
        opacity: .8
    }
    
    .ztree li a.curSelectedNode_Edit {
        padding-top: 0;
        background-color: #e5e5e5;
        color: #000;
        height: 21px;
        border: 1px #666 solid;
        opacity: .8
    }
    
    .ztree li a.tmpTargetNode_inner {
        padding-top: 0;
        background-color: #aaa;
        color: #fff;
        height: 21px;
        border: 1px #666 solid;
        opacity: .8;
        filter: alpha(opacity=80)
    }
    
    .ztree li a.tmpTargetNode_prev {
    }
    
    .ztree li a.tmpTargetNode_next {
    }
    
    .ztree li a input.rename {
        height: 14px;
        width: 80px;
        padding: 0;
        margin: 0;
        font-size: 12px;
        border: 1px #585956 solid;
        *border: 0px
    }
    
    .ztree li span {
        line-height: 21px;
        margin-right: 2px
    }
    
    .ztree li span.button {
        line-height: 0;
        margin: 0;
        padding: 0;
        width: 21px;
        height: 21px;
        display: inline-block;
        vertical-align: middle;
        border: 0;
        cursor: pointer;
        outline: 0;
        background-color: transparent;
        background-repeat: no-repeat;
        background-attachment: scroll;
        background-image: url(./img/bootstrap.png);
        *background-image: url("./img/bootstrap.gif")
    }
    
    .ztree li span.button.chk {
        width: 13px;
        height: 13px;
        margin: 0 2px;
        cursor: auto
    }
    
    .ztree li span.button.chk.checkbox_false_full {
        background-position: -5px -5px
    }
    
    .ztree li span.button.chk.checkbox_false_full_focus {
        background-position: -5px -26px
    }
    
    .ztree li span.button.chk.checkbox_false_part {
        background-position: -5px -48px
    }
    
    .ztree li span.button.chk.checkbox_false_part_focus {
        background-position: -5px -68px
    }
    
    .ztree li span.button.chk.checkbox_false_disable {
        background-position: -5px -89px
    }
    
    .ztree li span.button.chk.checkbox_true_full {
        background-position: -26px -5px
    }
    
    .ztree li span.button.chk.checkbox_true_full_focus {
        background-position: -26px -26px
    }
    
    .ztree li span.button.chk.checkbox_true_part {
        background-position: -26px -48px
    }
    
    .ztree li span.button.chk.checkbox_true_part_focus {
        background-position: -26px -68px
    }
    
    .ztree li span.button.chk.checkbox_true_disable {
        background-position: -26px -89px
    }
    
    .ztree li span.button.chk.radio_false_full {
        background-position: -47px -5px
    }
    
    .ztree li span.button.chk.radio_false_full_focus {
        background-position: -47px -26px
    }
    
    .ztree li span.button.chk.radio_false_part {
        background-position: -47px -47px
    }
    
    .ztree li span.button.chk.radio_false_part_focus {
        background-position: -47px -68px
    }
    
    .ztree li span.button.chk.radio_false_disable {
        background-position: -47px -89px
    }
    
    .ztree li span.button.chk.radio_true_full {
        background-position: -68px -5px
    }
    
    .ztree li span.button.chk.radio_true_full_focus {
        background-position: -68px -26px
    }
    
    .ztree li span.button.chk.radio_true_part {
        background-position: -68px -47px
    }
    
    .ztree li span.button.chk.radio_true_part_focus {
        background-position: -68px -68px
    }
    
    .ztree li span.button.chk.radio_true_disable {
        background-position: -68px -89px
    }
    
    .ztree li span.button.switch {
        width: 21px;
        height: 21px
    }
    
    .ztree li span.button.root_open {
        background-position: -105px -63px
    }
    
    .ztree li span.button.root_close {
        background-position: -126px -63px
    }
    
    .ztree li span.button.roots_open {
        background-position: -105px 0
    }
    
    .ztree li span.button.roots_close {
        background-position: -126px 0
    }
    
    .ztree li span.button.center_open {
        background-position: -105px -21px
    }
    
    .ztree li span.button.center_close {
        background-position: -126px -21px
    }
    
    .ztree li span.button.bottom_open {
        background-position: -105px -42px
    }
    
    .ztree li span.button.bottom_close {
        background-position: -126px -42px
    }
    
    .ztree li span.button.noline_open {
        background-position: -105px -84px
    }
    
    .ztree li span.button.noline_close {
        background-position: -126px -84px
    }
    
    .ztree li span.button.root_docu {
        background: 0 0
    }
    
    .ztree li span.button.roots_docu {
        background-position: -84px 0
    }
    
    .ztree li span.button.center_docu {
        background-position: -84px -21px
    }
    
    .ztree li span.button.bottom_docu {
        background-position: -84px -42px
    }
    
    .ztree li span.button.noline_docu {
        background: 0 0
    }
    
    .ztree li span.button.ico_open {
        margin-right: 2px;
        background-position: -147px -21px;
        vertical-align: top;
        *vertical-align: middle
    }
    
    .ztree li span.button.ico_close {
        margin-right: 2px;
        margin-right: 2px;
        background-position: -147px 0;
        vertical-align: top;
        *vertical-align: middle
    }
    
    .ztree li span.button.ico_docu {
        margin-right: 2px;
        background-position: -147px -43px;
        vertical-align: top;
        *vertical-align: middle
    }
    
    .ztree li span.button.edit {
        margin-left: 2px;
        margin-right: -1px;
        background-position: -189px -21px;
        vertical-align: top;
        *vertical-align: middle
    }
    
    .ztree li span.button.edit:hover {
        background-position: -168px -21px
    }
    
    .ztree li span.button.remove {
        margin-left: 2px;
        margin-right: -1px;
        background-position: -189px -42px;
        vertical-align: top;
        *vertical-align: middle
    }
    
    .ztree li span.button.remove:hover {
        background-position: -168px -42px
    }
    
    .ztree li span.button.add {
        margin-left: 2px;
        margin-right: -1px;
        background-position: -189px 0;
        vertical-align: top;
        *vertical-align: middle
    }
    
    .ztree li span.button.add:hover {
        background-position: -168px 0
    }
    
    .ztree li span.button.ico_loading {
        margin-right: 2px;
        background: url(./img/loading.gif) no-repeat scroll 0 0 transparent;
        vertical-align: top;
        *vertical-align: middle
    }
    
    ul.tmpTargetzTree {
        background-color: #ffe6b0;
        opacity: .8;
        filter: alpha(opacity=80)
    }
    
    span.tmpzTreeMove_arrow {
        width: 16px;
        height: 21px;
        display: inline-block;
        padding: 0;
        margin: 2px 0 0 1px;
        border: 0;
        position: absolute;
        background-color: transparent;
        background-repeat: no-repeat;
        background-attachment: scroll;
        background-position: -168px -84px;
        background-image: url(./img/bootstrap.png);
        *background-image: url("./img/bootstrap.gif")
    }
    
    ul.ztree.zTreeDragUL {
        margin: 0;
        padding: 0;
        position: absolute;
        width: auto;
        height: auto;
        overflow: hidden;
        background-color: #cfcfcf;
        border: 1px #00b83f dotted;
        opacity: .8;
        filter: alpha(opacity=80)
    }
    
    .ztreeMask {
        z-index: 10000;
        background-color: #cfcfcf;
        opacity: 0;
        filter: alpha(opacity=0);
        position: absolute
    }
    

     bootstrapStyle.css需要的图片,请下载后放到与bootstrapStyle.css相同目录下的img:bootstrap.png和line_conn.png;line_conn.png这张图就是一个小黑点,大家注意一下

    bootstrap select2插件整合ztree实现树形下拉框_第1张图片line_conn.png

    js依赖:

    jquery.min.js(3.3.1)、bootstrap.min.js(3.4.1)、jquery.ztree.all.min.js(3.5.40)、jquery.ztree.exhide.js、select2.js(4.0.8)、以及ztree模糊搜索扩展

    /**
     * ztree模糊搜索
     * 
     * @version: v1.0.0
     */
    !function($) {
    	/**
    	 * @param searchText
    	 *            模糊搜索文本内容
    	 * @param isHighLight
    	 *            是否高亮,默认高亮,传入false禁用
    	 * @param isExpand
    	 *            是否展开,默认合拢,传入true展开
    	 * 
    	 * @returns
    	 */
    	var _init = $.fn.zTree.init;
    	$.fn.zTree.init = function(obj, zSetting, zNodes) {
    		var zTreeTools = _init(obj, zSetting, zNodes);
    		// 为ztree对象添加模糊搜索方法
    		zTreeTools.fuzzySearch = function(searchText, isHighLight, isExpand) {
    			var zTreeObj = this;
    			var nameKey = zTreeObj.setting.data.key.name;
    			isHighLight = isHighLight === false ? false : true;
    			isExpand = isExpand ? true : false;
    			zTreeObj.setting.view.nameIsHTML = isHighLight;
    			var metaChar = '[\\[\\]\\\\\^\\$\\.\\|\\?\\*\\+\\(\\)]';
    			var rexMeta = new RegExp(metaChar, 'gi');
    			function ztreeFilter(zTreeObj, _keywords, callBackFunc) {
    				if (!_keywords) {
    					_keywords = '';
    				}
    				function filterFunc(node) {
    					if (node && node.oldname && node.oldname.length > 0) {
    						node[nameKey] = node.oldname;
    					}
    					zTreeObj.updateNode(node);
    					if (_keywords.length == 0) {
    						zTreeObj.showNode(node);
    						zTreeObj.expandNode(node, isExpand);
    						return true;
    					}
    					if (node[nameKey] && node[nameKey].toLowerCase().indexOf(_keywords.toLowerCase()) != -1) {
    						if (isHighLight) {
    							var newKeywords = _keywords.replace(rexMeta, function(matchStr) {
    								return '\\' + matchStr;
    							});
    							node.oldname = node[nameKey];
    							var rexGlobal = new RegExp(newKeywords, 'gi');
    							node[nameKey] = node.oldname.replace(rexGlobal, function(originalText) {
    								var highLightText = '' + originalText + '';
    								return highLightText;
    							});
    							zTreeObj.updateNode(node);
    						}
    						zTreeObj.showNode(node);
    						return true;
    					}
    
    					zTreeObj.hideNode(node);
    					return false;
    				}
    
    				var nodesShow = zTreeObj.getNodesByFilter(filterFunc);
    				processShowNodes(nodesShow, _keywords);
    				return nodesShow;
    			}
    
    			/**
    			 * 展示搜索结果前处理节点
    			 */
    			function processShowNodes(nodesShow, _keywords) {
    				if (nodesShow && nodesShow.length > 0) {
    					if (_keywords.length > 0) {
    						$.each(nodesShow, function(n, obj) {
    							var pathOfOne = obj.getPath();
    							if (pathOfOne && pathOfOne.length > 0) {
    								for (var i = 0; i < pathOfOne.length - 1; i++) {
    									zTreeObj.showNode(pathOfOne[i]);
    									zTreeObj.expandNode(pathOfOne[i], true);
    								}
    							}
    						});
    					} else {
    						var rootNodes = zTreeObj.getNodesByParam('level', '0');
    						$.each(rootNodes, function(n, obj) {
    							zTreeObj.expandNode(obj, true);
    						});
    					}
    				}
    			}
    			return ztreeFilter(zTreeObj, searchText);
    		}
    		return zTreeTools;
    	};
    }(jQuery);
    

     

    组件初始化代码如下:

    var setting = {
    				view : {
    					dblClickExpand : false
    				},
    				data : {
    					simpleData : {
    						enable : true
    					}
    				}
    			};
    
    			var zNodes = [ {
    				id : 1,
    				pId : 0,
    				name : "北京"
    			}, {
    				id : 2,
    				pId : 0,
    				name : "天津"
    			}, {
    				id : 3,
    				pId : 0,
    				name : "上海"
    			}, {
    				id : 6,
    				pId : 0,
    				name : "重庆"
    			}, {
    				id : 4,
    				pId : 0,
    				name : "河北省",
    				open : true
    			}, {
    				id : 41,
    				pId : 4,
    				name : "石家庄"
    			}, {
    				id : 42,
    				pId : 4,
    				name : "保定"
    			}, {
    				id : 43,
    				pId : 4,
    				name : "邯郸"
    			}, {
    				id : 44,
    				pId : 4,
    				name : "承德"
    			}, {
    				id : 5,
    				pId : 0,
    				name : "广东省",
    				open : true
    			}, {
    				id : 51,
    				pId : 5,
    				name : "广州"
    			}, {
    				id : 52,
    				pId : 5,
    				name : "深圳"
    			}, {
    				id : 53,
    				pId : 5,
    				name : "东莞"
    			}, {
    				id : 54,
    				pId : 5,
    				name : "佛山"
    			}, {
    				id : 6,
    				pId : 0,
    				name : "福建省",
    				open : true
    			}, {
    				id : 61,
    				pId : 6,
    				name : "福州"
    			}, {
    				id : 62,
    				pId : 6,
    				name : "厦门"
    			}, {
    				id : 63,
    				pId : 6,
    				name : "泉州"
    			}, {
    				id : 64,
    				pId : 6,
    				name : "三明"
    			} ];
    			//初始化树形下拉框
    			var select2ztree = $('#select2').select2ztree({
    				textField : 'name',
    				titleField : 'name',
    				ztree : {
    					setting : setting,
    					zNodes : zNodes
    				}
    			});
                //注意,select2的赋值方式与jquery常规的val赋值不一样,必须使用下面的方式
                $('#select2').select2ztree('val',[4]);

    最终实现的效果如下图

    bootstrap select2插件整合ztree实现树形下拉框_第2张图片

    你可能感兴趣的:(bootstrap插件,ztree扩展,select2扩展)