现在的位置: 首页 > 综合 > 正文

bootstrap源码学习与示例:bootstrap-dropdown

2013年01月12日 ⁄ 综合 ⁄ 共 8589字 ⁄ 字号 评论关闭

bootstrap-dropdown组件是个烂东西,我读后的整体感觉。

一个下拉开菜单的设计:

                  <ul class="nav pull-right">
                        <li id="fat-menu" class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown">下拉 <b class="caret"></b></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">动作</a></li>
                                <li><a href="#">另一个动作</a></li>
                                <li><a href="#">其他</a></li>
                                <li class="divider"></li>
                                <li><a href="#">另一个链接</a></li>
                            </ul>
                        </li>
                    </ul>

首先下拉开菜单其中就是当中第二层的UL元素,类名为dropdown-menu。下拉菜单通常处于隐藏状态。我们可以在它的父元素上加个open类名,让它显示出来。它的父元素通常带个dropdown类名,其实是提供了一个相对定位的包含块。当然除了dropdown外,你还可以选择用dropup。dropdown是让下拉菜单向下显示,dropup是向上展示。要打开下拉菜单,通常我们要点击某处让它显示出来。我们称之为触发器。这个触发器带一个data-toggle=dropdown的自定义属性。通过前面的学习,你们应该隐约察觉到,data-toggle在bootstrap有特殊意义,是让目标对象表现某一类控件,什么data-toggle=button,data-toggle=buttons-checked,data-toggle=radio……不过它还有专门的类名dropdown-toggle。触发器可以通过data-target自定义属性指定目标下拉框打开,也可以通过href属性打开。不过bootstrap的下拉框有严重的排它性,一个页面只能打开一个下拉框,也无法通过套嵌组建多级下拉框。

下面是它的源码,我们发现它是支持键盘切换的,但写得很烂,乃至多数情况下失败。

