Bootstrap简单认识之Dropdown组件

一、简介

  • 此组件可以不指定 data-target 属性,不指定的话,必须按.dropdown-toggle 按钮和 dropdown-menu 列表放在同一个父元素下
  • 注意,如果想要指定 data-target 属性,那么不是指向 .drop-menu 元素,而是指向包含 .dropdown-toggle 按钮和 dropdown-menu 列表的父元素 .dropdown

二、样式

  1. 隐藏与显示也是通过父类的 show 类,来控制子元素的 display 属性,在 noneblock 之间切换

    .show {
       // Show the menu
       > .dropdown-menu {
         display: block;
       }
    
      // Remove the outline when :focus is triggered
      > a {
        outline: 0;
      }
    }
  2. 发现.show类是可以让a标签的outline为0的,但是是show的直接子元素a才有此属性,而下面提到a标签必须被放在li标签里才可以被上下键选来选去,这样一来上下键选的时候a标签依旧有outline
  3. 按钮里的倒三角,只要指定了 dropdown-toggle 就可以有了,因为此类指定了after伪类,使用css3画了个三角:

    .dropdown-toggle {
    &::after {
      display: inline-block;
      width: 0;
      height: 0;
      margin-left: $caret-width;
      vertical-align: middle;
      content: "";
      border-top: $caret-width solid;
      border-right: $caret-width solid transparent;
      border-left: $caret-width solid transparent;
    }
    }
  4. 上面提及的是下拉框向下的时候,bootstrap允许下拉框朝上,成为上拉框,只要为父元素指定的是 dropup 类而不是 dropdown类就行
  5. 可以在选项之间使用分隔符,只要在需要的地方添加一个带 .dropdown-divider 类的div元素就行
  6. 下拉菜单默认是靠左的,如果需要让菜单靠右,那么可以在 .dropdiwn-menu 元素上添加 .dropdown-menu-right 类,与此同时,为 .dropdown 元素(当然,父元素不一定要有这个类,.dropdown的作用只是声明 position:relative),手动添加额外css属性:display:inline-block

三、脚本(可选)

此组件比较简单,类似Alert组件一样简单。
主要就是dropbox的弹出与收回。
以下是为此(类)组件绑定的事件:

$(document)
  // 为 $('[data-toggle="dropdown"], [role="menu"], [role="listbox"]') 元素绑定键盘的 keydown 事件
  .on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE,  Dropdown._dataApiKeydownHandler)
  .on(Event.KEYDOWN_DATA_API, Selector.ROLE_MENU,    Dropdown._dataApiKeydownHandler)
  .on(Event.KEYDOWN_DATA_API, Selector.ROLE_LISTBOX, Dropdown._dataApiKeydownHandler)
  // 点击document其他地方会导致收回下拉菜单
  .on(`${Event.CLICK_DATA_API} ${Event.FOCUSIN_DATA_API}`, Dropdown._clearMenus)
  // 点击按钮触发 toggle 事件
  .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, Dropdown.prototype.toggle)
  // 未知原因(怕是要真正用到才理解了)
  .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => {
    e.stopPropagation()
  })

