• 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的源码。


    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的理解

    ①首先,Jquery的API

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

    关于参数[selector],你可以简单地理解为:如果该参数等于null或被省略,则为当前匹配元素绑定事件;否则就是为当前匹配元素的后代元素中符合selector选择器的元素绑定事件。

    命名空间的理解

     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   }

  • 相关阅读:
    操作系统相关知识点
    const define static extern
    Openstack neutron学习
    TensorFlow_Faster_RCNN中demo.py的运行(CPU Only)
    研一前的暑假,深度学习初体验
    List.remove()的使用注意
    iOS开发之多线程(NSThread、NSOperation、GCD)
    PS 滤镜算法原理——碎片效果
    【翻译】ExtJS vs AngularJS
    【翻译】在Ext JS 5应用程序中如何使用路由
  • 原文地址:https://www.cnblogs.com/cndotabestdota/p/5672790.html
Copyright © 2020-2023  润新知