網上一搜各類插件,什麼文件上傳,圖片瀏覽 ,Autocomplete等javascript
這些插件,源代碼,新手看起來是很吃力的,固然我也是新手,我開始改AutoComplete這個插件一點點,卻真不知道如何開始,後面就用Firefox的fireDebug慢慢看。php
我也不是專門作前端的,可是總得了解,基本的使用仍是得會。css
今天,來總結下,最近看的插件的基礎知識,這裏算個入門,但願能幫到新手黨們。html
下面是2個很是重要的擴展函數。前端
$.extend and $.fn.extendjava
$.extend(object) 能夠理解爲JQuery 添加一個靜態方法。node
$.fn.extend(object) 能夠理解爲JQuery實例添加一個方法。jquery
基本的定義與調用:ajax
/* $.extend 定義與調用 */ $.extend({ fun1: function () { alert("執行方法一"); } }); $.fun1(); /* $.fn.extend 定義與調用 */ $.fn.extend({ fun2: function () { alert("執行方法2"); } }); $(this).fun2(); //等同於 $.fn.fun3 = function () { alert("執行方法三"); } $(this).fun3();
$.extend(target, [object1], [objectN]) 還有個用法,就是合併到第一個object.api
該方法主要用於合併兩個或更多對象的內容(屬性)到第一個對象,並返回合併後的第一對象。若是該方法只有一個參數target,則該參數將擴展jQuery的命名空間,即做爲靜態方法掛在jQuery全局對象下,如jQuery裏的$.ajax、$.getJSON全局函數等,如前面看到的:
值得注意的是:多個對象參數合併時,會破壞第一個對象的結構,因此可傳遞一個空對象做爲第一個參數,如:$.extend({}, object1, object2);
var options = $.extend({},defaults, options);
上面代碼就是合併到{}中,而後賦值到options。這個在寫插件的時候常用,用戶能夠本身定義某些屬性,
插件通常也會定義默認屬性,調用上面方法後,若是自定義屬性,這使用新屬性,不然,則使用默認屬性,這個是很是有用的。
$.fn.extend(target)
在jQuery中,$.fn本質上是等於jQuery的原型,即$.fn = $.prototype, 因此該方法其實是爲jQuery原型添加方法,即把target對象的方法添加到jQuery原型中去,這樣jQuery對象實例就能夠訪問添加的方法了,這也是jQuery插件開發經常使用的方法,特別是添加多個接口時。如:
// 將hello、hello2方法添加到jquery原型中 $.fn.extend({ hello: function() {alert("hello!");}, hello2: function() {alert("hello again!);} });
看看Autocomplete插件使用的該方法。
$.fn.extend({ autocomplete: function(urlOrData, options) { var isUrl = typeof urlOrData == "string"; options = $.extend({}, $.Autocompleter.defaults, { url: isUrl ? urlOrData : null, data: isUrl ? null : urlOrData, delay: isUrl ? $.Autocompleter.defaults.delay : 10 max: options && !options.scroll ? 1000 : 15000 }, options); // if highlight is set to false, replace it with a do-nothing function options.highlight = options.highlight || function(value) { return value; }; // if the formatMatch option is not specified, then use formatItem for backwards compatibility options.formatMatch = options.formatMatch || options.formatItem; return this.each(function() { new $.Autocompleter(this, options); }); }, result: function(handler) { return this.bind("result", handler); }, search: function(handler) { return this.trigger("search", [handler]); }, flushCache: function() { return this.trigger("flushCache"); }, setOptions: function(options){ return this.trigger("setOptions", [options]); }, unautocomplete: function() { return this.trigger("unautocomplete"); } });
看不懂?不要緊,下面馬來接上。
若是添加單個方法到jQuery原型中,可以使用$.fn.pluginName方法添加,如:
// 將hello方法添加到jquery原型中 $.fn.hello = function() { // ... };
插件通用模板(單個方法):
插件有本身的基本格式:
;(function($){ $.fn.yourName = function(options){ //各類屬性、參數 } var options = $.extend(defaults, options); return this.each(function(){ //插件實現代碼 }); }; })(jQuery);
上面代碼的最前面有一個分號,這是爲了防止多個腳本文件合併時,其餘腳本的結尾語句沒有添加分號,形成運行時錯誤。
一、把所有代碼放在閉包(一個即時執行函數)裏
此時閉包至關於一個私有做用域,外部沒法訪問到內部的信息,而且不會存在全局變量的污染狀況。官方建立開發規範的解釋是:
a) 避免全局依賴;
b) 避免第三方破壞;
c) 兼容jQuery操做符'$'和'jQuery '。
以下所示:
(function($) { // 局部做用域中使用$來引用jQuery // ... })(jQuery);
這段代碼在被解析時可理解成如下代碼:
var jQ = function($) { // code goes here }; jQ(jQuery);
新手若是沒法理解,你知道,這是官方標準就ok。
下面是這個美化表格的例子,實現原理卻是簡單,無非就是找到表格的奇偶行,而後添加不一樣的class,活動行高亮顯示也很簡單,只要判斷mouseover事件,而後添加一個class,mouseout的時候,再去掉這個class便可。
查看這個連接能看到詳細,你能夠查看具體看看,寫的不錯,特別是步驟很清晰。
http://www.cnblogs.com/JustinYoung/archive/2010/03/30/jquery-chajian.html
好,基本框架曉得了,下面就是填充具體內容了。
(function($){ $.fn.tableUI = function(options){ var defaults = { evenRowClass:"evenRow", oddRowClass:"oddRow", activeRowClass:"activeRow" } var options = $.extend({},defaults, options); this.each(function(){ //實現代碼 }); }; })(jQuery);
重複一句:
var options = $.extend({},defaults, options);
其實就是合併多個對象爲一個。這裏就是,若是你在調用的時候寫了新的參數,就用你新的參數,若是沒有寫,就用默認的參數。想進一步瞭解的朋友,能夠參考jquery的官方文檔: http://api.jquery.com/jQuery.extend/
其實,這裏定義默認 var defaults是不規則的寫法,
規則該是這樣
$.fn.TableUI.defaults={ evenRowCass: "evenRow", oddRowClass: "oddRow", activeRowClass: "activeRow" }; options = $.extend({},$.fn.TableUI.defaults,options);
好了,直接看所有代碼:
//這裏最好寫上相關描述,如時間,做者,主要用於幹什麼
/*
* tableUI 0.1
* Copyright (c) 2009 JustinYoung http://justinyoung.cnblogs.com/
* Date: 2010-03-30
* 使用tableUI能夠方便地將表格提示使用體驗。先提供的功能有奇偶行顏色交替,鼠標移上高亮顯示
*/
;(function($){ $.fn.tableUI = function(options){
$.fn.TableUI.defaults={
evenRowCass: "evenRow",
oddRowClass: "oddRow",
activeRowClass: "activeRow"
};
var options = $.extend({}, $.fn.TableUI.defaults, options); //合併 return this.each(function(){ var thisTable=$(this); //添加奇偶行顏色 $(thisTable).find("tr:even").addClass(options.evenRowClass); $(thisTable).find("tr:odd").addClass(options.oddRowClass); //添加活動行顏色 $(thisTable).find("tr").bind("mouseover",function(){ $(this).addClass(options.activeRowClass); }); $(thisTable).find("tr").bind("mouseout",function(){ $(this).removeClass(options.activeRowClass); }); }); }; })(jQuery);
效果就是下面這個效果,固然會有點不同,由於我本身從新定義了css樣式.:
http://downloads.cnblogs.com/justinyoung/articleIMG/2010/tableUI/index.html
下面是調用代碼。
<script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script> <script src="Scripts/TableUI.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function () { $(".table_solid").TableUI({ evenRowCass: "eventRow", oddRowClass: "oddRowClass", activeRowClass: "activeRow" }); //這裏是輸入新的參數,不使用默認參數。插件代碼會自動覆蓋。 }) </script> <style type="text/css"> body { font-family: 'Verdana' , '宋體'; font-size: 12px; } .eventRow { background-color: #f0f0ff; } .activeRow { background-color: #FFFF55; } .oddRowClass { background-color: red; } </style>
在看下面的代碼時,涉及到模式,這裏介紹兩個經常使用的模式,
單例模式
<script> /* * Here’s the setup function which populates an array with integers */ var myApp = { init: function (obj) { //code to intialize your application alert("init: " + obj); }, dashboard: function () { //your dashboard feature goes here alert("dashboard"); }, controlPanel: function () { //control panel code goes here alert("controlPanel"); }, appSettings: function () { //code that updates settings goes here alert("appSettings"); } } //kick it off with ready() $(function () { myApp.init("test"); myApp.dashboard(); myApp.controlPanel(); myApp.appSettings(); }); </script>
使用應用程序名稱空間,能夠極大地下降變量名衝突的可能性
模塊模式
模塊模式主要包含三個主要的組件:一個與前面相似的名稱空間,一個當即執行函數和函數返回的對象。
該返回對象包含了共有方法和共有屬性。
<script type="text/javascript"> //Our app namespace. We pass in the jQuery object to shorten lookups var myApp = function ($) { //當即執行函數 // private variables and methods, only available within this myApp //私有變量和方法 var message = "not directly accessible from outside the module"; function multiplier(x, y) { return x * y }; //the return object contains the public 函數的返回對象 //properties and public methods return { init: function () { //intialize the app alert("return init"); }, prop: "42", specialNumber: function () { //access to our private method 訪問私有方法 var num = multiplier(7, 6); return "Our special number is definitely " + num; }, //we provide controlled access to private variables shareMessage: function (arg) { if (arg === "open sesame") { return message + ",unless you know the magic word"; } else { throw new Error("You need to know the magic word"); } } }; } (jQuery); myApp.dashboard = function ($) { // private variables and methods var config = { "color": "blue", "title": "my dashboard", "width": "960px" }; return { init: function () { //intialize the dashboard }, //updateConfig allows for monitored configuration //of the private config object updateConfig: function (obj) { if ($.inArray(obj.color, ["red", "blue", "green", "purple"] !== -1)) { config.color = obj.color; } config.title = obj.title || config.title; config.width = obj.width || config.width; }, render: function () { //renders the dashboard var $dashboard = $("<div/>").html("<h1/>") $dashboard.text(config.title) .css( { "width": config.width, "color": config.color } ); $("#main").append($dashboard); } }; } (jQuery); console.log("message:" + myApp.message ) //undefined console.log("multiplier" + myApp.multiplier() ) console.log("shareMessage :"+ myApp.shareMessage( "please?" ) ) console.log( myApp.shareMessage( "open sesame" ) ) console.log( myApp.prop ); console.log( myApp.specialNumber() ); </script>
多看一個實例
<script type="text/javascript"> var blogModule = (function () { var my = {}, privateName = "博客園"; function privateAddTopic(data) { // 這裏是內部處理代碼 } my.Name = privateName; my.AddTopic = function (data) { privateAddTopic(data); }; return my; } ()); //咱們能夠在最後一個}後面加上一個括號(),來達到自執行的目的,這樣該實例在內存中只會存在一份copy //注意,匿名函數後面的括號,這是JavaScript語言所要求的,由於若是你不聲明的話, //JavaScript解釋器默認是聲明一個function函數,有括號,就是建立一個函數表達式,也就是自執行,用的時候不用和上面那樣在new了, //固然你也能夠這樣來聲明: blogModule.AddTopic('test'); var jspy = (function () { //首先咱們創造一個_count變量,下劃線代表它是一個私有變量。在Javascript中下劃線並無什麼實際的意義, //可是它是一個用來標明私有變量的廣泛用法。如今函數就能夠操縱、返回變量了: var _count = 0; var incrementCount = function () { _count++; }; var getCount = function () { return _count; }; return { Add: incrementCount, Get: getCount }; })(); jspy.Add(); alert(jspy.Get()) //app的名稱空間,傳入jQuery對象以縮短查找過程 var myApp = function ( $ ) { //私有變量和方法,僅在該myApp中可用 var message = "not directly accessible from outside the module"; function multiplier(x, y) { return x * y; }; //返回對象包含了公有屬性和公有方法 return { init: function () { //初始化app alert("初始化app"); }, prop: "42", specialNumber: function () { //訪問私有方法 var num = multiplier(7, 6); return "our special number is definitly " + num; }, //對私有變量的提供有控制的訪問 shareMessage: function (arg) { if (arg === "open sesame") { return message + ", unless you know the magic word"; } else { return ("You need to know the magic word"); } } }; }(jQuery); console.log("access message in myApp:" + myApp.message); //undefined console.log("call init in return:" + myApp.init()); // // console.log("call multiplier:" + myApp.multiplier(2, 3)); //throw exception Object doesn't support property or method 'multiplier' console.log("share message in return " + myApp.shareMessage()) //it's ok console.log("share arg message in return " + myApp.shareMessage("open sesame")) //it's ok console.log("show prop in return :" + myApp.prop); //it's work console.log("show specialNumber in return :" + myApp.specialNumber()); //it's work </script>
在控制檯中測試該實例app,在模塊內,能夠引用私有變量,可是在模塊以外,沒法直接調用私有變量
附加自動完成組件源代碼
/* * Autocomplete - jQuery plugin 1.1pre * * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer * * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * * Revision: $Id: jquery.autocomplete.js 5785 2008-07-12 10:37:33Z joern.zaefferer $ */ ;(function($) { $.fn.extend({ autocomplete: function(urlOrData, options) { var isUrl = typeof urlOrData == "string"; options = $.extend({}, $.Autocompleter.defaults, { url: isUrl ? urlOrData : null, data: isUrl ? null : urlOrData, delay: isUrl ? $.Autocompleter.defaults.delay : 0, max: options && !options.scroll ? 9999999 : 9999999 }, options); // if highlight is set to false, replace it with a do-nothing function options.highlight = options.highlight || function(value) { return value; }; // if the formatMatch option is not specified, then use formatItem for backwards compatibility options.formatMatch = options.formatMatch || options.formatItem; return this.each(function() { new $.Autocompleter(this, options); }); }, result: function(handler) { return this.bind("result", handler); }, search: function(handler) { return this.trigger("search", [handler]); }, flushCache: function() { return this.trigger("flushCache"); }, setOptions: function(options){ return this.trigger("setOptions", [options]); }, unautocomplete: function() { return this.trigger("unautocomplete"); } }); $.Autocompleter = function(input, options) { var NO_DATA_FOUND = "No Data Found"; var KEY = { UP: 38, DOWN: 40, DEL: 46, TAB: 9, RETURN: 13, ESC: 27, COMMA: 188, PAGEUP: 33, PAGEDOWN: 34, BACKSPACE: 8 }; // Create $ object for input element var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass); var timeout; var previousValue = ""; var cache = $.Autocompleter.Cache(options); var hasFocus = 0; var lastKeyPressCode; var config = { mouseDownOnSelect: false }; var select = $.Autocompleter.Select(options, input, selectCurrent, config); var blockSubmit; // prevent form submit in opera when selecting with return key $.browser.opera && $(input.form).bind("submit.autocomplete", function() { if (blockSubmit) { blockSubmit = false; return false; } }); // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) { // track last key pressed lastKeyPressCode = event.keyCode; switch(event.keyCode) { case KEY.UP: event.preventDefault(); if ( select.visible() ) { select.prev(); } else { onChange(0, true); } break; case KEY.DOWN: event.preventDefault(); if ( select.visible() ) { select.next(); } else { onChange(0, true); } break; case KEY.PAGEUP: event.preventDefault(); if ( select.visible() ) { select.pageUp(); } else { onChange(0, true); } break; case KEY.PAGEDOWN: event.preventDefault(); if ( select.visible() ) { select.pageDown(); } else { onChange(0, true); } break; // matches also semicolon case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA: case KEY.TAB: case KEY.RETURN: if( selectCurrent() ) { // stop default to prevent a form submit, Opera needs special handling event.preventDefault(); blockSubmit = true; return false; } break; case KEY.ESC: select.hide(); break; default: clearTimeout(timeout); timeout = setTimeout(onChange, options.delay); break; } }).focus(function(){ // track whether the field has focus, we shouldn't process any // results if the field no longer has focus hasFocus++; }).blur(function() { hasFocus = 0; if (!config.mouseDownOnSelect) { hideResults(); } }).click(function() { // show select when clicking in a focused field if ( hasFocus++ > 1 && !select.visible() ) { onChange(0, true); } }).bind("search", function() { // TODO why not just specifying both arguments? var fn = (arguments.length > 1) ? arguments[1] : null; function findValueCallback(q, data) { var result; if( data && data.length ) { for (var i=0; i < data.length; i++) { if( data[i].result.toLowerCase() == q.toLowerCase() ) { result = data[i]; break; } } } if( typeof fn == "function" ) fn(result); else $input.trigger("result", result && [result.data, result.value]); } $.each(trimWords($input.val()), function(i, value) { request(value, findValueCallback, findValueCallback); }); }).bind("flushCache", function() { cache.flush(); }).bind("setOptions", function() { $.extend(options, arguments[1]); // if we've updated the data, repopulate if ( "data" in arguments[1] ) cache.populate(); }).bind("unautocomplete", function() { select.unbind(); $input.unbind(); $(input.form).unbind(".autocomplete"); }); function selectCurrent() { var selected = select.selected(); if( !selected ) return false; var v = selected.result; previousValue = v; if ( options.multiple ) { var words = trimWords($input.val()); if ( words.length > 1 ) { v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v; } v += options.multipleSeparator; } if(v === NO_DATA_FOUND) v = ""; $input.val(v); hideResultsNow(); $input.trigger("result", [selected.data, selected.value]); return true; } function onChange(crap, skipPrevCheck) { if( lastKeyPressCode == KEY.DEL ) { select.hide(); return; } var currentValue = $input.val(); if ( !skipPrevCheck && currentValue == previousValue ) return; previousValue = currentValue; currentValue = lastWord(currentValue); if ( currentValue.length >= options.minChars) { $input.addClass(options.loadingClass); if (!options.matchCase) currentValue = currentValue.toLowerCase(); request(currentValue, receiveData, hideResultsNow); } else { stopLoading(); select.hide(); } }; function trimWords(value) { if ( !value ) { return [""]; } var words = value.split( options.multipleSeparator ); var result = []; $.each(words, function(i, value) { if ( $.trim(value) ) result[i] = $.trim(value); }); return result; } function lastWord(value) { if ( !options.multiple ) return value; var words = trimWords(value); return words[words.length - 1]; } // fills in the input box w/the first match (assumed to be the best match) // q: the term entered // sValue: the first matching result function autoFill(q, sValue){ // autofill in the complete box w/the first match as long as the user hasn't entered in more data // if the last user key pressed was backspace, don't autofill if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) { // fill in the value (keep the case the user has typed) $input.val($input.val() + sValue.substring(lastWord(previousValue).length)); // select the portion of the value not typed by the user (so the next character will erase) $.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length); } }; function hideResults() { clearTimeout(timeout); timeout = setTimeout(hideResultsNow, 200); }; function hideResultsNow() { var wasVisible = select.visible(); select.hide(); clearTimeout(timeout); stopLoading(); if (options.mustMatch) { // call search and run callback $input.search( function (result){ // if no value found, clear the input box if( !result ) { if (options.multiple) { var words = trimWords($input.val()).slice(0, -1); $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") ); } else $input.val( "" ); } } ); } if (wasVisible) // position cursor at end of input field $.Autocompleter.Selection(input, input.value.length, input.value.length); }; function receiveData(q, data) { if ( data && data.length && hasFocus ) { stopLoading(); select.display(data, q); autoFill(q, data[0].value); select.show(); } else { hideResultsNow(); } }; function request(term, success, failure) { if (!options.matchCase) term = term.toLowerCase(); var data = cache.load(term); // recieve the cached data if (data && data.length) { success(term, data); // if an AJAX url has been supplied, try loading the data now } else if( (typeof options.url == "string") && (options.url.length > 0) ){ var extraParams = { timestamp: +new Date() }; $.each(options.extraParams, function(key, param) { extraParams[key] = typeof param == "function" ? param() : param; }); $.ajax({ // try to leverage ajaxQueue plugin to abort previous requests mode: "abort", // limit abortion to this input port: "autocomplete" + input.name, dataType: options.dataType, url: options.url, data: $.extend({ q: lastWord(term), limit: options.max }, extraParams), success: function(data) { //added by lideng ,for if don't find data ,then assign 'No data found' to control if(!data) data = NO_DATA_FOUND;//alert('No data found'); var parsed = options.parse && options.parse(data) || parse(data); cache.add(term, parsed); success(term, parsed); } }); } else { // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match select.emptyList(); failure(term); } }; function parse(data) { var parsed = []; var rows = data.split("\n"); for (var i=0; i < rows.length; i++) { var row = $.trim(rows[i]); if (row) { row = row.split("|"); parsed[parsed.length] = { data: row, value: row[0], result: options.formatResult && options.formatResult(row, row[0]) || row[0] }; } } return parsed; }; function stopLoading() { $input.removeClass(options.loadingClass); }; }; $.Autocompleter.defaults = { inputClass: "ac_input", resultsClass: "ac_results", loadingClass: "ac_loading", minChars: 1, //delay: 400, delay: 0, matchCase: false, //matchSubset: true, matchSubset: false, matchContains: false, cacheLength: 10, max: 100, mustMatch: false, extraParams: {}, selectFirst: true, formatItem: function(row) { return row[0]; }, formatMatch: null, autoFill: false, width: 0, multiple: false, multipleSeparator: ", ", highlight: function(value, term) { return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>"); }, scroll: true, scrollHeight: 180 }; $.Autocompleter.Cache = function(options) { var data = {}; var length = 0; function matchSubset(s, sub) { if (!options.matchCase) s = s.toLowerCase(); var i = s.indexOf(sub); if (options.matchContains == "word"){ i = s.toLowerCase().search("\\b" + sub.toLowerCase()); } if (i == -1) return false; return i == 0 || options.matchContains; }; function add(q, value) { if (length > options.cacheLength){ flush(); } if (!data[q]){ length++; } data[q] = value; } function populate(){ if( !options.data ) return false; // track the matches var stMatchSets = {}, nullData = 0; // no url was specified, we need to adjust the cache length to make sure it fits the local data store if( !options.url ) options.cacheLength = 1; // track all options for minChars = 0 stMatchSets[""] = []; // loop through the array and create a lookup structure for ( var i = 0, ol = options.data.length; i < ol; i++ ) { var rawValue = options.data[i]; // if rawValue is a string, make an array otherwise just reference the array rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue; var value = options.formatMatch(rawValue, i+1, options.data.length); if ( value === false ) continue; var firstChar = value.charAt(0).toLowerCase(); // if no lookup array for this character exists, look it up now if( !stMatchSets[firstChar] ) stMatchSets[firstChar] = []; // if the match is a string var row = { value: value, data: rawValue, result: options.formatResult && options.formatResult(rawValue) || value }; // push the current match into the set list stMatchSets[firstChar].push(row); // keep track of minChars zero items if ( nullData++ < options.max ) { stMatchSets[""].push(row); } }; // add the data items to the cache $.each(stMatchSets, function(i, value) { // increase the cache size options.cacheLength++; // add to the cache add(i, value); }); } // populate any existing data setTimeout(populate, 25); function flush(){ data = {}; length = 0; } return { flush: flush, add: add, populate: populate, load: function(q) { if (!options.cacheLength || !length) return null; /* * if dealing w/local data and matchContains than we must make sure * to loop through all the data collections looking for matches */ if( !options.url && options.matchContains ){ // track all matches var csub = []; // loop through all the data grids for matches for( var k in data ){ // don't search through the stMatchSets[""] (minChars: 0) cache // this prevents duplicates if( k.length > 0 ){ var c = data[k]; $.each(c, function(i, x) { // if we've got a match, add it to the array if (matchSubset(x.value, q)) { csub.push(x); } }); } } return csub; } else // if the exact item exists, use it if (data[q]){ return data[q]; } else if (options.matchSubset) { for (var i = q.length - 1; i >= options.minChars; i--) { var c = data[q.substr(0, i)]; if (c) { var csub = []; $.each(c, function(i, x) { if (matchSubset(x.value, q)) { csub[csub.length] = x; } }); return csub; } } } return null; } }; }; $.Autocompleter.Select = function (options, input, select, config) { var CLASSES = { ACTIVE: "ac_over" }; var listItems, active = -1, data, term = "", needsInit = true, element, list; // Create results function init() { if (!needsInit) return; element = $("<div/>") .hide() .addClass(options.resultsClass) .css("position", "absolute") .appendTo(document.body); list = $("<ul/>").appendTo(element).mouseover( function(event) { if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') { active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event)); $(target(event)).addClass(CLASSES.ACTIVE); } }).click(function(event) { $(target(event)).addClass(CLASSES.ACTIVE); select(); // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus input.focus(); return false; }).mousedown(function() { config.mouseDownOnSelect = true; }).mouseup(function() { config.mouseDownOnSelect = false; }); if( options.width > 0 ) element.css("width", options.width); needsInit = false; } function target(event) { var element = event.target; while(element && element.tagName != "LI") element = element.parentNode; // more fun with IE, sometimes event.target is empty, just ignore it then if(!element) return []; return element; } function moveSelect(step) { listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE); movePosition(step); var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE); if(options.scroll) { var offset = 0; listItems.slice(0, active).each(function() { offset += this.offsetHeight; }); if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) { list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight()); } else if(offset < list.scrollTop()) { list.scrollTop(offset); } } }; function movePosition(step) { active += step; if (active < 0) { active = listItems.size() - 1; } else if (active >= listItems.size()) { active = 0; } } function limitNumberOfItems(available) { return options.max && options.max < available ? options.max : available; } function fillList() { list.empty(); var max = limitNumberOfItems(data.length); for (var i=0; i < max; i++) { if (!data[i]) continue; var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term); if ( formatted === false ) continue; var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0]; $.data(li, "ac_data", data[i]); } listItems = list.find("li"); if ( options.selectFirst ) { listItems.slice(0, 1).addClass(CLASSES.ACTIVE); active = 0; } // apply bgiframe if available if ( $.fn.bgiframe ) list.bgiframe(); } return { display: function(d, q) { init(); data = d; term = q; fillList(); }, next: function() { moveSelect(1); }, prev: function() { moveSelect(-1); }, pageUp: function() { if (active != 0 && active - 8 < 0) { moveSelect( -active ); } else { moveSelect(-8); } }, pageDown: function() { if (active != listItems.size() - 1 && active + 8 > listItems.size()) { moveSelect( listItems.size() - 1 - active ); } else { moveSelect(8); } }, hide: function() { element && element.hide(); listItems && listItems.removeClass(CLASSES.ACTIVE); active = -1; }, visible : function() { return element && element.is(":visible"); }, current: function() { return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]); }, show: function() { var offset = $(input).offset(); element.css({ width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(), top: offset.top + input.offsetHeight, left: offset.left }).show(); if(options.scroll) { list.scrollTop(0); list.css({ maxHeight: options.scrollHeight, overflow: 'auto' }); if($.browser.msie && ((typeof document.body.style.maxHeight === "undefined") || (document.body.style.maxHeight == ''))) { //if($.browser.msie && ((typeof document.body.style.maxHeight === "undefined"))) { var listHeight = 0; listItems.each(function() { listHeight += this.offsetHeight; }); var scrollbarsVisible = listHeight > options.scrollHeight; list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight ); // if (!scrollbarsVisible) { // // IE doesn't recalculate width when scrollbar disappears // listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) ); // } } } }, selected: function() { var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE); return selected && selected.length && $.data(selected[0], "ac_data"); }, emptyList: function (){ list && list.empty(); }, unbind: function() { element && element.remove(); } }; }; $.Autocompleter.Selection = function(field, start, end) { if( field.createTextRange ){ var selRange = field.createTextRange(); selRange.collapse(true); selRange.moveStart("character", start); selRange.moveEnd("character", end); selRange.select(); } else if( field.setSelectionRange ){ field.setSelectionRange(start, end); } else { if( field.selectionStart ){ field.selectionStart = start; field.selectionEnd = end; } } field.focus(); }; })(jQuery);
簡單實如今這裏了,歡迎交流學習