jquery ui widget 源代码分析

jquery ui 的全部组件都是基于一个简单,可重用的widget。

这个widget是jquery ui的核心部分,有用它能实现一致的API。创建有状态的插件,而无需关心插件的内部转换。

$.widget( name, base, prototype )

widget一共同拥有2或3个參数。base为可选。

这里之所以把base放在第二个參数里,主要是由于这样写代码更直观一些。(由于后面的prototype 是个代码很长的大对象)。

name:第一个參数是一个包括一个命名空间和组件名称的字符串,通过”.”来切割。

命名空间必须有。它指向widget prototype存储的全局jQuery对象。

假设命名空间没有,widget factory将会为你生成。

widget name是插件函数和原型的真实名称,

比方: jQuery.widget( “demo.multi”, {…} ) 将会生成 jQuery.demo , jQuery.demo.multi , and jQuery.demo.multi.prototype .

base:第二个參数(可选)是 widget prototype继承于什么对象。

比如jQuery UI有一个“mouse”的插件,它能够作为其它的插件提供的基础。

为了实现这个全部的基于mouse的插件比方draggable,

droppable能够这么做: jQuery.widget( "ui.draggable",
$.ui.mouse, {...} );

假设没有这个參数。widget默认继承自“base widget” jQuery.Widget(注意jQuery.widget 和 jQuery.Widget不同) 。

prototype:最后一个參数是一个对象文字。它会转化为全部widget实例的prototype。widget factory会生成属性链,连接到她继承的widget的prototype。一直到最主要的 jQuery.Widget。

一旦你调用jQuery.widget。它会在jQuery
prototype ( jQuery.fn )
上生成一个新的可用方法相应于widget的名字。比方我们这个样例jQuery.fn.multi。

.fn方法是包括Dom元素的jquery对象和你生成的 widget prototyp实例的接口。为每个jQuery对象生成一个新的widget的实例。

/*!
 * jQuery UI Widget @VERSION
 * http://jqueryui.com
 *
 * Copyright 2014 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://api.jqueryui.com/jQuery.widget/
 */

