bootstrap下拉菜單

 1 .dropdown-menu {
 2   position: absolute;
 3   top: 100%;
 4   left: 0;
 5   z-index: 1000;
 6   display: none;
 7   float: left;
 8   min-width: 160px;
 9   padding: 5px 0;
10   margin: 2px 0 0;
11   font-size: 14px;
12   text-align: left;
13   list-style: none;
14   background-color: #fff;
15   -webkit-background-clip: padding-box;
16   background-clip: padding-box;
17   border: 1px solid #ccc;
18   border: 1px solid rgba(0, 0, 0, .15);
19   border-radius: 4px;
20   -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
21   box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
22 }

對於一個下拉菜單來講,最重要的就是下拉區域的定位問題,bootstrap經過top:100%這個屬性設置,輕鬆地完成下拉菜單的定位問題,不須要任何的js計算操做。 細心的話就會發現,只要控制下拉元素的display就能夠簡單地實現下拉的效果,不過bs爲了兼容更多的場景,加入動畫的效果,js的處理也並不簡單。下面分析理解dropdown.js的源碼。jquery


 

1 $(document)
2     .on('click.bs.dropdown.data-api', clearMenus)//目的是爲了在展開下拉菜單後,再次點擊頁面任何區域都能隱藏以前已經展開的菜單,考慮的是一個頁面中存在多個下拉菜單的狀況,可是也有一個問題,就是點擊展開的下拉菜單裏面的菜單項,一樣會隱藏菜單,這在不須要隱藏菜單的場景中將會是一個問題
3     .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })//假以下拉菜單裏有表單元素時,經過冒泡阻止菜單的隱藏,就是阻止第一行代碼裏的監聽器響應點擊事件
4     .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)//自動註冊dropdown組件
5     .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)//在toggle元素獲取焦點後,容許按向下箭頭展開菜單
6     .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)//容許在展開菜單後,能夠經過向上向下箭頭,切換菜單項
7     .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)//容許在展開菜單後,能夠經過向上向下箭頭,切換菜單項

 

 關於對click.bs.drop.data-api的理解web

①首先,Jquery的APIbootstrap

1 events,[selector],[data],fnV1.7
2 
3 events:一個或多個用空格分隔的事件類型和可選的命名空間,如"click"或"keydown.myPlugin" 

關於參數[selector],你能夠簡單地理解爲:若是該參數等於null或被省略,則爲當前匹配元素綁定事件;不然就是爲當前匹配元素的後代元素中符合selector選擇器的元素綁定事件。api

命名空間的理解dom

 1 var backdrop = '.dropdown-backdrop'
 2   var toggle   = '[data-toggle="dropdown"]'
 3   var Dropdown = function (element) {
 4     $(element).on('click.bs.dropdown', this.toggle)
 5   }
 6 
 7   Dropdown.VERSION = '3.3.4'
 8 
 9   function Plugin(option) {
10     return this.each(function () {
11       var $this = $(this)
12       var data  = $this.data('bs.dropdown')
13 
14       if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
15       if (typeof option == 'string') data[option].call($this)
16     })
17   }
18 
19   var old = $.fn.dropdown
20 
21   $.fn.dropdown             = Plugin
22   $.fn.dropdown.Constructor = Dropdown
23 
24 
25   // DROPDOWN NO CONFLICT
26   // ====================
27 
28   $.fn.dropdown.noConflict = function () {
29     $.fn.dropdown = old
30     return this
31   }

 


  1     Dropdown.prototype.toggle = function (e) {
  2     var $this = $(this)
  3 
  4     if ($this.is('.disabled, :disabled')) return
  5 
  6     var $parent  = getParent($this)//getParent方法用來獲取父元素,不過這個父元素頗有可能不是dom結構上的父節點,而是經過data-target或者href指定的某個dom元素,因此纔有專門的一個方法來寫
  7     var isActive = $parent.hasClass('open')//父元素有open類,則說明菜單當前是已經展開的
  8 
  9     clearMenus()//先清空已經展開的全部菜單
 10 
 11     //只有在菜單未展開的時候才進行如下邏輯處理
 12     if (!isActive) {
 13       if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
 14         // if mobile we use a backdrop because click events don't delegate
 15         $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
 16       }
 17       //以上代碼的目的是爲了在移動端裏展開菜單後,點擊頁面任何區域都能隱藏菜單,由於bs的事件都是經過代理註冊的監聽器,而click事件在移動端裏若是是經過代理註冊的,不會執行相應的監聽器,因此bs才用了backdrop這樣的一個元素,替代實現隱藏菜單的功能。
 18 
 19       var relatedTarget = { relatedTarget: this }
 20       $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
 21 
 22       if (e.isDefaultPrevented()) return
 23 
 24       $this
 25         .trigger('focus')
 26         .attr('aria-expanded', 'true')
 27 
 28       $parent
 29         .toggleClass('open')
 30         .trigger('shown.bs.dropdown', relatedTarget)
 31     }
 32 
 33     return false
 34   }
 35 
 36   Dropdown.prototype.keydown = function (e) {
 37     if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
 38     //若是按鍵不是向上向下箭頭,空格和ESC鍵,或者按鍵是爲了在文本控件裏輸入字符,就不作如下處理
 39     //注意按下回車鍵,會觸發click事件!!!
 40 
 41     var $this = $(this)
 42 
 43     e.preventDefault()
 44     e.stopPropagation()
 45 
 46     if ($this.is('.disabled, :disabled')) return
 47 
 48     var $parent  = getParent($this)
 49     var isActive = $parent.hasClass('open')
 50 
 51     if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
 52       //實現的就是按向上向下和空格鍵展開菜單,再按esc鍵隱藏菜單。。。
 53       if (e.which == 27) $parent.find(toggle).trigger('focus')//這裏須要用find(toggle)的緣由是由於,$this有多是[role=menu]的dom元素而不是data-toggle元素
 54       return $this.trigger('click')
 55     }
 56 
 57     var desc = ' li:not(.disabled):visible a'
 58     var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
 59 
 60     if (!$items.length) return
 61 
 62     var index = $items.index(e.target)
 63 
 64     if (e.which == 38 && index > 0)                 index--                        // 按向上鍵,index--
 65     if (e.which == 40 && index < $items.length - 1) index++                        // 按向下鍵,index++
 66     if (!~index)                                      index = 0
 67     //~index的做用:~3 = -4,~4=-5,~5=-6,~0=-1,~-1=0,~-2=1,~-3=2,其實不必搞這種寫法,尼瑪。。。
 68 
 69     $items.eq(index).trigger('focus')
 70   }
 71   
 72   function clearMenus(e) {
 73     if (e && e.which === 3) return
 74     $(backdrop).remove()
 75     $(toggle).each(function () {
 76       var $this         = $(this)
 77       var $parent       = getParent($this)
 78       var relatedTarget = { relatedTarget: this }
 79 
 80       if (!$parent.hasClass('open')) return
 81 
 82       $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
 83 
 84       if (e.isDefaultPrevented()) return
 85 
 86       $this.attr('aria-expanded', 'false')
 87       $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
 88     })
 89   }
 90 
 91   function getParent($this) {
 92     var selector = $this.attr('data-target')
 93 
 94     if (!selector) {
 95       selector = $this.attr('href')
 96       selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
 97     }
 98 
 99     var $parent = selector && $(selector)
100 
101     return $parent && $parent.length ? $parent : $this.parent()
102   }
相關文章
相關標籤/搜索