!function ($) {
    "use strict"; // jshint ;_;
    /* DROPDOWN CLASS DEFINITION
  * ========================= */
    var toggle = '[data-toggle=dropdown]'
    , Dropdown = function (element) {
        var $el = $(element).on('click.dropdown.data-api', this.toggle)
        $('html').on('click.dropdown.data-api', function () {
            $el.parent().removeClass('open')
        })
    }

    Dropdown.prototype = {

        constructor: Dropdown  , 
        toggle: function (e) {
            var $this = $(this)
            , $parent
            , isActive

            if ($this.is('.disabled, :disabled')) return
            //分别通过以下三个途径:
            //1 data-target自定义属性 
            //2 href的属性中的hash(也是ID选择器), 
            //3这个按钮的直属父节点
            $parent = getParent($this)

            isActive = $parent.hasClass('open')
            //每次页面只能有一个菜单被打开,这是个失败的设计
            clearMenus()
            //如果没有打开,则打开它
            if (!isActive) {
                $parent.toggleClass('open')
            }
            //让当前菜单项获得焦点
            $this.focus()

            return false
        } , 
        keydown: function (e) {
            var $this
            , $items
            , $parent
            , isActive
            , index
            //如果不是上下方向键或回车键,返回
            if (!/(38|40|27)/.test(e.keyCode)) return

            $this = $(this)

            e.preventDefault()
            e.stopPropagation()
            //如果标识为禁止状态
            if ($this.is('.disabled, :disabled')) return
            //取得控件的容器
            $parent = getParent($this)
            //如果处于激活状态
            isActive = $parent.hasClass('open')
            //如果没有激活或激话了+回车,就触发其点击事件
            if (!isActive || (isActive && e.keyCode == 27)) return $this.click()
            //如果是菜单项(不能是作为分隔线的LI)下的A元素
            $items = $('[role=menu] li:not(.divider):visible a', $parent)

            if (!$items.length) return
            //那么我们取得当前获得焦点的元素作为基准,通过它上下移动
            index = $items.index($items.filter(':focus'))

            if (e.keyCode == 38 && index > 0) index--                                        // up
            if (e.keyCode == 40 && index 

以下是它对应的less。

//
// Dropdown menus
// --------------------------------------------------


// Use the .menu class on any 
  • element within the topbar or ul.tabs and you'll get some superfancy dropdowns
    .dropup,
    .dropdown {
    position: relative;
    }
    .dropdown-toggle {
    // The caret makes the toggle a bit too tall in IE7
    *margin-bottom: -3px;
    }
    .dropdown-toggle:active,
    .open .dropdown-toggle {
    outline: 0;
    }

    // Dropdown arrow/caret
    // --------------------
    .caret {
    display: inline-block;
    width: 0;
    height: 0;
    vertical-align: top;
    border-top: 4px solid @black;
    border-right: 4px solid transparent;
    border-left: 4px solid transparent;
    content: "";
    }

    // Place the caret
    .dropdown .caret {
    margin-top: 8px;
    margin-left: 2px;
    }

    // The dropdown menu (ul)
    // ----------------------
    .dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: @zindexDropdown;
    display: none; // none by default, but block on "open" of the menu
    float: left;
    min-width: 160px;
    padding: 5px 0;
    margin: 2px 0 0; // override default ul
    list-style: none;
    background-color: @dropdownBackground;
    border: 1px solid #ccc; // Fallback for IE7-8
    border: 1px solid @dropdownBorder;
    *border-right-width: 2px;
    *border-bottom-width: 2px;
    .border-radius(6px);
    .box-shadow(0 5px 10px rgba(0,0,0,.2));
    -webkit-background-clip: padding-box;
    -moz-background-clip: padding;
    background-clip: padding-box;

    // Aligns the dropdown menu to right
    &.pull-right {
    right: 0;
    left: auto;
    }

    // Dividers (basically an hr) within the dropdown
    .divider {
    .nav-divider(@dropdownDividerTop, @dropdownDividerBottom);
    }

    // Links within the dropdown menu
    li > a {
    display: block;
    padding: 3px 20px;
    clear: both;
    font-weight: normal;
    line-height: @baseLineHeight;
    color: @dropdownLinkColor;
    white-space: nowrap;
    }
    }

    // Hover state
    // -----------
    .dropdown-menu li > a:hover,
    .dropdown-menu li > a:focus,
    .dropdown-submenu:hover > a {
    text-decoration: none;
    color: @dropdownLinkColorHover;
    #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%));
    }

    // Active state
    // ------------
    .dropdown-menu .active > a,
    .dropdown-menu .active > a:hover {
    color: @dropdownLinkColorActive;
    text-decoration: none;
    outline: 0;
    #gradient > .vertical(@dropdownLinkBackgroundActive, darken(@dropdownLinkBackgroundActive, 5%));
    }

    // Disabled state
    // --------------
    // Gray out text and ensure the hover state remains gray
    .dropdown-menu .disabled > a,
    .dropdown-menu .disabled > a:hover {
    color: @grayLight;
    }
    // Nuke hover effects
    .dropdown-menu .disabled > a:hover {
    text-decoration: none;
    background-color: transparent;
    background-image: none; // Remove CSS gradient
    .reset-filter();
    cursor: default;
    }

    // Open state for the dropdown
    // ---------------------------
    .open {
    // IE7's z-index only goes to the nearest positioned ancestor, which would
    // make the menu appear below buttons that appeared later on the page
    *z-index: @zindexDropdown;

    & > .dropdown-menu {
    display: block;
    }
    }

    // Right aligned dropdowns
    // ---------------------------
    .pull-right > .dropdown-menu {
    right: 0;
    left: auto;
    }

    // Allow for dropdowns to go bottom up (aka, dropup-menu)
    // ------------------------------------------------------
    // Just add .dropup after the standard .dropdown class and you're set, bro.
    // TODO: abstract this so that the navbar fixed styles are not placed here?
    .dropup,
    .navbar-fixed-bottom .dropdown {
    // Reverse the caret
    .caret {
    border-top: 0;
    border-bottom: 4px solid @black;
    content: "";
    }
    // Different positioning for bottom up menu
    .dropdown-menu {
    top: auto;
    bottom: 100%;
    margin-bottom: 1px;
    }
    }

    // Sub menus
    // ---------------------------
    .dropdown-submenu {
    position: relative;
    }
    // Default dropdowns
    .dropdown-submenu > .dropdown-menu {
    top: 0;
    left: 100%;
    margin-top: -6px;
    margin-left: -1px;
    .border-radius(0 6px 6px 6px);
    }
    .dropdown-submenu:hover > .dropdown-menu {
    display: block;
    }

    // Dropups
    .dropup .dropdown-submenu > .dropdown-menu {
    top: auto;
    bottom: 0;
    margin-top: 0;
    margin-bottom: -2px;
    .border-radius(5px 5px 5px 0);
    }

    // Caret to indicate there is a submenu
    .dropdown-submenu > a:after {
    display: block;
    content: " ";
    float: right;
    width: 0;
    height: 0;
    border-color: transparent;
    border-style: solid;
    border-width: 5px 0 5px 5px;
    border-left-color: darken(@dropdownBackground, 20%);
    margin-top: 5px;
    margin-right: -10px;
    }
    .dropdown-submenu:hover > a:after {
    border-left-color: @dropdownLinkColorHover;
    }

    // Left aligned submenus
    .dropdown-submenu.pull-left {
    // Undo the float
    // Yes, this is awkward since .pull-left adds a float, but it sticks to our conventions elsewhere.
    float: none;

    // Positioning the submenu
    > .dropdown-menu {
    left: -100%;
    margin-left: 10px;
    .border-radius(6px 0 6px 6px);
    }
    }

    // Tweak nav headers
    // -----------------
    // Increase padding from 15px to 20px on sides
    .dropdown .dropdown-menu .nav-header {
    padding-left: 20px;
    padding-right: 20px;
    }

    // Typeahead
    // ---------
    .typeahead {
    z-index: 1051;
    margin-top: 2px; // give it some space to breathe
    .border-radius(@baseBorderRadius);
    }

  • 抱歉!评论已关闭.