├── .gitignore ├── LICENSE ├── README.md ├── a6028d29c6b277dfce24160713227cd7.png ├── index.html ├── jquery.dropdown.1.2.2.zip ├── jquery.dropdown.css ├── jquery.dropdown.js ├── jquery.dropdown.min.css ├── jquery.dropdown.min.js └── mock.js /.gitignore: -------------------------------------------------------------------------------- 1 | .history/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Janking 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dropdown 2 | Dropdown是面向PC端的基于jQuery开发的轻量级下拉框插件,支持key/value搜索,有token和select两种模式。 3 | 4 | ## Version 5 | - 1.2.0 6 | 7 | ## Support 8 | - Internet Explorer 8+ 9 | - Chrome for PC 10 | - Safari for PC 11 | - Firefox for PC 12 | 13 | ## Based 14 | - jQuery 1.4+ 15 | 16 | ## Log 17 | * 2018-05-28 version 1.2.2 18 | * 优化value值的匹配,[#33](/../../issues/33) 19 | * 2017-10-13 version 1.2.0 20 | * 新增方法 `reset` 21 | * 搜索支持分组名 22 | * 2017-08-05 version 1.1.7 23 | * 修复BUG [#6](/../../issues/8) 24 | 25 | * 2017-08-05 version 1.1.6 26 | * 新增自定义字段 `extendProps` 27 | * 新增方法 `choose` 28 | * 优化回调函数 `choice(event,data)`,新增第二个入参 29 | * [#6](/../../issues/6) 30 | * 2017-07-19 version 1.1.5 31 | * 新增 `init` 回调函数 32 | * 修复 [#4](/../../issues/4) 的问题 33 | * 2017-07-06 version 1.1.4 34 | * 修复`value`值为中文时的错误 [#2](/../../issues/2) 35 | * 修复单选无法触发回调问题 [#2](/../../issues/2) 36 | * 修复`destroy`时,事件未被移除的BUG [#3](/../../issues/3) 37 | * 2017-07-03 version 1.1.3 38 | * 新增 `update` 方法 39 | * 修复 `点击清除按钮默认提交表单` 的行为。 40 | * 2017-06-28 version 1.1.1 41 | * 修复 `IE8` 不兼容的问题 42 | 43 | * 2017-06-24 version 1.1.0 44 | * 多选 `select`模式下增加一个 **全部删除** 按钮 45 | * 新增 `changeStatus` 方法,提供`readonly`,`disabled`功能 46 | * 新增 `destroy`,`bindEvent`,`unbindEvent` 方法 47 | * 2017-06-21 version 1.0.0 上线 48 | 49 | 50 | ## Feature 51 | 1. 支持 `select` 和 `token` 两种模式 52 | 2. 支持 `optgroup` 分组 53 | 3. 保留原生 `select` 的键盘操作 54 | 4. 数据源可以直接通过接口 `data` 注入,也可以直接渲染 `select>option` ,由插件自动转换。 55 | 5. 插件同步 `select` 和 `ul>li` 标签,便于表单字段提交及前端校验, 56 | 57 | ## Principle 58 | **程序设计原理如下图所示:** 59 |  60 | 61 | 在一些前端渲染的场景,JSON数据是通过AJAX请求的,如果再拼成`xxx` 就有点多余了。 62 | 在这种情况下,建议直接将JSON数据转为以下这种格式: 63 | 64 | ```json 65 | [ 66 | { 67 | "id": 1, // value值 68 | "disabled": false, // 是否禁选 69 | "groupName": "分组名", 70 | "groupId": 3,//分组ID 71 | "selected": false, // 是否选中 72 | "name": "Betty Deborah Jackson" // 名称 73 | }, 74 | { 75 | "id": 2, 76 | "disabled": false, 77 | "groupName": "分组名", 78 | "groupId": 2, 79 | "selected": false, 80 | "name": "Jason Barbara Clark" 81 | } 82 | // more ... 83 | ] 84 | ``` 85 | 86 | Dropdown 会根据这个JSON来渲染 `select > option` 87 | 88 | ## Options 89 | | 名称 | 描述 | 类型|默认| 90 | | ----|-----|-----|----| 91 | | readOnly|是否只读|Boolean|`false`| 92 | | minCount|下限|Number|`0`| 93 | | minCountErrorMessage|下限自定義錯誤消息|String|最低选择个| 94 | | limitCount|选择上限|Number|`Infinity`| 95 | | limitCountErrorMessage|上限的自定义错误消息|String|最多可选择个| 96 | | input|搜索框模板|HTML|``| 97 | | data|数据源|Array|`[]`| 98 | | searchable|是否可开启搜索|Boolean|`true`| 99 | | searchNoData|无数据模板|HTML|`查无数据,换个词儿试试 /(ㄒoㄒ)/~~`| 100 | | choice|选择后回调函数|Function| `function(){}`| 101 | | init|插件初始化后回调函数|Function| `function(){}`| 102 | | extendProps|扩展自定义字段 `data-*`,字段名必须在`data`中存在,否则无效 **不建议扩展太多字段,会有性能影响**|Array| `['prop1','prop2']`| 103 | 104 | ## Methods 105 | 106 | ### changeStatus(status) 107 | 修改组件状态 108 | 109 | | 参数 |类型|描述| 110 | | ----|-----|-----| 111 | | status|String|支持`readonly`或`disabled`,不传则取消`readonly/disabled`| 112 | |return|undefined| 113 | 114 | 115 | ```js 116 | var dropdown = $('selector').dropdown(options).data('dropdown'); 117 | dropdown.changeStatus('readonly') // readonly 118 | dropdown.changeStatus('disabled') // disabled 119 | dropdown.changeStatus() // cancel 120 | 121 | ``` 122 | ### choose(value,status) 123 | 动态选择值 124 | 125 | | 参数 |类型|描述| 126 | | ----|-----|-----| 127 | | value | String\|Array | 需要被选中的值,多个值用数字 `['one','two','three']` 128 | | status|Boolean|选中或取消,默认为: true 129 | |return|undefined| 130 | 131 | 132 | ```js 133 | var dropdown = $('selector').dropdown(options).data('dropdown'); 134 | dropdown.destroy(); 135 | 136 | ``` 137 | 138 | ### update(Array,isCover) 139 | 更新数据 140 | 141 | | 参数 |类型|描述| 142 | | ----|-----|-----| 143 | | data|Array|需要更新的数据| 144 | | isCover|Boolean|是否覆盖原有数据,默认不覆盖| 145 | 146 | 147 | ### destroy() 148 | 销毁组件 149 | 150 | | 参数 |类型|描述| 151 | | ----|-----|-----| 152 | |return|undefined| 153 | 154 | 155 | 156 | ```js 157 | var dropdown = $('selector').dropdown(options).data('dropdown'); 158 | dropdown.destroy(); 159 | 160 | ``` 161 | 162 | 163 | ### reset() 164 | 重置 165 | 166 | | 参数 |类型|描述| 167 | | ----|-----|-----| 168 | |return|undefined| 169 | 170 | 171 | ```js 172 | var dropdown = $('selector').dropdown(options).data('dropdown'); 173 | dropdown.reset(); 174 | 175 | ``` 176 | 177 | ## Usage 178 | 引入 179 | 180 | ```html 181 | 182 | 183 | 184 | ``` 185 | 186 | HTML 部分 187 | 188 | ```html 189 | 190 | 191 | 192 | 1 193 | 2 194 | 3 195 | 4 196 | 5 197 | 6 198 | 7 199 | 8 200 | 9 201 | 10 202 | 11 203 | 12 204 | 205 | 206 | ``` 207 | JavaScript 部分 208 | 209 | ```js 210 | $('.dropdown-mul-1').dropdown({ 211 | limitCount: 40, 212 | multipleMode: 'label', 213 | choice: function () { 214 | console.log(arguments,this); 215 | } 216 | }); 217 | ``` 218 | 219 | 220 | ## Example 221 | 222 | [https://janking.github.io/dropdown/](https://janking.github.io/dropdown/) 223 | -------------------------------------------------------------------------------- /a6028d29c6b277dfce24160713227cd7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janking/dropdown/5b798d998c37f2b65b8a779b9a3f0da5947a7f26/a6028d29c6b277dfce24160713227cd7.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Dropdown.js - a lightweight dropdown of jQuery plugins 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 程序逻辑 26 | 27 | Demo 28 | 29 | 30 | 31 | 多选、JSON渲染、分组功能 32 | 33 | 34 | 35 | 36 | 37 | 38 | AJAX更新数据 39 | 40 | 41 | 42 | 43 | 44 | 45 | 多选、Option渲染 46 | 47 | 48 | 49 | 1 50 | 2 51 | 我是默认选中的 52 | 4 53 | 5 54 | 6 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 单选模式,Option渲染 64 | 65 | 66 | 67 | 1 68 | 2 69 | 我是默认选中的 70 | 4 71 | 5 72 | 6 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 100 条数据搜索测试 82 | 83 | 84 | 85 | 86 | 87 | 88 | AJAX更新数据 89 | reset 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /jquery.dropdown.1.2.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janking/dropdown/5b798d998c37f2b65b8a779b9a3f0da5947a7f26/jquery.dropdown.1.2.2.zip -------------------------------------------------------------------------------- /jquery.dropdown.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes iui-fadeIn { 2 | 0% { 3 | opacity: 0 4 | } 5 | 100% { 6 | opacity: 1 7 | } 8 | } 9 | 10 | @-moz-keyframes iui-fadeIn { 11 | 0% { 12 | opacity: 0 13 | } 14 | 100% { 15 | opacity: 1 16 | } 17 | } 18 | 19 | @-ms-keyframes iui-fadeIn { 20 | 0% { 21 | opacity: 0 22 | } 23 | 100% { 24 | opacity: 1 25 | } 26 | } 27 | 28 | @-o-keyframes iui-fadeIn { 29 | 0% { 30 | opacity: 0 31 | } 32 | 100% { 33 | opacity: 1 34 | } 35 | } 36 | 37 | @keyframes iui-fadeIn { 38 | 0% { 39 | opacity: 0 40 | } 41 | 100% { 42 | opacity: 1 43 | } 44 | } 45 | 46 | @-webkit-keyframes iui-fadeOut { 47 | 0% { 48 | opacity: 1 49 | } 50 | 100% { 51 | opacity: 0 52 | } 53 | } 54 | 55 | @-moz-keyframes iui-fadeOut { 56 | 0% { 57 | opacity: 1 58 | } 59 | 100% { 60 | opacity: 0 61 | } 62 | } 63 | 64 | @-ms-keyframes iui-fadeOut { 65 | 0% { 66 | opacity: 1 67 | } 68 | 100% { 69 | opacity: 0 70 | } 71 | } 72 | 73 | @-o-keyframes iui-fadeOut { 74 | 0% { 75 | opacity: 1 76 | } 77 | 100% { 78 | opacity: 0 79 | } 80 | } 81 | 82 | @keyframes iui-fadeOut { 83 | 0% { 84 | opacity: 1 85 | } 86 | 100% { 87 | opacity: 0 88 | } 89 | } 90 | 91 | .dropdown-multiple, 92 | .dropdown-multiple-label, 93 | .dropdown-single { 94 | position: relative 95 | } 96 | 97 | .dropdown-multiple-label.active .dropdown-main, 98 | .dropdown-multiple.active .dropdown-main, 99 | .dropdown-single.active .dropdown-main { 100 | display: block; 101 | -webkit-animation: iui-fadeIn .2s ease-in forwards; 102 | -moz-animation: iui-fadeIn .2s ease-in forwards; 103 | -ms-animation: iui-fadeIn .2s ease-in forwards; 104 | -o-animation: iui-fadeIn .2s ease-in forwards; 105 | animation: iui-fadeIn .2s ease-in forwards 106 | } 107 | 108 | .dropdown-multiple-label.active .dropdown-display-label:after, 109 | .dropdown-multiple-label.active .dropdown-display:after, 110 | .dropdown-multiple.active .dropdown-display-label:after, 111 | .dropdown-multiple.active .dropdown-display:after, 112 | .dropdown-single.active .dropdown-display-label:after, 113 | .dropdown-single.active .dropdown-display:after { 114 | border-top: none; 115 | border-bottom: 10px solid #999; 116 | border-left: 5px solid transparent; 117 | border-right: 5px solid transparent 118 | } 119 | 120 | .dropdown-multiple-label.active .dropdown-display, 121 | .dropdown-multiple-label.active .dropdown-display-label, 122 | .dropdown-multiple.active .dropdown-display, 123 | .dropdown-multiple.active .dropdown-display-label, 124 | .dropdown-single.active .dropdown-display, 125 | .dropdown-single.active .dropdown-display-label { 126 | border-bottom-left-radius: 0; 127 | border-bottom-right-radius: 0 128 | } 129 | 130 | .dropdown-display, 131 | .dropdown-display-label { 132 | position: relative; 133 | display: block; 134 | margin-bottom: 0; 135 | font-size: 14px; 136 | line-height: 1.42857143; 137 | vertical-align: middle; 138 | touch-action: manipulation; 139 | cursor: pointer; 140 | user-select: none; 141 | background-image: none; 142 | border: 1px solid #ccc; 143 | border-radius: 4px; 144 | color: #333; 145 | background-color: #fff 146 | } 147 | 148 | .dropdown-display-label:after, 149 | .dropdown-display:after { 150 | content: ''; 151 | position: absolute; 152 | border-top: 10px solid #999; 153 | border-left: 5px solid transparent; 154 | border-right: 5px solid transparent; 155 | top: 12px; 156 | right: 8px 157 | } 158 | 159 | .dropdown-clear-all { 160 | background-color: #fff; 161 | border: none; 162 | font-size: 22px; 163 | z-index: 999; 164 | color: #999; 165 | position: absolute; 166 | right: 2px; 167 | top: 2px; 168 | display: none; 169 | width: 25px; 170 | height: 30px; 171 | text-align: center; 172 | line-height: 30px; 173 | } 174 | 175 | .dropdown-clear-all:focus { 176 | outline: 0 177 | } 178 | 179 | .dropdown-clear-all:hover { 180 | color: #ccc; 181 | text-decoration: none; 182 | } 183 | 184 | .dropdown-display { 185 | white-space: nowrap; 186 | padding: 6px 20px 6px 12px 187 | } 188 | 189 | .dropdown-multiple:hover .dropdown-clear-all, 190 | .dropdown-single:hover .dropdown-clear-all { 191 | display: block 192 | } 193 | 194 | .dropdown-display .dropdown-chose-list { 195 | display: inline-block; 196 | vertical-align: middle; 197 | width: 100%; 198 | overflow: hidden; 199 | text-overflow: ellipsis; 200 | white-space: nowrap 201 | } 202 | 203 | .dropdown-display .dropdown-chose-list span:before { 204 | content: ',' 205 | } 206 | 207 | .dropdown-display .dropdown-chose-list span:first-child:before { 208 | content: '' 209 | } 210 | 211 | .dropdown-display .placeholder { 212 | display: none 213 | } 214 | 215 | .dropdown-display .placeholder:first-child { 216 | position: absolute; 217 | height: 100%; 218 | width: 100%; 219 | top: 0; 220 | left: 0; 221 | color: #999; 222 | display: block; 223 | text-indent: 10px; 224 | font-size: 13px; 225 | line-height: 32px 226 | } 227 | 228 | .dropdown-display input { 229 | border: 0; 230 | outline: 0 231 | } 232 | 233 | .dropdown-display-label { 234 | cursor: text; 235 | padding: 6px 25px 5px 0 236 | } 237 | 238 | .dropdown-display-label .dropdown-search { 239 | display: inline-block 240 | } 241 | 242 | .dropdown-display-label input, 243 | .dropdown-display-label input:focus { 244 | border: none; 245 | outline: 0 246 | } 247 | 248 | .dropdown-display-label .dropdown-chose-list { 249 | display: inline-block; 250 | padding: 0 5px 251 | } 252 | 253 | .dropdown-display-label .dropdown-chose-list .placeholder { 254 | display: none 255 | } 256 | 257 | .dropdown-display-label .dropdown-selected { 258 | position: relative; 259 | margin: 0 5px 5px 0; 260 | padding: 0 20px 0 5px; 261 | border: 1px solid #aaa; 262 | max-width: 100%; 263 | border-radius: 3px; 264 | background-repeat: repeat-x; 265 | color: #333; 266 | cursor: default; 267 | display: inline-block 268 | } 269 | 270 | .dropdown-display-label .dropdown-selected .del { 271 | -webkit-appearance: none; 272 | padding: 0; 273 | cursor: pointer; 274 | background: 0 0; 275 | border: 0; 276 | float: right; 277 | line-height: 1; 278 | color: #999; 279 | position: absolute; 280 | right: 3px; 281 | top: 0 282 | } 283 | 284 | .dropdown-display-label .dropdown-selected .del:after { 285 | content: '\D7'; 286 | font-size: 16px 287 | } 288 | 289 | .dropdown-main { 290 | position: absolute; 291 | top: 100%; 292 | left: 0; 293 | z-index: 1010; 294 | width: 100%; 295 | color: #444; 296 | box-sizing: border-box; 297 | background-color: #fff; 298 | border: 1px solid #ccc; 299 | border-radius: 0 0 4px 4px; 300 | box-shadow: 0 6px 12px rgba(0, 0, 0, .175); 301 | margin-top: -1px; 302 | border-top: 0; 303 | padding: 4px 7px; 304 | display: none 305 | } 306 | 307 | .dropdown-main ul { 308 | overflow-x: hidden; 309 | overflow-y: auto; 310 | max-height: 240px; 311 | margin: 0; 312 | padding: 0 313 | } 314 | 315 | .dropdown-main input { 316 | margin-top: 0; 317 | display: block; 318 | box-sizing: border-box; 319 | height: 30px; 320 | border: 1px solid #ccc; 321 | width: 100%; 322 | text-indent: 5px; 323 | border-radius: 3px 324 | } 325 | 326 | .dropdown-main .dropdown-search { 327 | display: block; 328 | padding: 5px 0 329 | } 330 | 331 | .dropdown-group { 332 | font-weight: 700 333 | } 334 | 335 | .dropdown-group, 336 | .dropdown-option { 337 | margin: 0; 338 | padding-left: 12px; 339 | list-style: none; 340 | line-height: 26px; 341 | word-wrap: break-word 342 | } 343 | 344 | .dropdown-option { 345 | cursor: pointer 346 | } 347 | 348 | .dropdown-option:focus, 349 | .dropdown-option:hover { 350 | background-color: #efefef; 351 | outline: 0 352 | } 353 | 354 | .dropdown-option[disabled] { 355 | color: #ddd; 356 | background-color: #fff; 357 | cursor: not-allowed; 358 | text-decoration: line-through 359 | } 360 | 361 | .dropdown-option.dropdown-chose:after { 362 | content: ''; 363 | float: right; 364 | width: 10px; 365 | height: 10px; 366 | background: #4AB1E9; 367 | border-radius: 100%; 368 | margin: 8px 5px 0 0 369 | } 370 | 371 | .dropdown-maxItem-alert, .dropdown-minItem-alert { 372 | position: absolute; 373 | top: 0; 374 | left: 0; 375 | background-color: #e4e3e2; 376 | width: 100%; 377 | height: 39px; 378 | line-height: 39px; 379 | padding: 0 5px; 380 | border-radius: 5px; 381 | color: #999; 382 | -webkit-animation: iui-fadeIn .2s ease-in forwards; 383 | -moz-animation: iui-fadeIn .2s ease-in forwards; 384 | -ms-animation: iui-fadeIn .2s ease-in forwards; 385 | -o-animation: iui-fadeIn .2s ease-in forwards; 386 | animation: iui-fadeIn .2s ease-in forwards 387 | } 388 | -------------------------------------------------------------------------------- /jquery.dropdown.js: -------------------------------------------------------------------------------- 1 | ; 2 | (function ($) { 3 | 'use strict'; 4 | 5 | function noop() { } 6 | 7 | function throttle(func, wait, options) { 8 | var context, args, result; 9 | var timeout = null; 10 | // 上次执行时间点 11 | var previous = 0; 12 | if (!options) options = {}; 13 | // 延迟执行函数 14 | var later = function () { 15 | // 若设定了开始边界不执行选项,上次执行时间始终为0 16 | previous = options.leading === false ? 0 : new Date().getTime(); 17 | timeout = null; 18 | result = func.apply(context, args); 19 | if (!timeout) context = args = null; 20 | }; 21 | return function () { 22 | var now = new Date().getTime(); 23 | // 首次执行时,如果设定了开始边界不执行选项,将上次执行时间设定为当前时间。 24 | if (!previous && options.leading === false) previous = now; 25 | // 延迟执行时间间隔 26 | var remaining = wait - (now - previous); 27 | context = this; 28 | args = arguments; 29 | // 延迟时间间隔remaining小于等于0,表示上次执行至此所间隔时间已经超过一个时间窗口 30 | // remaining大于时间窗口wait,表示客户端系统时间被调整过 31 | if (remaining <= 0 || remaining > wait) { 32 | clearTimeout(timeout); 33 | timeout = null; 34 | previous = now; 35 | result = func.apply(context, args); 36 | if (!timeout) context = args = null; 37 | //如果延迟执行不存在,且没有设定结尾边界不执行选项 38 | } else if (!timeout && options.trailing !== false) { 39 | timeout = setTimeout(later, remaining); 40 | } 41 | return result; 42 | }; 43 | } 44 | 45 | var isSafari = function () { 46 | var ua = navigator.userAgent.toLowerCase(); 47 | if (ua.indexOf('safari') !== -1) { 48 | return ua.indexOf('chrome') > -1 ? false : true; 49 | } 50 | }(); 51 | 52 | var settings = { 53 | readonly: false, 54 | minCount: 0, 55 | minCountErrorMessage: '', 56 | limitCount: Infinity, 57 | limitCountErrorMessage: '', 58 | input: '', 59 | data: [], 60 | searchable: true, 61 | searchNoData: '查无数据,换个词儿试试 /(ㄒoㄒ)/~~', 62 | init: noop, 63 | choice: noop, 64 | extendProps: [] 65 | }; 66 | 67 | var KEY_CODE = { 68 | up: 38, 69 | down: 40, 70 | enter: 13 71 | }; 72 | 73 | var EVENT_SPACE = { 74 | click: 'click.iui-dropdown', 75 | focus: 'focus.iui-dropdown', 76 | keydown: 'keydown.iui-dropdown', 77 | keyup: 'keyup.iui-dropdown' 78 | }; 79 | 80 | var ALERT_TIMEOUT_PERIOD = 1000; 81 | 82 | // 创建模板 83 | function createTemplate() { 84 | var isLabelMode = this.isLabelMode; 85 | var searchable = this.config.searchable; 86 | var templateSearch = searchable ? '' + this.config.input + '' : ''; 87 | 88 | return isLabelMode ? '' + templateSearch + '{{ul}}' : '\xD7' + templateSearch + '{{ul}}'; 89 | } 90 | 91 | // 小于minCount提示的元素 92 | function minItemsAlert() { 93 | var _dropdown = this; 94 | var _config = _dropdown.config; 95 | var $el = _dropdown.$el; 96 | var $alert = $el.find('.dropdown-minItem-alert'); 97 | var alertMessage = _config.minCountErrorMessage; 98 | clearTimeout(_dropdown.itemCountAlertTimer); 99 | 100 | if ($alert.length === 0) { 101 | if (!alertMessage) { 102 | alertMessage = '\u6700\u4f4e\u9009\u62e9' + _config.minCount + '\u4E2A'; 103 | } 104 | $alert = $('' + alertMessage + ''); 105 | } 106 | 107 | $el.append($alert); 108 | _dropdown.itemCountAlertTimer = setTimeout(function () { 109 | $el.find('.dropdown-minItem-alert').remove(); 110 | }, ALERT_TIMEOUT_PERIOD); 111 | } 112 | 113 | // 超出限制提示 114 | function maxItemAlert() { 115 | var _dropdown = this; 116 | var _config = _dropdown.config; 117 | var $el = _dropdown.$el; 118 | var $alert = $el.find('.dropdown-maxItem-alert'); 119 | var alertMessage = _config.limitCountErrorMessage; 120 | clearTimeout(_dropdown.itemLimitAlertTimer); 121 | 122 | if ($alert.length === 0) { 123 | if (!alertMessage) { 124 | alertMessage = '\u6700\u591A\u53EF\u9009\u62E9' + _config.limitCount + '\u4E2A'; 125 | } 126 | $alert = $('' + alertMessage + ''); 127 | } 128 | 129 | $el.append($alert); 130 | _dropdown.itemLimitAlertTimer = setTimeout(function () { 131 | $el.find('.dropdown-maxItem-alert').remove(); 132 | }, ALERT_TIMEOUT_PERIOD); 133 | } 134 | 135 | // select-option 转 ul-li 136 | function selectToDiv(str) { 137 | var result = str || ''; 138 | // 移除select标签 139 | result = result.replace(/]*>/gi, '').replace('', ''); 140 | // 移除 optgroup 结束标签 141 | result = result.replace(/<\/optgroup>/gi, ''); 142 | result = result.replace(/]*>/gi, function (matcher) { 143 | var groupName = /label="(.[^"]*)"(\s|>)/.exec(matcher); 144 | var groupId = /data\-group\-id="(.[^"]*)"(\s|>)/.exec(matcher); 145 | return '' + (groupName ? groupName[1] : '') + ''; 146 | }); 147 | result = result.replace(//gi, function (matcher) { 148 | // var value = /value="?([\w\u4E00-\u9FA5\uF900-\uFA2D]+)"?/.exec(matcher); 149 | var value = $(matcher).val(); 150 | var name = />(.*)<\//.exec(matcher); 151 | // 强制要求html中使用selected/disabled,而不是selected="selected","disabled="disabled" 152 | var isSelected = matcher.indexOf('selected') > -1 ? true : false; 153 | var isDisabled = matcher.indexOf('disabled') > -1 ? true : false; 154 | var extendAttr = '' 155 | var extendProps = matcher.replace(/data-(\w+)="?(.[^"]+)"?/g, function ($1) { 156 | extendAttr += $1 + ' ' 157 | }); 158 | return '' + (name ? name[1] : '') + ''; 159 | }); 160 | 161 | return result; 162 | } 163 | 164 | // object-data 转 select-option 165 | function objectToSelect(data) { 166 | var dropdown = this; 167 | var map = {}; 168 | var result = ''; 169 | var name = []; 170 | var selectAmount = 0; 171 | var extendProps = dropdown.config.extendProps; 172 | 173 | if (!data || !data.length) { 174 | return false; 175 | } 176 | 177 | $.each(data, function (index, val) { 178 | // disable 权重高于 selected 179 | var hasGroup = val.groupId; 180 | var isDisabled = val.disabled ? ' disabled' : ''; 181 | var isSelected = val.selected && !isDisabled ? ' selected' : ''; 182 | var extendAttr = '' 183 | $.each(extendProps, function (index, value) { 184 | if (val[value]) { 185 | extendAttr += 'data-' + value + '="' + val[value] + '" ' 186 | } 187 | }) 188 | var temp = '' + val.name + ''; 189 | if (isSelected) { 190 | name.push('' + val.name + ''); 191 | selectAmount++; 192 | } 193 | // 判断是否有分组 194 | if (hasGroup) { 195 | if (map[val.groupId]) { 196 | map[val.groupId] += temp; 197 | } else { 198 | // &janking& just a separator 199 | map[val.groupId] = val.groupName + '&janking&' + temp; 200 | } 201 | } else { 202 | map[index] = temp; 203 | } 204 | }); 205 | 206 | $.each(map, function (index, val) { 207 | var option = val.split('&janking&'); 208 | // 判断是否有分组 209 | if (option.length === 2) { 210 | var groupName = option[0]; 211 | var items = option[1]; 212 | result += '' + items + ''; 213 | } else { 214 | result += val; 215 | } 216 | }); 217 | 218 | return [result, name, selectAmount]; 219 | } 220 | 221 | // select-option 转 object-data 222 | // 223 | function selectToObject(el) { 224 | var $select = el; 225 | var result = []; 226 | 227 | function readOption(key, el) { 228 | var $option = $(el); 229 | this.id = $option.prop('value'); 230 | this.name = $option.text(); 231 | this.disabled = $option.prop('disabled'); 232 | this.selected = $option.prop('selected'); 233 | } 234 | 235 | $.each($select.children(), function (key, el) { 236 | var tmp = {}; 237 | var tmpGroup = {}; 238 | var $el = $(el); 239 | if (el.nodeName === 'OPTGROUP') { 240 | tmpGroup.groupId = $el.data('groupId'); 241 | tmpGroup.groupName = $el.attr('label'); 242 | $.each($el.children(), $.proxy(readOption, tmp)); 243 | $.extend(tmp, tmpGroup); 244 | } else { 245 | $.each($el, $.proxy(readOption, tmp)); 246 | } 247 | result.push(tmp); 248 | }); 249 | 250 | return result; 251 | } 252 | 253 | var action = { 254 | show: function (event) { 255 | event.stopPropagation(); 256 | var _dropdown = this; 257 | $(document).trigger('click.dropdown'); 258 | _dropdown.$el.addClass('active'); 259 | }, 260 | search: throttle(function (event) { 261 | var _dropdown = this; 262 | var _config = _dropdown.config; 263 | var $el = _dropdown.$el; 264 | var $input = $(event.target); 265 | var intputValue = $input.val(); 266 | var data = _dropdown.config.data; 267 | var result = []; 268 | if (event.keyCode > 36 && event.keyCode < 41) { 269 | return; 270 | } 271 | $.each(data, function (key, value) { 272 | if ((value.groupName && value.groupName.toLowerCase().indexOf(intputValue.toLowerCase()) > -1) || value.name.toLowerCase().indexOf(intputValue.toLowerCase()) > -1 || '' + value.id === '' + intputValue) { 273 | result.push(value); 274 | } 275 | }); 276 | $el.find('ul').html(selectToDiv(objectToSelect.call(_dropdown, result)[0]) || _config.searchNoData); 277 | }, 300), 278 | control: function (event) { 279 | var keyCode = event.keyCode; 280 | var KC = KEY_CODE; 281 | var index = 0; 282 | var direct; 283 | var itemIndex; 284 | var $items; 285 | if (keyCode === KC.down || keyCode === KC.up) { 286 | // 方向 287 | direct = keyCode === KC.up ? -1 : 1; 288 | $items = this.$el.find('[tabindex]'); 289 | itemIndex = $items.index($(document.activeElement)); 290 | // 初始 291 | if (itemIndex === -1) { 292 | index = direct + 1 ? -1 : 0; 293 | } else { 294 | index = itemIndex; 295 | } 296 | // 确认位序 297 | index = index + direct; 298 | // 最后位循环 299 | if (index === $items.length) { 300 | index = 0; 301 | } 302 | $items.eq(index).focus(); 303 | event.preventDefault(); 304 | } 305 | }, 306 | multiChoose: function (event, status) { 307 | var _dropdown = this; 308 | var _config = _dropdown.config; 309 | var $select = _dropdown.$select; 310 | var $target = $(event.target); 311 | var value = $target.attr('data-value'); 312 | var hasSelected = $target.hasClass('dropdown-chose'); 313 | var selectedName = []; 314 | var selectedProp; 315 | 316 | if ($target.hasClass('dropdown-display')) { 317 | return false; 318 | } 319 | 320 | if (hasSelected) { 321 | $target.removeClass('dropdown-chose'); 322 | _dropdown.selectAmount--; 323 | } else { 324 | if (_dropdown.selectAmount < _config.limitCount) { 325 | $target.addClass('dropdown-chose'); 326 | _dropdown.selectAmount++; 327 | } else { 328 | maxItemAlert.call(_dropdown); 329 | return false; 330 | } 331 | } 332 | 333 | _dropdown.name = []; 334 | 335 | $.each(_config.data, function (key, item) { 336 | if ('' + item.id === '' + value) { 337 | selectedProp = item; 338 | item.selected = hasSelected ? false : true; 339 | } 340 | if (item.selected) { 341 | selectedName.push(item.name); 342 | _dropdown.name.push('' + item.name + ''); 343 | } 344 | }); 345 | 346 | $select.find('option[value="' + value + '"]').prop('selected', hasSelected ? false : true); 347 | 348 | if (hasSelected && _dropdown.selectAmount < _config.minCount) { 349 | minItemsAlert.call(_dropdown); 350 | } 351 | 352 | _dropdown.$choseList.find('.dropdown-selected').remove(); 353 | _dropdown.$choseList.prepend(_dropdown.name.join('')); 354 | _dropdown.$el.find('.dropdown-display').attr('title', selectedName.join(',')); 355 | _config.choice.call(_dropdown, event, selectedProp); 356 | }, 357 | singleChoose: function (event) { 358 | var _dropdown = this; 359 | var _config = _dropdown.config; 360 | var $el = _dropdown.$el; 361 | var $select = _dropdown.$select; 362 | var $target = $(event.target); 363 | var value = $target.attr('data-value'); 364 | var hasSelected = $target.hasClass('dropdown-chose'); 365 | 366 | if ($target.hasClass('dropdown-chose') || $target.hasClass('dropdown-display')) { 367 | return false; 368 | } 369 | 370 | _dropdown.name = []; 371 | 372 | 373 | $el.removeClass('active').find('li').not($target).removeClass('dropdown-chose'); 374 | 375 | $target.toggleClass('dropdown-chose'); 376 | $.each(_config.data, function (key, item) { 377 | // id 有可能是数字也有可能是字符串,强制全等有弊端 2017-03-20 22:19:21 378 | item.selected = false; 379 | if ('' + item.id === '' + value) { 380 | item.selected = hasSelected ? 0 : 1; 381 | if (item.selected) { 382 | _dropdown.name.push('' + item.name + ''); 383 | } 384 | } 385 | }); 386 | 387 | $select.find('option[value="' + value + '"]').prop('selected', true); 388 | 389 | _dropdown.name.push('' + _dropdown.placeholder + ''); 390 | _dropdown.$choseList.html(_dropdown.name.join('')); 391 | _config.choice.call(_dropdown, event); 392 | }, 393 | del: function (event) { 394 | var _dropdown = this; 395 | var _config = _dropdown.config; 396 | var $target = $(event.target); 397 | var id = $target.data('id'); 398 | // 2017-03-23 15:58:50 测试 399 | // 10000条数据测试删除,耗时 ~3ms 400 | $.each(_dropdown.name, function (key, value) { 401 | if (value.indexOf('data-id="' + id + '"') !== -1) { 402 | _dropdown.name.splice(key, 1); 403 | return false; 404 | } 405 | }); 406 | 407 | $.each(_dropdown.config.data, function (key, item) { 408 | if ('' + item.id == '' + id) { 409 | item.selected = false; 410 | return false; 411 | } 412 | }); 413 | 414 | _dropdown.selectAmount--; 415 | _dropdown.$el.find('[data-value="' + id + '"]').removeClass('dropdown-chose'); 416 | _dropdown.$el.find('[value="' + id + '"]').prop('selected', false).removeAttr('selected'); 417 | $target.closest('.dropdown-selected').remove(); 418 | _config.choice.call(_dropdown, event); 419 | 420 | return false; 421 | }, 422 | clearAll: function (event) { 423 | var _dropdown = this; 424 | var _config = _dropdown.config; 425 | event && event.preventDefault(); 426 | console.log(this) 427 | this.$choseList.find('.del').each(function (index, el) { 428 | $(el).trigger('click'); 429 | }); 430 | 431 | if (_config.minCount > 0) { 432 | minItemsAlert.call(_dropdown); 433 | } 434 | 435 | this.$el.find('.dropdown-display').removeAttr('title'); 436 | return false; 437 | } 438 | }; 439 | 440 | function Dropdown(options, el) { 441 | this.$el = $(el); 442 | this.$select = this.$el.find('select'); 443 | this.placeholder = this.$select.attr('placeholder'); 444 | this.config = options; 445 | this.name = []; 446 | this.isSingleSelect = !this.$select.prop('multiple'); 447 | this.selectAmount = 0; 448 | this.itemLimitAlertTimer = null; 449 | this.isLabelMode = this.config.multipleMode === 'label'; 450 | this.init(); 451 | } 452 | 453 | Dropdown.prototype = { 454 | init: function () { 455 | var _this = this; 456 | var _config = _this.config; 457 | var $el = _this.$el; 458 | _this.$select.hide(); 459 | // 判断dropdown是否单选,是否token模式 460 | $el.addClass(_this.isSingleSelect ? 'dropdown-single' : _this.isLabelMode ? 'dropdown-multiple-label' : 'dropdown-multiple'); 461 | 462 | if (_config.data.length === 0) { 463 | _config.data = selectToObject(_this.$select); 464 | } 465 | 466 | var processResult = objectToSelect.call(_this, _config.data); 467 | 468 | _this.name = processResult[1]; 469 | _this.selectAmount = processResult[2]; 470 | _this.$select.html(processResult[0]); 471 | _this.renderSelect(); 472 | // disabled权重高于readonly 473 | _this.changeStatus(_config.disabled ? 'disabled' : _config.readonly ? 'readonly' : false); 474 | 475 | _this.config.init(); 476 | }, 477 | // 渲染 select 为 dropdown 478 | renderSelect: function (isUpdate, isCover) { 479 | var _this = this; 480 | var $el = _this.$el; 481 | var $select = _this.$select; 482 | var elemLi = selectToDiv($select.prop('outerHTML')); 483 | var template; 484 | 485 | if (isUpdate) { 486 | $el.find('ul')[isCover ? 'html' : 'append'](elemLi); 487 | } else { 488 | template = createTemplate.call(_this).replace('{{ul}}', '' + elemLi + ''); 489 | $el.append(template).find('ul').removeAttr('style class'); 490 | } 491 | 492 | if (isCover) { 493 | _this.name = []; 494 | _this.$el.find('.dropdown-selected').remove(); 495 | _this.$select.val(''); 496 | } 497 | 498 | _this.$choseList = $el.find('.dropdown-chose-list'); 499 | 500 | if (!_this.isLabelMode) { 501 | _this.$choseList.html($('').text(_this.placeholder)); 502 | } 503 | 504 | _this.$choseList.prepend(_this.name ? _this.name.join('') : []); 505 | }, 506 | bindEvent: function () { 507 | var _this = this; 508 | var $el = _this.$el; 509 | var openHandle = isSafari ? EVENT_SPACE.click : EVENT_SPACE.focus; 510 | 511 | $el.on(EVENT_SPACE.click, function (event) { 512 | event.stopPropagation(); 513 | }); 514 | 515 | $el.on(EVENT_SPACE.click, '.del', $.proxy(action.del, _this)); 516 | 517 | // show 518 | if (_this.isLabelMode) { 519 | $el.on(EVENT_SPACE.click, '.dropdown-display-label', function () { 520 | $el.find('input').focus(); 521 | }); 522 | if (_this.config.searchable) { 523 | $el.on(EVENT_SPACE.focus, 'input', $.proxy(action.show, _this)); 524 | } else { 525 | $el.on(EVENT_SPACE.click, $.proxy(action.show, _this)); 526 | } 527 | $el.on(EVENT_SPACE.keydown, 'input', function (event) { 528 | if (event.keyCode === 8 && this.value === '' && _this.name.length) { 529 | $el.find('.del').eq(-1).trigger('click'); 530 | } 531 | }); 532 | } else { 533 | $el.on(openHandle, '.dropdown-display', $.proxy(action.show, _this)); 534 | $el.on(openHandle, '.dropdown-clear-all', $.proxy(action.clearAll, _this)); 535 | } 536 | 537 | // 搜索 538 | $el.on(EVENT_SPACE.keyup, 'input', $.proxy(action.search, _this)); 539 | 540 | // 按下enter键设置token 541 | $el.on(EVENT_SPACE.keyup, function (event) { 542 | var keyCode = event.keyCode; 543 | var KC = KEY_CODE; 544 | if (keyCode === KC.enter) { 545 | $.proxy(_this.isSingleSelect ? action.singleChoose : action.multiChoose, _this, event)(); 546 | } 547 | }); 548 | 549 | // 按下上下键切换token 550 | $el.on(EVENT_SPACE.keydown, $.proxy(action.control, _this)); 551 | 552 | $el.on(EVENT_SPACE.click, 'li[tabindex]', $.proxy(_this.isSingleSelect ? action.singleChoose : action.multiChoose, _this)); 553 | }, 554 | unbindEvent: function () { 555 | var _this = this; 556 | var $el = _this.$el; 557 | var openHandle = isSafari ? EVENT_SPACE.click : EVENT_SPACE.focus; 558 | 559 | $el.off(EVENT_SPACE.click); 560 | $el.off(EVENT_SPACE.click, '.del'); 561 | 562 | // show 563 | if (_this.isLabelMode) { 564 | $el.off(EVENT_SPACE.click, '.dropdown-display-label'); 565 | $el.off(EVENT_SPACE.focus, 'input'); 566 | $el.off(EVENT_SPACE.keydown, 'input'); 567 | } else { 568 | $el.off(openHandle, '.dropdown-display'); 569 | $el.off(openHandle, '.dropdown-clear-all'); 570 | } 571 | // 搜索 572 | $el.off(EVENT_SPACE.keyup, 'input'); 573 | // 按下enter键设置token 574 | $el.off(EVENT_SPACE.keyup); 575 | // 按下上下键切换token 576 | $el.off(EVENT_SPACE.keydown); 577 | $el.off(EVENT_SPACE.click, '[tabindex]'); 578 | }, 579 | changeStatus: function (status) { 580 | var _this = this; 581 | if (status === 'readonly') { 582 | _this.unbindEvent(); 583 | } else if (status === 'disabled') { 584 | _this.$select.prop('disabled', true); 585 | _this.unbindEvent(); 586 | } else { 587 | _this.$select.prop('disabled', false); 588 | _this.bindEvent(); 589 | } 590 | }, 591 | update: function (data, isCover) { 592 | var _this = this; 593 | var _config = _this.config; 594 | var $el = _this.$el; 595 | var _isCover = isCover || false; 596 | 597 | if (Object.prototype.toString.call(data) !== '[object Array]') { 598 | return; 599 | } 600 | 601 | _config.data = _isCover ? data.slice(0) : _config.data.concat(data); 602 | 603 | var processResult = objectToSelect.call(_this, _config.data); 604 | 605 | _this.name = processResult[1]; 606 | _this.selectAmount = processResult[2]; 607 | _this.$select.html(processResult[0]); 608 | _this.renderSelect(true, _isCover); 609 | }, 610 | destroy: function () { 611 | this.unbindEvent(); 612 | this.$el.children().not('select').remove(); 613 | this.$el.removeClass('dropdown-single dropdown-multiple-label dropdown-multiple'); 614 | this.$select.show(); 615 | }, 616 | choose: function (values, status) { 617 | var valArr = Object.prototype.toString.call(values) === '[object Array]' ? values : [values]; 618 | var _this = this; 619 | var _status = status !== void 0 ? !!status : true 620 | $.each(valArr, function (index, value) { 621 | var $target = _this.$el.find('[data-value="' + value + '"]'); 622 | var targetStatus = $target.hasClass('dropdown-chose'); 623 | if (targetStatus !== _status) { 624 | $target.trigger(EVENT_SPACE.click, status || true) 625 | } 626 | }); 627 | }, 628 | reset: function () { 629 | action.clearAll.call(this) 630 | } 631 | }; 632 | 633 | $(document).on('click.dropdown', function () { 634 | $('.dropdown-single,.dropdown-multiple,.dropdown-multiple-label').removeClass('active'); 635 | }); 636 | 637 | $.fn.dropdown = function (options) { 638 | this.each(function (index, el) { 639 | $(el).data('dropdown', new Dropdown($.extend(true, {}, settings, options), el)); 640 | }); 641 | return this; 642 | } 643 | })(jQuery); 644 | -------------------------------------------------------------------------------- /jquery.dropdown.min.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes iui-fadeIn{0%{opacity:0}100%{opacity:1}}@-moz-keyframes iui-fadeIn{0%{opacity:0}100%{opacity:1}}@-ms-keyframes iui-fadeIn{0%{opacity:0}100%{opacity:1}}@-o-keyframes iui-fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes iui-fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes iui-fadeOut{0%{opacity:1}100%{opacity:0}}@-moz-keyframes iui-fadeOut{0%{opacity:1}100%{opacity:0}}@-ms-keyframes iui-fadeOut{0%{opacity:1}100%{opacity:0}}@-o-keyframes iui-fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes iui-fadeOut{0%{opacity:1}100%{opacity:0}}.dropdown-multiple,.dropdown-multiple-label,.dropdown-single{position:relative}.dropdown-multiple-label.active .dropdown-main,.dropdown-multiple.active .dropdown-main,.dropdown-single.active .dropdown-main{display:block;-webkit-animation:iui-fadeIn .2s ease-in forwards;-moz-animation:iui-fadeIn .2s ease-in forwards;-ms-animation:iui-fadeIn .2s ease-in forwards;-o-animation:iui-fadeIn .2s ease-in forwards;animation:iui-fadeIn .2s ease-in forwards}.dropdown-multiple-label.active .dropdown-display-label:after,.dropdown-multiple-label.active .dropdown-display:after,.dropdown-multiple.active .dropdown-display-label:after,.dropdown-multiple.active .dropdown-display:after,.dropdown-single.active .dropdown-display-label:after,.dropdown-single.active .dropdown-display:after{border-top:none;border-bottom:10px solid #999;border-left:5px solid transparent;border-right:5px solid transparent}.dropdown-multiple-label.active .dropdown-display,.dropdown-multiple-label.active .dropdown-display-label,.dropdown-multiple.active .dropdown-display,.dropdown-multiple.active .dropdown-display-label,.dropdown-single.active .dropdown-display,.dropdown-single.active .dropdown-display-label{border-bottom-left-radius:0;border-bottom-right-radius:0}.dropdown-display,.dropdown-display-label{position:relative;display:block;margin-bottom:0;font-size:14px;line-height:1.42857143;vertical-align:middle;touch-action:manipulation;cursor:pointer;user-select:none;background-image:none;border:1px solid #ccc;border-radius:4px;color:#333;background-color:#fff}.dropdown-display-label:after,.dropdown-display:after{content:'';position:absolute;border-top:10px solid #999;border-left:5px solid transparent;border-right:5px solid transparent;top:12px;right:8px}.dropdown-clear-all{background-color:#fff;border:none;font-size:22px;z-index:999;color:#999;position:absolute;right:2px;top:2px;display:none;width:25px;height:30px;text-align:center;line-height:30px}.dropdown-clear-all:focus{outline:0}.dropdown-clear-all:hover{color:#ccc;text-decoration:none}.dropdown-display{white-space:nowrap;padding:6px 20px 6px 12px}.dropdown-multiple:hover .dropdown-clear-all,.dropdown-single:hover .dropdown-clear-all{display:block}.dropdown-display .dropdown-chose-list{display:inline-block;vertical-align:middle;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dropdown-display .dropdown-chose-list span:before{content:','}.dropdown-display .dropdown-chose-list span:first-child:before{content:''}.dropdown-display .placeholder{display:none}.dropdown-display .placeholder:first-child{position:absolute;height:100%;width:100%;top:0;left:0;color:#999;display:block;text-indent:10px;font-size:13px;line-height:32px}.dropdown-display input{border:0;outline:0}.dropdown-display-label{cursor:text;padding:6px 25px 5px 0}.dropdown-display-label .dropdown-search{display:inline-block}.dropdown-display-label input,.dropdown-display-label input:focus{border:none;outline:0}.dropdown-display-label .dropdown-chose-list{display:inline-block;padding:0 5px}.dropdown-display-label .dropdown-chose-list .placeholder{display:none}.dropdown-display-label .dropdown-selected{position:relative;margin:0 5px 5px 0;padding:0 20px 0 5px;border:1px solid #aaa;max-width:100%;border-radius:3px;background-repeat:repeat-x;color:#333;cursor:default;display:inline-block}.dropdown-display-label .dropdown-selected .del{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0;float:right;line-height:1;color:#999;position:absolute;right:3px;top:0}.dropdown-display-label .dropdown-selected .del:after{content:'\D7';font-size:16px}.dropdown-main{position:absolute;top:100%;left:0;z-index:1010;width:100%;color:#444;box-sizing:border-box;background-color:#fff;border:1px solid #ccc;border-radius:0 0 4px 4px;box-shadow:0 6px 12px rgba(0,0,0,.175);margin-top:-1px;border-top:0;padding:4px 7px;display:none}.dropdown-main ul{overflow-x:hidden;overflow-y:auto;max-height:240px;margin:0;padding:0}.dropdown-main input{margin-top:0;display:block;box-sizing:border-box;height:30px;border:1px solid #ccc;width:100%;text-indent:5px;border-radius:3px}.dropdown-main .dropdown-search{display:block;padding:5px 0}.dropdown-group{font-weight:700}.dropdown-group,.dropdown-option{margin:0;padding-left:12px;list-style:none;line-height:26px;word-wrap:break-word}.dropdown-option{cursor:pointer}.dropdown-option:focus,.dropdown-option:hover{background-color:#efefef;outline:0}.dropdown-option[disabled]{color:#ddd;background-color:#fff;cursor:not-allowed;text-decoration:line-through}.dropdown-option.dropdown-chose:after{content:'';float:right;width:10px;height:10px;background:#4AB1E9;border-radius:100%;margin:8px 5px 0 0}.dropdown-maxItem-alert,.dropdown-minItem-alert{position:absolute;top:0;left:0;background-color:#e4e3e2;width:100%;height:39px;line-height:39px;padding:0 5px;border-radius:5px;color:#999;-webkit-animation:iui-fadeIn .2s ease-in forwards;-moz-animation:iui-fadeIn .2s ease-in forwards;-ms-animation:iui-fadeIn .2s ease-in forwards;-o-animation:iui-fadeIn .2s ease-in forwards;animation:iui-fadeIn .2s ease-in forwards} 2 | -------------------------------------------------------------------------------- /jquery.dropdown.min.js: -------------------------------------------------------------------------------- 1 | (function(e){"use strict";function o(){}function n(e,o,n){var t,i,l,a=null,d=0;n||(n={});var s=function(){d=n.leading===!1?0:(new Date).getTime(),a=null,l=e.apply(t,i),a||(t=i=null)};return function(){var r=(new Date).getTime();d||n.leading!==!1||(d=r);var c=o-(r-d);return t=this,i=arguments,c<=0||c>o?(clearTimeout(a),a=null,d=r,l=e.apply(t,i),a||(t=i=null)):a||n.trailing===!1||(a=setTimeout(s,c)),l}}function t(){var e=this.isLabelMode,o=this.config.searchable,n=o?''+this.config.input+"":"";return e?''+n+'{{ul}}':'×'+n+"{{ul}}"}function i(){var o=this,n=o.config,t=o.$el,i=t.find(".dropdown-minItem-alert"),l=n.minCountErrorMessage;clearTimeout(o.itemCountAlertTimer),0===i.length&&(l||(l="最低选择"+n.minCount+"个"),i=e(''+l+"")),t.append(i),o.itemCountAlertTimer=setTimeout(function(){t.find(".dropdown-minItem-alert").remove()},f)}function l(){var o=this,n=o.config,t=o.$el,i=t.find(".dropdown-maxItem-alert"),l=n.limitCountErrorMessage;clearTimeout(o.itemLimitAlertTimer),0===i.length&&(l||(l="最多可选择"+n.limitCount+"个"),i=e(''+l+"")),t.append(i),o.itemLimitAlertTimer=setTimeout(function(){t.find(".dropdown-maxItem-alert").remove()},f)}function a(o){var n=o||"";return n=n.replace(/]*>/gi,"").replace("",""),n=n.replace(/<\/optgroup>/gi,""),n=n.replace(/]*>/gi,function(e){var o=/label="(.[^"]*)"(\s|>)/.exec(e),n=/data\-group\-id="(.[^"]*)"(\s|>)/.exec(e);return''+(o?o[1]:"")+""}),n=n.replace(//gi,function(o){var n=e(o).val(),t=/>(.*)<\//.exec(o),i=o.indexOf("selected")>-1,l=o.indexOf("disabled")>-1,a="";o.replace(/data-(\w+)="?(.[^"]+)"?/g,function(e){a+=e+" "});return""+(t?t[1]:"")+""})}function d(o){var n=this,t={},i="",l=[],a=0,d=n.config.extendProps;return!(!o||!o.length)&&(e.each(o,function(o,n){var i=n.groupId,s=n.disabled?" disabled":"",r=n.selected&&!s?" selected":"",c="";e.each(d,function(e,o){n[o]&&(c+="data-"+o+'="'+n[o]+'" ')});var p=""+n.name+"";r&&(l.push(''+n.name+''),a++),i?t[n.groupId]?t[n.groupId]+=p:t[n.groupId]=n.groupName+"&janking&"+p:t[o]=p}),e.each(t,function(e,o){var n=o.split("&janking&");if(2===n.length){var t=n[0],l=n[1];i+=''+l+""}else i+=o}),[i,l,a])}function s(o){function n(o,n){var t=e(n);this.id=t.prop("value"),this.name=t.text(),this.disabled=t.prop("disabled"),this.selected=t.prop("selected")}var t=o,i=[];return e.each(t.children(),function(o,t){var l={},a={},d=e(t);"OPTGROUP"===t.nodeName?(a.groupId=d.data("groupId"),a.groupName=d.attr("label"),e.each(d.children(),e.proxy(n,l)),e.extend(l,a)):e.each(d,e.proxy(n,l)),i.push(l)}),i}function r(o,n){this.$el=e(n),this.$select=this.$el.find("select"),this.placeholder=this.$select.attr("placeholder"),this.config=o,this.name=[],this.isSingleSelect=!this.$select.prop("multiple"),this.selectAmount=0,this.itemLimitAlertTimer=null,this.isLabelMode="label"===this.config.multipleMode,this.init()}var c=function(){var e=navigator.userAgent.toLowerCase();if(e.indexOf("safari")!==-1)return!(e.indexOf("chrome")>-1)}(),p={readonly:!1,minCount:0,minCountErrorMessage:"",limitCount:1/0,limitCountErrorMessage:"",input:'',data:[],searchable:!0,searchNoData:'查无数据,换个词儿试试 /(ㄒoㄒ)/~~',init:o,choice:o,extendProps:[]},u={up:38,down:40,enter:13},h={click:"click.iui-dropdown",focus:"focus.iui-dropdown",keydown:"keydown.iui-dropdown",keyup:"keyup.iui-dropdown"},f=1e3,m={show:function(o){o.stopPropagation();var n=this;e(document).trigger("click.dropdown"),n.$el.addClass("active")},search:n(function(o){var n=this,t=n.config,i=n.$el,l=e(o.target),s=l.val(),r=n.config.data,c=[];o.keyCode>36&&o.keyCode<41||(e.each(r,function(e,o){(o.groupName&&o.groupName.toLowerCase().indexOf(s.toLowerCase())>-1||o.name.toLowerCase().indexOf(s.toLowerCase())>-1||""+o.id==""+s)&&c.push(o)}),i.find("ul").html(a(d.call(n,c)[0])||t.searchNoData))},300),control:function(o){var n,t,i,l=o.keyCode,a=u,d=0;l!==a.down&&l!==a.up||(n=l===a.up?-1:1,i=this.$el.find("[tabindex]"),t=i.index(e(document.activeElement)),d=t===-1?n+1?-1:0:t,d+=n,d===i.length&&(d=0),i.eq(d).focus(),o.preventDefault())},multiChoose:function(o,n){var t,a=this,d=a.config,s=a.$select,r=e(o.target),c=r.attr("data-value"),p=r.hasClass("dropdown-chose"),u=[];if(r.hasClass("dropdown-display"))return!1;if(p)r.removeClass("dropdown-chose"),a.selectAmount--;else{if(!(a.selectAmount'+o.name+''))}),s.find('option[value="'+c+'"]').prop("selected",!p),p&&a.selectAmount'+o.name+''))}),l.find('option[value="'+d+'"]').prop("selected",!0),n.name.push(''+n.placeholder+""),n.$choseList.html(n.name.join("")),void t.choice.call(n,o))},del:function(o){var n=this,t=n.config,i=e(o.target),l=i.data("id");return e.each(n.name,function(e,o){if(o.indexOf('data-id="'+l+'"')!==-1)return n.name.splice(e,1),!1}),e.each(n.config.data,function(e,o){if(""+o.id==""+l)return o.selected=!1,!1}),n.selectAmount--,n.$el.find('[data-value="'+l+'"]').removeClass("dropdown-chose"),n.$el.find('[value="'+l+'"]').prop("selected",!1).removeAttr("selected"),i.closest(".dropdown-selected").remove(),t.choice.call(n,o),!1},clearAll:function(o){var n=this,t=n.config;return o&&o.preventDefault(),console.log(this),this.$choseList.find(".del").each(function(o,n){e(n).trigger("click")}),t.minCount>0&&i.call(n),this.$el.find(".dropdown-display").removeAttr("title"),!1}};r.prototype={init:function(){var e=this,o=e.config,n=e.$el;e.$select.hide(),n.addClass(e.isSingleSelect?"dropdown-single":e.isLabelMode?"dropdown-multiple-label":"dropdown-multiple"),0===o.data.length&&(o.data=s(e.$select));var t=d.call(e,o.data);e.name=t[1],e.selectAmount=t[2],e.$select.html(t[0]),e.renderSelect(),e.changeStatus(o.disabled?"disabled":!!o.readonly&&"readonly"),e.config.init()},renderSelect:function(o,n){var i,l=this,d=l.$el,s=l.$select,r=a(s.prop("outerHTML"));o?d.find("ul")[n?"html":"append"](r):(i=t.call(l).replace("{{ul}}",""+r+""),d.append(i).find("ul").removeAttr("style class")),n&&(l.name=[],l.$el.find(".dropdown-selected").remove(),l.$select.val("")),l.$choseList=d.find(".dropdown-chose-list"),l.isLabelMode||l.$choseList.html(e('').text(l.placeholder)),l.$choseList.prepend(l.name?l.name.join(""):[])},bindEvent:function(){var o=this,n=o.$el,t=c?h.click:h.focus;n.on(h.click,function(e){e.stopPropagation()}),n.on(h.click,".del",e.proxy(m.del,o)),o.isLabelMode?(n.on(h.click,".dropdown-display-label",function(){n.find("input").focus()}),o.config.searchable?n.on(h.focus,"input",e.proxy(m.show,o)):n.on(h.click,e.proxy(m.show,o)),n.on(h.keydown,"input",function(e){8===e.keyCode&&""===this.value&&o.name.length&&n.find(".del").eq(-1).trigger("click")})):(n.on(t,".dropdown-display",e.proxy(m.show,o)),n.on(t,".dropdown-clear-all",e.proxy(m.clearAll,o))),n.on(h.keyup,"input",e.proxy(m.search,o)),n.on(h.keyup,function(n){var t=n.keyCode,i=u;t===i.enter&&e.proxy(o.isSingleSelect?m.singleChoose:m.multiChoose,o,n)()}),n.on(h.keydown,e.proxy(m.control,o)),n.on(h.click,"li[tabindex]",e.proxy(o.isSingleSelect?m.singleChoose:m.multiChoose,o))},unbindEvent:function(){var e=this,o=e.$el,n=c?h.click:h.focus;o.off(h.click),o.off(h.click,".del"),e.isLabelMode?(o.off(h.click,".dropdown-display-label"),o.off(h.focus,"input"),o.off(h.keydown,"input")):(o.off(n,".dropdown-display"),o.off(n,".dropdown-clear-all")),o.off(h.keyup,"input"),o.off(h.keyup),o.off(h.keydown),o.off(h.click,"[tabindex]")},changeStatus:function(e){var o=this;"readonly"===e?o.unbindEvent():"disabled"===e?(o.$select.prop("disabled",!0),o.unbindEvent()):(o.$select.prop("disabled",!1),o.bindEvent())},update:function(e,o){var n=this,t=n.config,i=(n.$el,o||!1);if("[object Array]"===Object.prototype.toString.call(e)){t.data=i?e.slice(0):t.data.concat(e);var l=d.call(n,t.data);n.name=l[1],n.selectAmount=l[2],n.$select.html(l[0]),n.renderSelect(!0,i)}},destroy:function(){this.unbindEvent(),this.$el.children().not("select").remove(),this.$el.removeClass("dropdown-single dropdown-multiple-label dropdown-multiple"),this.$select.show()},choose:function(o,n){var t="[object Array]"===Object.prototype.toString.call(o)?o:[o],i=this,l=void 0===n||!!n;e.each(t,function(e,o){var t=i.$el.find('[data-value="'+o+'"]'),a=t.hasClass("dropdown-chose");a!==l&&t.trigger(h.click,n||!0)})},reset:function(){m.clearAll.call(this)}},e(document).on("click.dropdown",function(){e(".dropdown-single,.dropdown-multiple,.dropdown-multiple-label").removeClass("active")}),e.fn.dropdown=function(o){return this.each(function(n,t){e(t).data("dropdown",new r(e.extend(!0,{},p,o),t))}),this}})(jQuery); --------------------------------------------------------------------------------