bootstrap插件学习-bootstrap.typehead.js - HackerVirus

bootstrap插件学习-bootstrap.typehead.js

bootstrap插件学习-bootstrap.typehead.js

先看bootstrap.typehead.js的结构

var Typeahead = function ( element, options ){} //构造器
Typeahead.prototype = {} //构造器的原型
$.fn.typeahead = function ( option ){} //jQuery原型上自定义方法
$.fn.typeahead.defaults ={} //默认参数
$.fn.typeahead.Constructor = Typeahead //重写jQuery原型上的自定义方法的构造器名
$(function () {}) //初始化

HTML结构

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

<input class="span3" type="text"
       data-source="[\'Alabama\',\'Alaska\',\'Arizona\',\'Arkansas\',\'California\',\'Colorado\',\'Connecticut\',\'Delaware\',\'Florida\'
       ,\'Georgia\',\'Hawaii\',\'Idaho\',\'Illinois\',\'Indiana\',\'Iowa\',\'Kansas\',\'Kentucky\',\'Louisiana\',\'Maine\',\'Maryland\'
       ,\'Massachusetts\',\'Michigan\',\'Minnesota\',\'Mississippi\',\'Missouri\',\'Montana\',\'Nebraska\',\'Nevada\',\'New Hampshire\'
       ,\'New Jersey\',\'New Mexico\',\'New York\',\'North Dakota\',\'North Carolina\',\'Ohio\',\'Oklahoma\',\'Oregon\',\'Pennsylvania\'
       ,\'Rhode Island\',\'South Carolina\',\'South Dakota\',\'Tennessee\',\'Texas\',\'Utah\',\'Vermont\',\'Virginia\',\'Washington\'
       ,\'West Virginia\',\'Wisconsin\',\'Wyoming\']" data-items="4" data-provide="typeahead" />

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

先从初始化开始

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

