bootstrap下拉菜单
2016-07-15 10:45
417 查看
.dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 1000; display: none; float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; font-size: 14px; text-align: left; list-style: none; background-color: #fff; -webkit-background-clip: padding-box; background-clip: padding-box; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, .15); border-radius: 4px; -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); box-shadow: 0 6px 12px rgba(0, 0, 0, .175); }
对于一个下拉菜单来说,最重要的就是下拉区域的定位问题,bootstrap通过top:100%这个属性设置,轻松地完成下拉菜单的定位问题,不需要任何的js计算操作。 细心的话就会发现,只要控制下拉元素的display就可以简单地实现下拉的效果,不过bs为了兼容更多的场景,加入动画的效果,js的处理也并不简单。下面分析理解dropdown.js的源码。
$(document) .on('click.bs.dropdown.data-api', clearMenus)//目的是为了在展开下拉菜单后,再次点击页面任何区域都能隐藏之前已经展开的菜单,考虑的是一个页面中存在多个下拉菜单的情况,但是也有一个问题,就是点击展开的下拉菜单里面的菜单项,同样会隐藏菜单,这在不需要隐藏菜单的场景中将会是一个问题 .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })//假如下拉菜单里有表单元素时,通过冒泡阻止菜单的隐藏,就是阻止第一行代码里的监听器响应点击事件 .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)//自动注册dropdown组件 .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)//在toggle元素获取焦点后,允许按向下箭头展开菜单 .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)//允许在展开菜单后,可以通过向上向下箭头,切换菜单项 .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)//允许在展开菜单后,可以通过向上向下箭头,切换菜单项
关于对click.bs.drop.data-api的理解
①首先,Jquery的API
events,[selector],[data],fnV1.7 events:一个或多个用空格分隔的事件类型和可选的命名空间,如"click"或"keydown.myPlugin"
关于参数[
selector],你可以简单地理解为:如果该参数等于
null或被省略,则为当前匹配元素绑定事件;否则就是为当前匹配元素的后代元素中符合
selector选择器的元素绑定事件。
②命名空间的理解
var backdrop = '.dropdown-backdrop' var toggle = '[data-toggle="dropdown"]' var Dropdown = function (element) { $(element).on('click.bs.dropdown', this.toggle) } Dropdown.VERSION = '3.3.4' function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.dropdown') if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.dropdown $.fn.dropdown = Plugin $.fn.dropdown.Constructor = Dropdown // DROPDOWN NO CONFLICT // ==================== $.fn.dropdown.noConflict = function () { $.fn.dropdown = old return this }
Dropdown.prototype.toggle = function (e) { var $this = $(this) if ($this.is('.disabled, :disabled')) return var $parent = getParent($this)//getParent方法用来获取父元素,不过这个父元素很有可能不是dom结构上的父节点,而是通过data-target或者href指定的某个dom元素,所以才有专门的一个方法来写 var isActive = $parent.hasClass('open')//父元素有open类,则说明菜单当前是已经展开的 clearMenus()//先清空已经展开的所有菜单 //只有在菜单未展开的时候才进行以下逻辑处理 if (!isActive) { if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { // if mobile we use a backdrop because click events don't delegate $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus) } //以上代码的目的是为了在移动端里展开菜单后,点击页面任何区域都能隐藏菜单,因为bs的事件都是通过代理注册的监听器,而click事件在移动端里如果是通过代理注册的,不会执行相应的监听器,所以bs才用了backdrop这样的一个元素,替代实现隐藏菜单的功能。 var relatedTarget = { relatedTarget: this } $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this .trigger('focus') .attr('aria-expanded', 'true') $parent .toggleClass('open') .trigger('shown.bs.dropdown', relatedTarget) } return false } Dropdown.prototype.keydown = function (e) { if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return //如果按键不是向上向下箭头,空格和ESC键,或者按键是为了在文本控件里输入字符,就不做以下处理 //注意按下回车键,会触发click事件!!! var $this = $(this) e.preventDefault() e.stopPropagation() if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') if ((!isActive && e.which != 27) || (isActive && e.which == 27)) { //实现的就是按向上向下和空格键展开菜单,再按esc键隐藏菜单。。。 if (e.which == 27) $parent.find(toggle).trigger('focus')//这里需要用find(toggle)的原因是因为,$this有可能是[role=menu]的dom元素而不是data-toggle元素 return $this.trigger('click') } var desc = ' li:not(.disabled):visible a' var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc) if (!$items.length) return var index = $items.index(e.target) if (e.which == 38 && index > 0) index-- // 按向上键,index-- if (e.which == 40 && index < $items.length - 1) index++ // 按向下键,index++ if (!~index) index = 0 //~index的作用:~3 = -4,~4=-5,~5=-6,~0=-1,~-1=0,~-2=1,~-3=2,其实没必要搞这种写法,尼玛。。。 $items.eq(index).trigger('focus') } function clearMenus(e) { if (e && e.which === 3) return $(backdrop).remove() $(toggle).each(function () { var $this = $(this) var $parent = getParent($this) var relatedTarget = { relatedTarget: this } if (!$parent.hasClass('open')) return $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this.attr('aria-expanded', 'false') $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget) }) } function getParent($this) { var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = selector && $(selector) return $parent && $parent.length ? $parent : $this.parent() }
相关文章推荐
- Bootstrap 折叠(Collapse)插件
- BootStrap之表格和按钮
- bootstrap .col-md-6 文字居中问题处理
- BootstrapTable+KnockoutJS
- JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查
- Bootstrap 最常用的JS插件系列总结(图片轮播、标签切换等)
- 基于jquery,bootstrap数据验证插件bootstrapValidator 教程
- 两周“学会”bootstrap搭建一个移动站点
- BootStrap 模态框禁用空白处点击关闭问题
- Bootstrap模态对话框
- Bootstrapvalidator插件feedbackIcons的作用
- bootstrap 只显示选择年份的 十年视图日期控件实现
- Bootstrap Dialog 使用
- bootstrap 模态窗口实例
- BootstrapTest_3(基础排板样式-2)
- 【转】让Bootstrap 3兼容IE8浏览器
- bootstrap教程(一)--bootstrap简介
- bootstrap教程(一)--bootstrap简介
- 关于bootstrap插件datepicker
- Bootstrap JS插件系列总结