//这里判定是否支持amd or cmd 模式
(function(factory) {
	if (typeof define === "function" && define.amd) {

		// AMD. Register as an anonymous module.
		define(["jquery"], factory);
	} else {

		// Browser globals
		factory(jQuery);
	}
}(function($) {

	var widget_uuid = 0,
		//插件的实例化数量
		widget_slice = Array.prototype.slice; //数组的slice方法,这里的作用是将參赛arguments 转为真正的数组

	//清除插件的数据及缓存
	$.cleanData = (function(orig) {
		return function(elems) {
			for (var i = 0, elem;
			(elem = elems[i]) != null; i++) {
				try {
					// 重写cleanData方法,调用后触发每一个元素的remove事件
					$(elem).triggerHandler("remove");
					// http://bugs.jquery.com/ticket/8235
				} catch (e) {}
			}
			orig(elems);
		};
	})($.cleanData);

	/**
	 * widget工厂方法。用于创建插件
	 * @param name 包括命名空间的插件名称。格式 xx.xxx
	 * @param base 须要继承的ui组件
	 * @param prototype 插件的实际代码
	 * @returns {Function}
	 */
	$.widget = function(name, base, prototype) {
		var fullName, //插件全称
		existingConstructor, //原有的构造函数
		constructor, //当前构造函数
		basePrototype, //父类的Prototype
		// proxiedPrototype allows the provided prototype to remain unmodified
		// so that it can be used as a mixin for multiple widgets (#8876)
		proxiedPrototype = {},
			//可调用父类方法_spuer的prototype对象,扩展于prototype
			namespace = name.split(".")[0];

		name = name.split(".")[1];
		fullName = namespace + "-" + name;
		//假设仅仅有2个參数  base默觉得Widget类,组件默认会继承base类的全部方法
		if (!prototype) {
			prototype = base;
			base = $.Widget;
		}

		//    console.log(base, $.Widget)

		// create selector for plugin
		//创建一个自己定义的伪类选择器
		//如 $(‘:ui-menu‘) 则表示选择定义了ui-menu插件的元素
		$.expr[":"][fullName.toLowerCase()] = function(elem) {
			return !!$.data(elem, fullName);
		};

		// 判定命名空间对象是否存在,没有的话 则创建一个空对象
		$[namespace] = $[namespace] || {};
		//这里存一份旧版的插件,假设这个插件已经被使用或者定义了
		existingConstructor = $[namespace][name];
		//这个是插件实例化的主要部分
		//constructor存储了插件的实例,同一时候也创建了基于命名空间的对象
		//如$.ui.menu
		constructor = $[namespace][name] = function(options, element) {
			// allow instantiation without "new" keyword
			//同意直接调用命名空间上的方法来创建组件
			//比方:$.ui.menu({},‘#id‘) 这样的方式创建的话,默认没有new 实例化。

由于_createWidget是prototype上的方法,须要new关键字来实例化
			//通过 调用 $.ui.menu 来实例化插件
			if (!this._createWidget) {
				console.info(this)
				return new constructor(options, element);
			}

			// allow instantiation without initializing for simple inheritance
			// must use "new" keyword (the code above always passes args)
			//假设存在參数,则说明是正常调用插件
			//_createWidget是创建插件的核心方法
			if (arguments.length) {
				this._createWidget(options, element);
			}
		};
		// extend with the existing constructor to carry over any static properties
		//合并对象。将旧插件实例,及版本、prototype合并到constructor
		$.extend(constructor, existingConstructor, {

			version: prototype.version,
			// copy the object used to create the prototype in case we need to
			// redefine the widget later
			//创建一个新的插件对象
			//将插件实例暴露给外部,可用户改动及覆盖
			_proto: $.extend({}, prototype),
			// track widgets that inherit from this widget in case this widget is
			// redefined after a widget inherits from it
			_childConstructors: []
		});

		//实例化父类 获取父类的  prototype
		basePrototype = new base();
		// we need to make the options hash a property directly on the new instance
		// otherwise we‘ll modify the options hash on the prototype that we‘re
		// inheriting from
		//这里深复制一份options
		basePrototype.options = $.widget.extend({}, basePrototype.options);
		//在传入的ui原型中有方法调用this._super 和this.__superApply会调用到base上(最基类上)的方法
		$.each(prototype, function(prop, value) {
			//假设val不是function 则直接给对象赋值字符串
			if (!$.isFunction(value)) {
				proxiedPrototype[prop] = value;
				return;
			}
			//假设val是function
			proxiedPrototype[prop] = (function() {
				//两种调用父类函数的方法
				var _super = function() {
						//将当期实例调用父类的方法
						return base.prototype[prop].apply(this, arguments);
					},
					_superApply = function(args) {
						return base.prototype[prop].apply(this, args);
					};
				return function() {
					var __super = this._super,
						__superApply = this._superApply,
						returnValue;
					//                console.log(prop, value,this,this._super,‘===‘)
					//                debugger;
					//在这里调用父类的函数
					this._super = _super;
					this._superApply = _superApply;

					returnValue = value.apply(this, arguments);

					this._super = __super;
					this._superApply = __superApply;
					//                console.log(this,value,returnValue,prop,‘===‘)
					return returnValue;
				};
			})();
		});
		//    console.info(proxiedPrototype)
		//    debugger;
		//这里是实例化获取的内容
		constructor.prototype = $.widget.extend(basePrototype, {
			// TODO: remove support for widgetEventPrefix
			// always use the name + a colon as the prefix, e.g., draggable:start
			// don‘t prefix for widgets that aren‘t DOM-based
			widgetEventPrefix: existingConstructor ?

(basePrototype.widgetEventPrefix || name) : name
		}, proxiedPrototype, {
			//又一次把constructor指向 constructor 变量
			constructor: constructor,
			namespace: namespace,
			widgetName: name,
			widgetFullName: fullName
		});

		// If this widget is being redefined then we need to find all widgets that
		// are inheriting from it and redefine all of them so that they inherit from
		// the new version of this widget. We‘re essentially trying to replace one
		// level in the prototype chain.
		//这里判定插件是否被使用了。一般来说,都不会被使用的。

//由于插件的开发人员都是我们自己,呵呵
		if (existingConstructor) {
			$.each(existingConstructor._childConstructors, function(i, child) {
				var childPrototype = child.prototype;

				// redefine the child widget using the same prototype that was
				// originally used, but inherit from the new version of the base
				$.widget(childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto);
			});
			// remove the list of existing child constructors from the old constructor
			// so the old child constructors can be garbage collected
			delete existingConstructor._childConstructors;
		} else {
			//父类加入当前插件的实例 主要用于作用域链查找 不至于断层
			base._childConstructors.push(constructor);
		}

		//将此方法挂在jQuery对象上
		$.widget.bridge(name, constructor);

		return constructor;
	};

	//扩展jq的extend方法,实际上相似$.extend(true,..) 深复制
	$.widget.extend = function(target) {
		var input = widget_slice.call(arguments, 1),
			inputIndex = 0,
			inputLength = input.length,
			key, value;
		for (; inputIndex < inputLength; inputIndex++) {
			for (key in input[inputIndex]) {
				value = input[inputIndex][key];
				if (input[inputIndex].hasOwnProperty(key) && value !== undefined) {
					// Clone objects
					if ($.isPlainObject(value)) {
						target[key] = $.isPlainObject(target[key]) ? $.widget.extend({}, target[key], value) :
						// Don‘t extend strings, arrays, etc. with objects
						$.widget.extend({}, value);
						// Copy everything else by reference
					} else {
						target[key] = value;
					}
				}
			}
		}
		return target;
	};

	//bridge 是设计模式的一种,这里将对象转为插件调用
	$.widget.bridge = function(name, object) {
		var fullName = object.prototype.widgetFullName || name;
		//这里就是插件了
		//这部分的实现主要做了几个工作,也是制作一个优雅的插件的主要代码
		//1、初次实例化时将插件对象缓存在dom上,兴许则可直接调用,避免在同样元素下widget的多实例化。

简单的说,就是一个单例方法。
		//2、合并用户提供的默认设置选项options
		//3、能够通过调用插件时传递字符串来调用插件内的方法。

如:$(‘#id‘).menu(‘hide‘) 实际就是实例插件并调用hide()方法。

//4、同一时候限制外部调用“_”下划线的私有方法
		$.fn[name] = function(options) {
			var isMethodCall = typeof options === "string",
				args = widget_slice.call(arguments, 1),
				returnValue = this;

			// allow multiple hashes to be passed on init.
			//能够简单觉得是$.extend(true,options,args[0],...),args能够是一个參数或是数组
			options = !isMethodCall && args.length ? $.widget.extend.apply(null, [options].concat(args)) : options;
			//这里对字符串和对象分别作处理
			if (isMethodCall) {
				this.each(function() {
					var methodValue, instance = $.data(this, fullName);
					//假设传递的是instance则将this返回。
					if (options === "instance") {
						returnValue = instance;
						return false;
					}
					if (!instance) {
						return $.error("cannot call methods on " + name + " prior to initialization; " + "attempted to call method ‘" + options + "‘");
					}
					//这里对私有方法的调用做了限制,直接调用会抛出异常事件
					if (!$.isFunction(instance[options]) || options.charAt(0) === "_") {
						return $.error("no such method ‘" + options + "‘ for " + name + " widget instance");
					}
					//这里是假设传递的是字符串,则调用字符串方法,并传递相应的參数.
					//比方插件有个方法hide(a,b); 有2个參数:a。b
					//则调用时$(‘#id‘).menu(‘hide‘,1,2);//1和2 分别就是參数a和b了。
					methodValue = instance[options].apply(instance, args);
					if (methodValue !== instance && methodValue !== undefined) {
						returnValue = methodValue && methodValue.jquery ?

returnValue.pushStack(methodValue.get()) : methodValue;
						return false;
					}
				});
			} else {
				this.each(function() {
					var instance = $.data(this, fullName);

					if (instance) {
						instance.option(options || {});
						//这里每次都调用init方法
						if (instance._init) {
							instance._init();
						}
					} else {
						//缓存插件实例
						$.data(this, fullName, new object(options, this));
					}
				});
			}

			return returnValue;
		};
	};

	//这里是真正的widget基类
	$.Widget = function( /* options, element */ ) {};
	$.Widget._childConstructors = [];

	$.Widget.prototype = {
		widgetName: "widget",
		//用来决定事件的名称和插件提供的callbacks的关联。
		// 比方dialog有一个close的callback,当close的callback被运行的时候,一个dialogclose的事件被触发。
		// 事件的名称和事件的prefix+callback的名称。widgetEventPrefix 默认就是控件的名称。可是假设事件须要不同的名称也能够被重写。
		// 比方一个用户開始拖拽一个元素,我们不想使用draggablestart作为事件的名称,我们想使用dragstart,所以我们能够重写事件的prefix。
		// 假设callback的名称和事件的prefix同样,事件的名称将不会是prefix。

// 它阻止像dragdrag一样的事件名称。

widgetEventPrefix: "",
		defaultElement: "<div>",
		//属性会在创建模块时被覆盖
		options: {
			disabled: false,

			// callbacks
			create: null
		},
		_createWidget: function(options, element) {
			element = $(element || this.defaultElement || this)[0];
			this.element = $(element);
			this.uuid = widget_uuid++;
			this.eventNamespace = "." + this.widgetName + this.uuid;
			this.options = $.widget.extend({}, this.options, this._getCreateOptions(), options);

			this.bindings = $();
			this.hoverable = $();
			this.focusable = $();

			if (element !== this) {
				//            debugger
				$.data(element, this.widgetFullName, this);
				this._on(true, this.element, {
					remove: function(event) {
						if (event.target === element) {
							this.destroy();
						}
					}
				});
				this.document = $(element.style ?
				// element within the document
				element.ownerDocument :
				// element is window or document
				element.document || element);
				this.window = $(this.document[0].defaultView || this.document[0].parentWindow);
			}

			this._create();
			//创建插件时,有个create的回调
			this._trigger("create", null, this._getCreateEventData());
			this._init();
		},
		_getCreateOptions: $.noop,
		_getCreateEventData: $.noop,
		_create: $.noop,
		_init: $.noop,
		//销毁模块:去除绑定事件、去除数据、去除样式、属性
		destroy: function() {
			this._destroy();
			// we can probably remove the unbind calls in 2.0
			// all event bindings should go through this._on()
			this.element.unbind(this.eventNamespace).removeData(this.widgetFullName)
			// support: jquery <1.6.3
			// http://bugs.jquery.com/ticket/9413
			.removeData($.camelCase(this.widgetFullName));
			this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(
			this.widgetFullName + "-disabled " + "ui-state-disabled");

			// clean up events and states
			this.bindings.unbind(this.eventNamespace);
			this.hoverable.removeClass("ui-state-hover");
			this.focusable.removeClass("ui-state-focus");
		},
		_destroy: $.noop,

		widget: function() {
			return this.element;
		},
		//设置选项函数
		option: function(key, value) {
			var options = key,
				parts, curOption, i;

			if (arguments.length === 0) {
				// don‘t return a reference to the internal hash
				//返回一个新的对象。不是内部数据的引用
				return $.widget.extend({}, this.options);
			}

			if (typeof key === "string") {
				// handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
				options = {};
				parts = key.split(".");
				key = parts.shift();
				if (parts.length) {
					curOption = options[key] = $.widget.extend({}, this.options[key]);
					for (i = 0; i < parts.length - 1; i++) {
						curOption[parts[i]] = curOption[parts[i]] || {};
						curOption = curOption[parts[i]];
					}
					key = parts.pop();
					if (arguments.length === 1) {
						return curOption[key] === undefined ? null : curOption[key];
					}
					curOption[key] = value;
				} else {
					if (arguments.length === 1) {
						return this.options[key] === undefined ? null : this.options[key];
					}
					options[key] = value;
				}
			}

			this._setOptions(options);

			return this;
		},
		_setOptions: function(options) {
			var key;

			for (key in options) {
				this._setOption(key, options[key]);
			}

			return this;
		},
		_setOption: function(key, value) {
			this.options[key] = value;

			if (key === "disabled") {
				this.widget().toggleClass(this.widgetFullName + "-disabled", !! value);

				// If the widget is becoming disabled, then nothing is interactive
				if (value) {
					this.hoverable.removeClass("ui-state-hover");
					this.focusable.removeClass("ui-state-focus");
				}
			}

			return this;
		},

		enable: function() {
			return this._setOptions({
				disabled: false
			});
		},
		disable: function() {
			return this._setOptions({
				disabled: true
			});
		},

		_on: function(suppressDisabledCheck, element, handlers) {
			var delegateElement, instance = this;

			// no suppressDisabledCheck flag, shuffle arguments
			if (typeof suppressDisabledCheck !== "boolean") {
				handlers = element;
				element = suppressDisabledCheck;
				suppressDisabledCheck = false;
			}

			// no element argument, shuffle and use this.element
			if (!handlers) {
				handlers = element;
				element = this.element;
				delegateElement = this.widget();
			} else {
				// accept selectors, DOM elements
				element = delegateElement = $(element);
				this.bindings = this.bindings.add(element);
			}

			$.each(handlers, function(event, handler) {
				function handlerProxy() {
					// allow widgets to customize the disabled handling
					// - disabled as an array instead of boolean
					// - disabled class as method for disabling individual parts
					if (!suppressDisabledCheck && (instance.options.disabled === true || $(this).hasClass("ui-state-disabled"))) {
						return;
					}
					return (typeof handler === "string" ? instance[handler] : handler).apply(instance, arguments);
				}

				// copy the guid so direct unbinding works
				if (typeof handler !== "string") {
					handlerProxy.guid = handler.guid = handler.guid || handlerProxy.guid || $.guid++;
				}

				var match = event.match(/^([\w:-]*)\s*(.*)$/),
					eventName = match[1] + instance.eventNamespace,
					selector = match[2];
				if (selector) {
					delegateElement.delegate(selector, eventName, handlerProxy);
				} else {
					element.bind(eventName, handlerProxy);
				}
			});
		},

		_off: function(element, eventName) {
			eventName = (eventName || "").split(" ").join(this.eventNamespace + " ") + this.eventNamespace;
			element.unbind(eventName).undelegate(eventName);
		},

		_delay: function(handler, delay) {
			function handlerProxy() {
				return (typeof handler === "string" ? instance[handler] : handler).apply(instance, arguments);
			}
			var instance = this;
			return setTimeout(handlerProxy, delay || 0);
		},

		_hoverable: function(element) {
			this.hoverable = this.hoverable.add(element);
			this._on(element, {
				mouseenter: function(event) {
					$(event.currentTarget).addClass("ui-state-hover");
				},
				mouseleave: function(event) {
					$(event.currentTarget).removeClass("ui-state-hover");
				}
			});
		},

		_focusable: function(element) {
			this.focusable = this.focusable.add(element);
			this._on(element, {
				focusin: function(event) {
					$(event.currentTarget).addClass("ui-state-focus");
				},
				focusout: function(event) {
					$(event.currentTarget).removeClass("ui-state-focus");
				}
			});
		},

		_trigger: function(type, event, data) {
			var prop, orig, callback = this.options[type];

			data = data || {};
			event = $.Event(event);
			event.type = (type === this.widgetEventPrefix ? type : this.widgetEventPrefix + type).toLowerCase();
			// the original event may come from any element
			// so we need to reset the target on the new event
			event.target = this.element[0];

			// copy original event properties over to the new event
			orig = event.originalEvent;
			if (orig) {
				for (prop in orig) {
					if (!(prop in event)) {
						event[prop] = orig[prop];
					}
				}
			}

			this.element.trigger(event, data);
			return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());
		}
	};

	$.each({
		show: "fadeIn",
		hide: "fadeOut"
	}, function(method, defaultEffect) {
		$.Widget.prototype["_" + method] = function(element, options, callback) {
			if (typeof options === "string") {
				options = {
					effect: options
				};
			}
			var hasOptions, effectName = !options ? method : options === true || typeof options === "number" ?

defaultEffect : options.effect || defaultEffect;
			options = options || {};
			if (typeof options === "number") {
				options = {
					duration: options
				};
			}
			hasOptions = !$.isEmptyObject(options);
			options.complete = callback;
			if (options.delay) {
				element.delay(options.delay);
			}
			if (hasOptions && $.effects && $.effects.effect[effectName]) {
				element[method](options);
			} else if (effectName !== method && element[effectName]) {
				element[effectName](options.duration, options.easing, callback);
			} else {
				element.queue(function(next) {
					$(this)[method]();
					if (callback) {
						callback.call(element[0]);
					}
					next();
				});
			}
		};
	});

	return $.widget;

}));
时间: 2024-10-13 09:41:20