/*
    * 初始化
    * */
    $(function () {
        $(\'body\').on(\'focus.typeahead.data-api\', \'[data-provide="typeahead"]\', function (e) {
            var $this = $(this)
            if ($this.data(\'typeahead\')) return
            e.preventDefault() //阻止冒泡
            $this.typeahead($this.data())
        })
    })

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

为所有拥有data-provide=\'typehead\'属性的标签绑定focus事件,并且阻止事件冒泡,进入jQuery的原型方法中

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

/*
    * jQuery原型上自定义方法
    * */
    $.fn.typeahead = function ( option ) {
        return this.each(function () {
            var $this = $(this)
                , data = $this.data(\'typeahead\')
                , options = typeof option == \'object\' && option
            if (!data) $this.data(\'typeahead\', (data = new Typeahead(this, options)))//实例化构造器
            if (typeof option == \'string\') data[option]()
        })
    }

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

由于,$this.data(\'typehead\')为空,我们执行实例化。

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

/*
    * 构造器
    * */
    var Typeahead = function ( element, options ) {
        this.$element = $(element)
        this.options = $.extend({}, $.fn.typeahead.defaults, options)
        this.matcher = this.options.matcher || this.matcher//原型上的matcher方法
        this.sorter = this.options.sorter || this.sorter//原型上的sorter方法
        this.highlighter = this.options.highlighter || this.highlighter//原型上的highlighter方法
        this.$menu = $(this.options.menu).appendTo(\'body\')//将下拉框ul加入body中,返回ul的jQuery对象
        this.source = this.options.source //获得input控件里的参数。input优先级大于默认项
        this.shown = false
        this.listen()
    }

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

这里注意一点是,控件中的data属性与默认项中如果有重名属性的话,data属性优先级高,会覆盖。进入listen方法

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

listen: function () {
            this.$element
                .on(\'blur\',     $.proxy(this.blur, this))//绑定blur事件
                .on(\'keypress\', $.proxy(this.keypress, this))//绑定keypress事件
                .on(\'keyup\',    $.proxy(this.keyup, this))//绑定keyup事件
            /*
            * 如果浏览器内核是webkit或者是ie内核的,绑定keydown事件。
            * */
            if ($.browser.webkit || $.browser.msie) {
                this.$element.on(\'keydown\', $.proxy(this.keypress, this))
            }
            /*
            * ul绑定click事件和mouserenter事件
            * */
            this.$menu
                .on(\'click\', $.proxy(this.click, this))
                .on(\'mouseenter\', \'li\', $.proxy(this.mouseenter, this))
        }

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

完成事件监听的绑定工作,至此初始化工作完成。原型上拥有一堆API,让我们一一尝试。

先看input的keypress事件,对应的keypress方法

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

keypress: function (e) {
            if (!this.shown) return

            switch(e.keyCode) {
                case 9: // tab
                case 13: // enter
                case 27: // escape
                    e.preventDefault()
                    break

                case 38: // up arrow
                    e.preventDefault()
                    this.prev()
                    break

                case 40: // down arrow
                    e.preventDefault()
                    this.next()
                    break
            }

            e.stopPropagation()  //阻止冒泡

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

键盘上的按钮对应响应的keyCode,preventDefault方法将阻止元素放生默认行为。如果按了向上的箭头,进入prev方法

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

prev: function (event) {
            var active = this.$menu.find(\'.active\').removeClass(\'active\')
                , prev = active.prev()

            if (!prev.length) {
                prev = this.$menu.find(\'li\').last()
            }

            prev.addClass(\'active\')
        }

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

ul中li元素遍历,上箭头可以向上选择li元素,如果到顶了,则返回最下面的li元素重新开始。被选中的li元素会拥有active类,一个选中样式,其他li则没有。

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

next: function (event) {
            var active = this.$menu.find(\'.active\').removeClass(\'active\')
                , next = active.next()

            if (!next.length) {
                next = $(this.$menu.find(\'li\')[0])
            }

            next.addClass(\'active\')
        }

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

next的方法与prev类似,像API中还有类似的方法,大家依照demo,一调试就可以出来,比如mouseenter,click,blur,show,hide方法等。

对于具有联想功能的插件,学习学习它的联想功能如何实现比较重要。

好,我们从keyup开始

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

keyup: function (e) {
            switch(e.keyCode) {
                case 40: // down arrow
                case 38: // up arrow
                    break

                case 9: // tab
                case 13: // enter
                    if (!this.shown) return
                    this.select()
                    break

                case 27: // escape
                    if (!this.shown) return
                    this.hide()
                    break

                default:
                    this.lookup()// 一般按键
            }

            e.stopPropagation()
            e.preventDefault()
        }

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

如果我们输入一般字母,进入默认的lookup方法

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

 /*
          * 查询匹配方法
          * */
        , lookup: function (event) {
            var that = this
                , items
                , q

            this.query = this.$element.val()//获取用户输入内容
            if (!this.query) {   //不输入空格
                return this.shown ? this.hide() : this
            }

            items = $.grep(this.source, function (item) {
                if (that.matcher(item)) return item
            })//遍历this.source,其中的成员执行函数,最后返回结果
            items = this.sorter(items)
if (!items.length) { return this.shown ? this.hide() : this } /*截取前4位*/ return this.render(items.slice(0, this.options.items)).show() }

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

这里代码有个错误,就是这个this.source,它的类型不是object,或者是一个数组,导致最后联想功能只能匹配一个字符串,现在的this.source只是字符串,所以我们需要让它变成object对象,有人用json的API,可以,浏览器不兼容怎么办,引入J相关JSON的兼容包就可以搞定了,那还有了,对,eval。这个方法缺点一大堆,不过简单粗暴直接。我们修改一下源码

items = $.grep(eval(this.source), function (item) {
                if (that.matcher(item)) return item
            })

其实解决办法很多,我这里图方便了,我们继续。修改return

return this.render(items).show();

进入render方法

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

/*
          * 生成li标签
          * */
        , render: function (items) {
            var that = this
            items = $(items).map(function (i, item) {
                i = $(that.options.item).attr(\'data-value\', item) //往li中塞值
                i.find(\'a\').html(that.highlighter(item))
                return i[0]
            })
            items.first().addClass(\'active\') //默认下拉内容中第一个显示
            this.$menu.html(items) //将生气li标签插入ul中
            return this
        }

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

看一下highlighter函数

highlighter: function (item) {
            return item.replace(new RegExp(\'(\' + this.query + \')\', \'ig\'), function ($1, match) {
                return \'<strong>\' + match + \'</strong>\'
            })
        }

高亮处理,replace的用法,大家可以学习一下。

最后是show出来

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

/*
          * 显示
          * */
        , show: function () {
            var pos = $.extend({}, this.$element.offset(), {
                height: this.$element[0].offsetHeight
            })

            this.$menu.css({
                top: pos.top + pos.height
                , left: pos.left
            })

            this.$menu.show()
            this.shown = true
            return this
        }

bootstrap插件学习-bootstrap.typehead.js - HackerVirus

调用jQuery的show方法显示。

这个插件中也有不少写的很蛋疼的方法,不过稍作修改就可以完成了,其实关于搜索这块的效率,我没有提及。每个人思路不一样,实现也不一样。希望园友们也能思考一下,能给出更加高效的查询办法。

内容不多,时间刚好,以上是我的一点读码体会,如有错误,请指出,大家共通学习。

posted on 2013-08-29 10:17 HackerVirus 阅读(220) 评论(0) 编辑收藏举报