zoukankan      html  css  js  c++  java
  • Jquery 插件入门

      网上一搜各种插件,什么文件上传,图片浏览 ,Autocomplete等

    这些插件,源代码,新手看起来是很吃力的,当然我也是新手,我开始改AutoComplete这个插件一点点,却真不知道如何开始,后面就用Firefox的fireDebug慢慢看。

    我也不是专门做前端的,但是总得了解,基本的使用还是得会。

    今天,来总结下,最近看的插件的基础知识,这里算个入门,希望能帮到新手党们。

    下面是2个非常重要的扩展函数。

    $.extend  and $.fn.extend

    $.extend(object) 可以理解为JQuery 添加一个静态方法。

    $.fn.extend(object) 可以理解为JQuery实例添加一个方法。

    基本的定义与调用:

    /* $.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.

    该方法主要用于合并两个或更多对象的内容(属性)到第一个对象,并返回合并后的第一对象。如果该方法只有一个参数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);

     上面代码的最前面有一个分号,这是为了防止多个脚本文件合并时,其他脚本的结尾语句没有添加分号,造成运行时错误。

    1、把全部代码放在闭包(一个即时执行函数)里

    此时闭包相当于一个私有作用域,外部无法访问到内部的信息,并且不会存在全局变量的污染情况。官方创建开发规范的解释是:

    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>
    View Code

    使用应用程序名称空间,可以极大地降低变量名冲突的可能性

    模块模式

    模块模式主要包含三个主要的组件:一个与前面类似的名称空间,一个立即执行函数和函数返回的对象。

    该返回对象包含了共有方法和共有属性。

    <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>
    View Code

     多看一个实例

     <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>
    View Code

    在控制台中测试该实例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("
    ");
            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,
         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({
                     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);
    View Code

    简单实现在这里了,欢迎交流学习

  • 相关阅读:
    PHP基础学习笔记(一)
    安装wampserver之后,浏览器中输入localhost页面显示IIS7解决办法
    HTML5常识总结(一)
    AngularJs中的服务
    AngularJs中的directives(指令part1)
    Happy Number——LeetCode
    Binary Tree Zigzag Level Order Traversal——LeetCode
    Construct Binary Tree from Preorder and Inorder Traversal——LeetCode
    Construct Binary Tree from Inorder and Postorder Traversal——LeetCode
    Convert Sorted Array to Binary Search Tree——LeetCode
  • 原文地址:https://www.cnblogs.com/lideng/p/3605551.html
Copyright © 2011-2022 走看看