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結構

<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" style="margin: 0 auto;"/>

先從初始化開始

/*
    * 初始化
    * */
    $(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())
        })
    })

為所有擁有data-provide='typehead'屬性的標簽綁定focus事件,並且阻止事件冒泡,進入jQuery的原型方法中

/*
    * 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]()
        })
    }

由於,$this.data('typehead')為空,我們執行實例化。

/*
    * 構造器
    * */
    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()
    }

這里注意一點是,控件中的data屬性與默認項中如果有重名屬性的話,data屬性優先級高,會覆蓋。進入listen方法

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))
        }

完成事件監聽的綁定工作,至此初始化工作完成。原型上擁有一堆API,讓我們一一嘗試。

先看input的keypress事件,對應的keypress方法

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()  //阻止冒泡

鍵盤上的按鈕對應響應的keyCode,preventDefault方法將阻止元素放生默認行為。如果按了向上的箭頭,進入prev方法

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')
        }

ul中li元素遍歷,上箭頭可以向上選擇li元素,如果到頂了,則返回最下面的li元素重新開始。被選中的li元素會擁有active類,一個選中樣式,其他li則沒有。

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')
        }

next的方法與prev類似,像API中還有類似的方法,大家依照demo,一調試就可以出來,比如mouseenter,click,blur,show,hide方法等。

對於具有聯想功能的插件,學習學習它的聯想功能如何實現比較重要。

好,我們從keyup開始

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()
        }

如果我們輸入一般字母,進入默認的lookup方法

 /*
          * 查詢匹配方法
          * */
        , 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() }

 這里代碼有個錯誤,就是這個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方法

/*
          * 生成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
        }

看一下highlighter函數

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

高亮處理,replace的用法,大家可以學習一下。

最后是show出來

/*
          * 顯示
          * */
        , 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
        }

調用jQuery的show方法顯示。

這個插件中也有不少寫的很蛋疼的方法,不過稍作修改就可以完成了,其實關於搜索這塊的效率,我沒有提及。每個人思路不一樣,實現也不一樣。希望園友們也能思考一下,能給出更加高效的查詢辦法。

內容不多,時間剛好,以上是我的一點讀碼體會,如有錯誤,請指出,大家共通學習。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM