Bootstrap简单认识之Dropdown组件

本文详细介绍了Bootstrap下拉菜单组件的工作原理,包括其样式控制、脚本交互逻辑及键盘导航功能实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Dropdown(下拉框)组件

一、简介
  • 此组件可以不指定 data-target 属性,不指定的话,必须按.dropdown-toggle 按钮和 dropdown-menu 列表放在同一个父元素下
  • 注意,如果想要指定 data-target 属性,那么不是指向 .drop-menu 元素,而是指向包含 .dropdown-toggle 按钮和 dropdown-menu 列表的父元素 .dropdown
二、样式
  1. 隐藏与显示也是通过父类的 show 类,来控制子元素的 display 属性,在 none 和 block 之间切换

    .show {
       // Show the menu
       > .dropdown-menu {
         display: block;
       }
    
      // Remove the outline when :focus is triggered
      > a {
        outline: 0;
      }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  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;
    }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

  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钩子
    }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    根据代码,可以知道为什么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钩子
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    关于上述代码中,在移动端会添加阴影层,我这里没有模拟。。。不过这与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()
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34

    上述代码获取下拉菜单选项的时候,获取的方式为: $(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">Action</a></li>
        <li><a href="#" class="dropdown-item">Action Second</a></li>
        <li><a href="#" class="dropdown-item">Action Third</a></li>
    </ul>
    </div>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值