接下来就针对每个事件的回调进行简单分析:

  1. 下拉菜单收回 function _clearMenus

    // 将所有下拉菜单收回
    static _clearMenus(event) {
    const toggles = $.makeArray($(Selector.DATA_TOGGLE))
    for (let i = 0; i < toggles.length; i++) {
        const parent = ... // 获取data-target元素或是父元素
        // 只对展开的dropdown操作
        if (!$(parent).hasClass(ClassName.SHOW)) {
          continue
        }
        // 如果是点击 .dropdown 内的特定元素,则不再操作
        // 触发hide钩子
        // 移除parent的class:.show
        // 触发hidden钩子
    }
    }

    根据代码,可以知道为什么data-target 属性,不是指向 .drop-menu 元素,而是指向包含 .dropdown-toggle 按钮和 dropdown-menu 列表的父元素 .dropdown
    有几种情况不收回下拉框:

    • 本身就没展开
    • 点击 .dropdown 元素中的 input 或是 textarea 元素不收回下拉框
    • 其他元素若只触发 focusin 事件也不收回(一般是键盘触发)。鼠标的click事件往往先触发focusin事件,之后继续触发click事件,所以依旧导致下拉框收回
    • 用户在hide钩子中调用 preventDefault 函数
  2. 下拉框的弹出与收回 function toggle
    此函数是绑定在事件钩子上的,所以this多是指按钮元素

    toggle() {
    // 不管弹没弹开,先收起所有存在的展开的dropdown
    Dropdown._clearMenus()
    // 如果 .dropdown 元素有 show 类,那么不再继续
    // 下面就是弹开按钮菜单的过程
    // 
    if ('ontouchstart' in document.documentElement &&
         !$(parent).closest(Selector.NAVBAR_NAV).length) {
    
        // if mobile we use a backdrop because click events don't delegate
        const dropdown     = document.createElement('div')
        dropdown.className = ClassName.BACKDROP
        $(dropdown).insertBefore(this)
        $(dropdown).on('click', Dropdown._clearMenus)
      }
    
      // 触发show钩子
      // 触发shown钩子
    }

    关于上述代码中,在移动端会添加阴影层,我这里没有模拟。。。不过这与Modal的阴影层类似,所以也可以想象出打开后的情形

  3. 键盘控制事件 function

    • 可以使用tab键切换到dropdown按钮后,可以通过空格键(_dataApiKeydownHandler)或是ENTER键(toggle)打开(经过测试,发现button类型被focus后,按下ENTER键,会触发click事件!)
    • 方向键上或者下也可以打开,并且可以进行选项选择(这个需要场景触发,代码分析完会提及)
    • ESC键则可以收回下拉框
      代码分析如下:
      static _dataApiKeydownHandler(event) {
      // REGEXP_KEYDOWN 为TAB, ARROW_UP,ARROW_DOWN,ESC键的code
      if (!REGEXP_KEYDOWN.test(event.which) ||
         /input|textarea/i.test(event.target.tagName)) {
        return
      }
      
      // 键盘控制toggle
      if (!isActive && event.which !== ESCAPE_KEYCODE ||
         isActive && event.which === ESCAPE_KEYCODE) {
          // 打开状态下按ESC,除了执行toggle,还focus按钮
          if (event.which === ESCAPE_KEYCODE) {
            const toggle = $(parent).find(Selector.DATA_TOGGLE)[0]
            $(toggle).trigger('focus')
          }
      
          $(this).trigger('click')
          return
      }
      
        // 键盘控制选项
        const items = $(parent).find(Selector.VISIBLE_ITEMS).get()
        let index = items.indexOf(event.target)
        if (event.which === ARROW_UP_KEYCODE && index > 0) { // up
          index--
        }
        if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // down
          index++
        }
        if (index < 0) {
          index = 0
        }
        items[index].focus()
      }

    上述代码获取下拉菜单选项的时候,获取的方式为: $(parent).find([role="menu"] li:not(.disabled) a, [role="listbox"] li:not(.disabled) a).get(),可以发现只有当我们指定 role 属性的时候,这些子元素才会被发现!且需要每个a标签被li所包含,那么li自然是被放在ul标签中的,下面是可用的示例:

    <div class="dropdown " id="dropdown-example" >
     <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" data-target="#dropdown-example">
        DropDown Toggle
     button>
    
    <ul class="dropdown-menu" role='menu' >
        <li><a href="#" class="dropdown-item">Actiona>li>
        <li><a href="#" class="dropdown-item">Action Seconda>li>
        <li><a href="#" class="dropdown-item">Action Thirda>li>
    ul>
    div>

你可能感兴趣的:(前端)