html文件:
1 <input id="theInput"type="text" autocomplete="off">
js文件:
1 this.initAutoComplete = function () { 2 $("#theInput").autocomplete({ //将$("#theInput")绑上autocomplete 3 source: function (request, response) { //source 参数 4 $.ajax({ 5 type: 'post', 6 url: "/CustomAccount/GetADUsers", 7 dataType: "json", 8 data: { mail: request.term,isall:false }, //mail:request.term 传进去的是在#theInput中输入的值 9 success: function (data) { 10 response($.map(data, function (item) { 11 return { 12 displayname: item 13 }; 14 })); 15 } 16 }); 17 }, 18 select: function (event, ui) { //select参数 19 $(this).val(ui.item.displayname); 20 return false; 21 }, 22 max: 10, 23 }).each(function () { 24 $(this).data("autocomplete")._renderItem = function (ul, item) { //重写了autocomplete里面的_renderItem函数 25 return $("<li>") 26 .data("item.autocomplete", item) 27 .append("<a data-type='autocomplete-item'>" + item.displayname + "</a>").attr('title', item.displayname) 28 .appendTo(ul); 29 }; 30 }); 31 };
前台js调用这个initAutoComplete 函数,利用ajax在controller里面取值,controller
CustomAccountController里面的GetADUsers方法:
1 [HttpPost] 2 public ActionResult GetADUsers(string mail, Boolean isall) 3 { 4 var results = this.systemUserService.FindADUsers(mail, isall); //return 一个list<String>,是根据传进来的mail值过滤的 5 results.AddRange(this.systemUserService.FindADGroups(mail, isall).Select(g => g.Email));//在原来的results里加上另一个根据传进来的mail值过滤的的List<>
6 results.Sort(); //排序
7 return this.Json(results, JsonRequestBehavior.AllowGet);
8 }
附件jQuery UI Autocomplete 1.9.1:
1 /*! 2 * jQuery UI Autocomplete 1.9.1 3 * http://jqueryui.com 4 * 5 * Copyright 2012 jQuery Foundation and other contributors 6 * Released under the MIT license. 7 * http://jquery.org/license 8 * 9 * http://api.jqueryui.com/autocomplete/ 10 * 11 * Depends: 12 * jquery.ui.core.js 13 * jquery.ui.widget.js 14 * jquery.ui.position.js 15 * jquery.ui.menu.js 16 */ 17 (function ($, undefined) { 18 19 // used to prevent race conditions with remote data sources 20 var requestIndex = 0; 21 22 $.widget("ui.autocomplete", { 23 version: "1.9.1", 24 defaultElement: "<input>", 25 options: { 26 appendTo: "body", 27 autoFocus: false, 28 delay: 300, 29 minLength: 1, 30 position: { 31 my: "left top", 32 at: "left bottom", 33 collision: "none" 34 }, 35 source: null, 36 // callbacks 37 change: null, 38 close: null, 39 focus: null, 40 open: null, 41 response: null, 42 search: null, 43 select: null, 44 0, 45 max: 10, 46 47 }, 48 49 pending: 0, 50 51 _create: function () { 52 // Some browsers only repeat keydown events, not keypress events, 53 // so we use the suppressKeyPress flag to determine if we've already 54 // handled the keydown event. #7269 55 // Unfortunately the code for & in keypress is the same as the up arrow, 56 // so we use the suppressKeyPressRepeat flag to avoid handling keypress 57 // events when we know the keydown event was used to modify the 58 // search term. #7799 59 var suppressKeyPress, suppressKeyPressRepeat, suppressInput; 60 61 this.isMultiLine = this._isMultiLine(); 62 this.valueMethod = this.element[this.element.is("input,textarea") ? "val" : "text"]; 63 this.isNewMenu = true; 64 65 this.element 66 .addClass("ui-autocomplete-input") 67 .attr("autocomplete", "off"); 68 69 this._on(this.element, { 70 keydown: function (event) { 71 if (this.element.prop("readOnly")) { 72 suppressKeyPress = true; 73 suppressInput = true; 74 suppressKeyPressRepeat = true; 75 return; 76 } 77 78 suppressKeyPress = false; 79 suppressInput = false; 80 suppressKeyPressRepeat = false; 81 var keyCode = $.ui.keyCode; 82 switch (event.keyCode) { 83 case keyCode.PAGE_UP: 84 suppressKeyPress = true; 85 this._move("previousPage", event); 86 break; 87 case keyCode.PAGE_DOWN: 88 suppressKeyPress = true; 89 this._move("nextPage", event); 90 break; 91 case keyCode.UP: 92 suppressKeyPress = true; 93 this._keyEvent("previous", event); 94 break; 95 case keyCode.DOWN: 96 suppressKeyPress = true; 97 this._keyEvent("next", event); 98 break; 99 case keyCode.ENTER: 100 case keyCode.NUMPAD_ENTER: 101 // when menu is open and has focus 102 if (this.menu.active) { 103 // #6055 - Opera still allows the keypress to occur 104 // which causes forms to submit 105 suppressKeyPress = true; 106 event.preventDefault(); 107 this.menu.select(event); 108 } 109 break; 110 case keyCode.TAB: 111 if (this.menu.active) { 112 this.menu.select(event); 113 } 114 break; 115 case keyCode.ESCAPE: 116 if (this.menu.element.is(":visible")) { 117 this._value(this.term); 118 this.close(event); 119 // Different browsers have different default behavior for escape 120 // Single press can mean undo or clear 121 // Double press in IE means clear the whole form 122 event.preventDefault(); 123 } 124 break; 125 default: 126 suppressKeyPressRepeat = true; 127 // search timeout should be triggered before the input value is changed 128 this._searchTimeout(event); 129 break; 130 } 131 }, 132 keypress: function (event) { 133 if (suppressKeyPress) { 134 suppressKeyPress = false; 135 event.preventDefault(); 136 return; 137 } 138 if (suppressKeyPressRepeat) { 139 return; 140 } 141 142 // replicate some key handlers to allow them to repeat in Firefox and Opera 143 var keyCode = $.ui.keyCode; 144 switch (event.keyCode) { 145 case keyCode.PAGE_UP: 146 this._move("previousPage", event); 147 break; 148 case keyCode.PAGE_DOWN: 149 this._move("nextPage", event); 150 break; 151 case keyCode.UP: 152 this._keyEvent("previous", event); 153 break; 154 case keyCode.DOWN: 155 this._keyEvent("next", event); 156 break; 157 } 158 }, 159 input: function (event) { 160 if (suppressInput) { 161 suppressInput = false; 162 event.preventDefault(); 163 return; 164 } 165 this._searchTimeout(event); 166 }, 167 focus: function () { 168 this.selectedItem = null; 169 this.previous = this._value(); 170 }, 171 blur: function (event) { 172 if (this.cancelBlur) { 173 delete this.cancelBlur; 174 return; 175 } 176 177 clearTimeout(this.searching); 178 this.close(event); 179 this._change(event); 180 } 181 }); 182 183 this._initSource(); 184 this.menu = $("<ul>") 185 .addClass("ui-autocomplete") 186 .appendTo(this.document.find(this.options.appendTo || "body")[0]) 187 .menu({ 188 // custom key handling for now 189 input: $(), 190 // disable ARIA support, the live region takes care of that 191 role: null 192 }) 193 .zIndex(this.element.zIndex() + 1) 194 .hide() 195 .data("menu"); 196 197 this._on(this.menu.element, { 198 mousedown: function (event) { 199 // prevent moving focus out of the text field 200 event.preventDefault(); 201 202 // IE doesn't prevent moving focus even with event.preventDefault() 203 // so we set a flag to know when we should ignore the blur event 204 this.cancelBlur = true; 205 this._delay(function () { 206 delete this.cancelBlur; 207 }); 208 209 // clicking on the scrollbar causes focus to shift to the body 210 // but we can't detect a mouseup or a click immediately afterward 211 // so we have to track the next mousedown and close the menu if 212 // the user clicks somewhere outside of the autocomplete 213 var menuElement = this.menu.element[0]; 214 if (!$(event.target).closest(".ui-menu-item").length) { 215 this._delay(function () { 216 var that = this; 217 this.document.one("mousedown", function (event) { 218 if (event.target !== that.element[0] && 219 event.target !== menuElement && 220 !$.contains(menuElement, event.target)) { 221 that.close(); 222 } 223 }); 224 }); 225 } 226 }, 227 menufocus: function (event, ui) { 228 // #7024 - Prevent accidental activation of menu items in Firefox 229 if (this.isNewMenu) { 230 this.isNewMenu = false; 231 if (event.originalEvent && /^mouse/.test(event.originalEvent.type)) { 232 this.menu.blur(); 233 234 this.document.one("mousemove", function () { 235 $(event.target).trigger(event.originalEvent); 236 }); 237 238 return; 239 } 240 } 241 242 // back compat for _renderItem using item.autocomplete, via #7810 243 // TODO remove the fallback, see #8156 244 var item = ui.item.data("ui-autocomplete-item") || ui.item.data("item.autocomplete"); 245 if (false !== this._trigger("focus", event, { item: item })) { 246 // use value to match what will end up in the input, if it was a key event 247 if (event.originalEvent && /^key/.test(event.originalEvent.type)) { 248 this._value(item.value); 249 } 250 } else { 251 // Normally the input is populated with the item's value as the 252 // menu is navigated, causing screen readers to notice a change and 253 // announce the item. Since the focus event was canceled, this doesn't 254 // happen, so we update the live region so that screen readers can 255 // still notice the change and announce it. 256 this.liveRegion.text(item.value); 257 } 258 }, 259 menuselect: function (event, ui) { 260 // back compat for _renderItem using item.autocomplete, via #7810 261 // TODO remove the fallback, see #8156 262 var item = ui.item.data("ui-autocomplete-item") || ui.item.data("item.autocomplete"), 263 previous = this.previous; 264 265 // only trigger when focus was lost (click on menu) 266 if (this.element[0] !== this.document[0].activeElement) { 267 this.element.focus(); 268 this.previous = previous; 269 // #6109 - IE triggers two focus events and the second 270 // is asynchronous, so we need to reset the previous 271 // term synchronously and asynchronously :-( 272 this._delay(function () { 273 this.previous = previous; 274 this.selectedItem = item; 275 }); 276 } 277 278 if (false !== this._trigger("select", event, { item: item })) { 279 this._value(item.value); 280 } 281 // reset the term after the select event 282 // this allows custom select handling to work properly 283 this.term = this._value(); 284 285 this.close(event); 286 this.selectedItem = item; 287 } 288 }); 289 290 this.liveRegion = $("<span>", { 291 role: "status", 292 "aria-live": "polite" 293 }) 294 .addClass("ui-helper-hidden-accessible"); 295 //.insertAfter(this.element); 296 297 if ($.fn.bgiframe) { 298 this.menu.element.bgiframe(); 299 } 300 301 // turning off autocomplete prevents the browser from remembering the 302 // value when navigating through history, so we re-enable autocomplete 303 // if the page is unloaded before the widget is destroyed. #7790 304 this._on(this.window, { 305 beforeunload: function () { 306 this.element.removeAttr("autocomplete"); 307 } 308 }); 309 },//end _create 310 311 _destroy: function () { 312 clearTimeout(this.searching); 313 this.element 314 .removeClass("ui-autocomplete-input") 315 .removeAttr("autocomplete"); 316 this.menu.element.remove(); 317 this.liveRegion.remove(); 318 }, 319 320 _setOption: function (key, value) { 321 this._super(key, value); 322 if (key === "source") { 323 this._initSource(); 324 } 325 if (key === "appendTo") { 326 this.menu.element.appendTo(this.document.find(value || "body")[0]); 327 } 328 if (key === "disabled" && value && this.xhr) { 329 this.xhr.abort(); 330 } 331 }, 332 333 _isMultiLine: function () { 334 // Textareas are always multi-line 335 if (this.element.is("textarea")) { 336 return true; 337 } 338 // Inputs are always single-line, even if inside a contentEditable element 339 // IE also treats inputs as contentEditable 340 if (this.element.is("input")) { 341 return false; 342 } 343 // All other element types are determined by whether or not they're contentEditable 344 return this.element.prop("isContentEditable"); 345 }, 346 347 _initSource: function () { 348 var array, url, 349 that = this; 350 if ($.isArray(this.options.source)) { 351 array = this.options.source; 352 this.source = function (request, response) { 353 response($.ui.autocomplete.filter(array, request.term)); 354 }; 355 } else if (typeof this.options.source === "string") { 356 url = this.options.source; 357 this.source = function (request, response) { 358 if (that.xhr) { 359 that.xhr.abort(); 360 } 361 that.xhr = $.ajax({ 362 url: url, 363 data: request, 364 dataType: "json", 365 success: function (data) { 366 response(data); 367 }, 368 error: function () { 369 response([]); 370 } 371 }); 372 }; 373 } else { 374 this.source = this.options.source; 375 } 376 }, 377 378 _searchTimeout: function (event) { 379 clearTimeout(this.searching); 380 this.searching = this._delay(function () { 381 // only search if the value has changed 382 if (this.term !== this._value()) { 383 this.selectedItem = null; 384 this.search(null, event); 385 } 386 }, this.options.delay); 387 }, 388 389 search: function (value, event) { 390 value = value != null ? value : this._value(); 391 392 // always save the actual value, not the one passed as an argument 393 this.term = this._value(); 394 395 if (value.length < this.options.minLength) { 396 return this.close(event); 397 } 398 399 if (this._trigger("search", event) === false) { 400 return; 401 } 402 403 return this._search(value); 404 }, 405 406 _search: function (value) { 407 this.pending++; 408 this.element.addClass("ui-autocomplete-loading"); 409 this.cancelSearch = false; 410 411 this.source({ term: value }, this._response()); 412 }, 413 414 _response: function () { 415 var that = this, 416 index = ++requestIndex; 417 418 return function (content) { 419 if (index === requestIndex) { 420 that.__response(content); 421 } 422 423 that.pending--; 424 if (!that.pending) { 425 that.element.removeClass("ui-autocomplete-loading"); 426 } 427 }; 428 }, 429 430 __response: function (content) { 431 if (content) { 432 content = this._normalize(content); 433 } 434 this._trigger("response", null, { content: content }); 435 //content && content.length && 436 if (!this.options.disabled && !this.cancelSearch) { 437 this._suggest(content); 438 this._trigger("open"); 439 } else { 440 // use ._close() instead of .close() so we don't cancel future searches 441 this._close(); 442 } 443 }, 444 445 close: function (event) { 446 this.cancelSearch = true; 447 this._close(event); 448 }, 449 450 _close: function (event) { 451 if (this.menu.element.is(":visible")) { 452 this.menu.element.hide(); 453 this.menu.blur(); 454 this.isNewMenu = true; 455 this._trigger("close", event); 456 } 457 }, 458 459 _change: function (event) { 460 if (this.previous !== this._value()) { 461 this._trigger("change", event, { item: this.selectedItem }); 462 } 463 }, 464 465 _normalize: function (items) { 466 // assume all items have the right format when the first item is complete 467 if (items.length && items[0].label && items[0].value) { 468 return items; 469 } 470 return $.map(items, function (item) { 471 if (typeof item === "string") { 472 return { 473 label: item, 474 value: item 475 }; 476 } 477 return $.extend({ 478 label: item.label || item.value, 479 value: item.value || item.label 480 }, item); 481 }); 482 }, 483 484 _suggest: function (items) { 485 var ul = this.menu.element 486 .empty() 487 .zIndex(this.element.zIndex() + 1); 488 this._renderMenu(ul, items); 489 this.menu.refresh(); 490 // size and position menu 491 ul.show(); 492 this._resizeMenu(); 493 ul.position($.extend({ 494 of: this.element 495 }, this.options.position)); 496 497 if (this.options.autoFocus) { 498 this.menu.next(); 499 } 500 }, 501 502 _resizeMenu: function () { 503 var that = this; 504 var ul = this.menu.element; 505 506 ul.outerWidth( 507 this.element.outerWidth() 508 ); 509 510 511 }, 512 513 _limitNumberOfItems:function(available) { 514 return this.options.max && this.options.max < available 515 ? this.options.max 516 : available; 517 }, 518 519 _renderMenu: function (ul, items) { 520 var that = this; 521 var max = that._limitNumberOfItems(items.length); 522 var num = 0; 523 for (var i = 0; i < max; i++) { 524 that._renderItemData(ul, items[i]); 525 num++; 526 } 527 //$.each(items, function (index, item) { 528 // that._renderItemData(ul, item); 529 // num++; 530 //}); 531 var bottomlist = "<span class='autocomplete_bottom'>" + 532 "<span class='firstline'>Showing " +num+" Users</span>" + 533 "<span class='secondline' >"+ 534 "<span class='sprite-icon sprite-icon-info-16x16'></span>"+ 535 "<span class='autocomplete_bottom_txt'>Please type in more characters to narrow down the results.</span>" + 536 "</span></span>"; 537 ul.append(bottomlist); 538 if (num == 0) { 539 $('.secondline').hide(); 540 $('.firstline').text('No search results.'); 541 } 542 }, 543 544 _renderItemData: function (ul, item) { 545 return this._renderItem(ul, item).data("ui-autocomplete-item", item); 546 }, 547 548 _renderItem: function (ul, item) { 549 return $("<li>") 550 .append($("<a>").text(item.label)) 551 .appendTo(ul); 552 }, 553 554 _move: function (direction, event) { 555 if (!this.menu.element.is(":visible")) { 556 this.search(null, event); 557 return; 558 } 559 if (this.menu.isFirstItem() && /^previous/.test(direction) || 560 this.menu.isLastItem() && /^next/.test(direction)) { 561 this._value(this.term); 562 this.menu.blur(); 563 return; 564 } 565 this.menu[direction](event); 566 }, 567 568 widget: function () { 569 return this.menu.element; 570 }, 571 572 _value: function () { 573 return this.valueMethod.apply(this.element, arguments); 574 }, 575 576 _keyEvent: function (keyEvent, event) { 577 if (!this.isMultiLine || this.menu.element.is(":visible")) { 578 this._move(keyEvent, event); 579 580 // prevents moving cursor to beginning/end of the text field in some browsers 581 event.preventDefault(); 582 } 583 } 584 });//end $.widget() 585 586 587 $.extend($.ui.autocomplete, { 588 escapeRegex: function (value) { 589 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); 590 }, 591 filter: function (array, term) { 592 var matcher = new RegExp($.ui.autocomplete.escapeRegex(term), "i"); 593 return $.grep(array, function (value) { 594 return matcher.test(value.label || value.value || value); 595 }); 596 } 597 }); 598 599 // live region extension, adding a `messages` option 600 // NOTE: This is an experimental API. We are still investigating 601 // a full solution for string manipulation and internationalization. 602 $.widget("ui.autocomplete", $.ui.autocomplete, { 603 options: { 604 messages: { 605 noResults: "No search results.", 606 results: function (amount) { 607 return amount + (amount > 1 ? " results are" : " result is") + 608 " available, use up and down arrow keys to navigate."; 609 } 610 } 611 }, 612 613 __response: function (content) { 614 var message; 615 this._superApply(arguments); 616 if (this.options.disabled || this.cancelSearch) { 617 return; 618 } 619 if (content && content.length) { 620 message = this.options.messages.results(content.length); 621 } else { 622 message = this.options.messages.noResults; 623 } 624 this.liveRegion.text(message).css('position',''); 625 } 626 }); 627 628 629 }(jQuery));