jquery ui widget 源代码分析的相关文章

jquery ui widget 源码分析

jquery ui 的所有组件都是基于一个简单,可重用的widget. 这个widget是jquery ui的核心部分,实用它能实现一致的API,创建有状态的插件,而无需关心插件的内部转换. $.widget( name, base, prototype ) widget一共有2或3个参数.base为可选. 这里之所以把base放在第二个参数里,主要是因为这样写代码更直观一些.(因为后面的prototype 是个代码非常长的大对象). name:第一个参数是一个包含一个命名空间和组件名称的字符串

通过扩展jQuery UI Widget Factory实现手动调整Accordion高度

□ 实现Accordion高度一致 <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <link href="~/Content/jquery-ui.min.css" rel="stylesheet" /> <script src="~/S

jQuery ui widget和jQuery plugin的实现原理简单比较

一.创建 1.  jQuery plugin (function($){ $.fn.MyPlugin=function(){ //js代码 } })(jQuery) 为了与页面上其他代码友好相处,将plugin定义在一个闭包里,MyPlugin是plugin的名字.调用方式:$('选择器').MyPlugin(); 2.  jquery ui widget (function($){ $.widget('ui.mywidget',{ options:{ //默认的配置参数 }, //方法的定义

jQuery UI Widget 原理

先看下代码的相关注释: Javascript代码 1 /*! 2 * jQuery UI Widget 1.8.1 3 * 4 * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) 5 * Dual licensed under the MIT (MIT-LICENSE.txt) 6 * and GPL (GPL-LICENSE.txt) licenses. 7 * 8 * http://docs.jquery.com/UI/W

jQuery UI Widget(1.8.1)工作原理--转载

先看下代码的相关注释: /*! * jQuery UI Widget 1.8.1 * * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. * * http://docs.jquery.com/UI/Widget */ (function( $ ) { var

jQuery UI Widget(1.8.1)工作原理

/*! * jQuery UI Widget 1.8.1 * * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. * * http://docs.jquery.com/UI/Widget */ (function( $ ) { var _remove = $

使用 jQuery UI Widget Factory 编写有状态的插件(Stateful Plugins)

使用 jQuery UI Widget Factory 编写有状态的插件(Stateful Plugins) Note 这一章节的内容是基于 Scott Gonzalez 一篇博客 Building Stateful jQuery Plugins(已获作者许可) 虽然大多数的 jQuery 插件都是无状态的(stateless),也就是说, 与插件进行交互的就限于调用插件时的那一组对象, 但是有好大一部分功能需求没办法通过这种简单的插件模式来实现. 为了填补这一空白,jQuery UI 实现一套

JQuery Sizzle引擎源代码分析

最近在拜读艾伦在慕课网上写的JQuery课程,感觉在国内对JQuery代码分析透彻的人没几个能比得过艾伦.有没有吹牛?是不是我说大话了? 什么是Sizzle引擎? 我们经常使用JQuery的选择器查询元素,查询的选择器有简单也有复杂:    简单点:“div”.“.navi”.“div.navi”. 复杂点:"div input[type='checkbox']"."div.navi + .section p". Query实现查询时也是优先使用DOM标准查询函数,

解决Select2控件不能在jQuery UI Dialog中不能搜索的bug

本文使用博客园Markdown编辑器进行编辑 1.问题呈现 项目中使用了jQuery UI的Dialog控件,一般用来处理需要提示用户输入或操作的简单页面.逻辑是修改一个广告的图片和标题. 效果截图如下: 使用Select2,主要是因为它支持下拉式搜索.所以在数据稍微多一点,作为搜索选择功能的首选.但是运行出来之后,发现搜索框无法点击.开始想到的index不够大,被其他的元素覆盖了.但是跳转z-index也无法解决.在普通的页面,搜索框是ok的. 2.解决办法 通过Google搜索,发现sele