Bootstrap 源代码分析(未完待续)

Bootstrap 是最受欢迎的 HTML、CSS 和 JS 框架,用于开发响应式布局、移动设备优先的 WEB 项目。—— Bootstrap 中文文档

Bootstrap 因为支持响应式布局、移动设备优先和易用易学等特点,使得它成为最受欢迎的前端开发框架。

Bootstrap 的响应式设计、组件开发和 JavaScript 插件开发和 预处理脚本的开发方法,也是值得学习的。

源代码

源代码下载和编译

推荐到 GitHub 下载最新、最全的 Bootstrap 源代码

GitHub 是 Bootstrap 源代码托管仓库,不仅包含源代码,还包含 Bootstrap 使用文档的源文件。因此,可以在没有网络的情况下,可以通过 编译运行文档源代码,在本地机器上浏览文档。

源代码目录

bootstrap 源代码目录包含:
•文档部署代码子目录 _gh_pages/
 •文档源代码子目录 docs/
 •bootstrap 部署代码子目录 dist/
 •bootstrap 脚本子目录 js/
 •bootstrap 样式子目录 less/
 •bootstrap 字体子目录 fonts/
 •grunt 构建工具脚本子目录 grunt/
 •包管理器 nuget 子目录 nuget/
 •许多配置文件

切入点

Bootstrap 框架的源代码很复杂,要从作者开发框架的角度分析,无疑是很困难的。可以对问题进行简单化,不关注框架是怎么构建或部署的,只关注框架的工作原理,即 HTML、CSS/LESS 和 JS 部分。

通过 分治 的思想,把复杂的问题分解成许多简单的问题进行解决。当所有小问题都解决了,复杂的问题也就迎刃而解了。

把整个 Bootstrap框架分治成一个个组件,以组件为切入点,理解其工作原理,然后逐步分析整个框架。

组件分析

下拉菜单 dropdown

HTML代码

<!-- 组件:下拉菜单 -->
<div class="dropdown">
 <!-- 触发按钮 -->
 <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">
 Dropdown
 <span class="caret"></span>
 </button>
 <!-- 下拉菜单 -->
 <ul class="dropdown-menu">
 <li><a href="#">Action</a></li>
 <li><a href="#">Another action</a></li>
 <li><a href="#">Something else here</a></li>
 </ul>
</div>

注意: 代码中去除了源代码中的可访问属性aria-*,便于分析。实际应用中不可省略。有关按钮样式这里也不展开进行分析

CSS代码

// Dropdown arrow/caret
.caret {
 display: inline-block;
 width: 0;
 height: 0;
 margin-left: 2px;
 vertical-align: middle;
 border-top: @caret-width-base dashed;
 border-top: @caret-width-base solid ~"\9"; // IE8
 border-right: @caret-width-base solid transparent;
 border-left: @caret-width-base solid transparent;
}

// The dropdown wrapper (div)
.dropup,
.dropdown {
 position: relative; // 父元素相对定位
}

// Prevent the focus on the dropdown toggle when closing dropdowns
.dropdown-toggle:focus {
 outline: 0;
}

// The dropdown menu (ul)
.dropdown-menu {
 position: absolute; //子元素绝对定位
 top: 100%; // 下拉菜单紧贴父元素下边沿
 left: 0;
 z-index: @zindex-dropdown;
 display: none; //默认隐藏,当触发按钮显示(display:block)
 float: left;
 min-width: 160px;
 padding: 5px 0;
 margin: 2px 0 0; // override default ul
 list-style: none;
 font-size: @font-size-base;
 text-align: left;
 background-color: @dropdown-bg;
 border: 1px solid @dropdown-fallback-border; // IE8 fallback
 border: 1px solid @dropdown-border;
 border-radius: @border-radius-base;
 .box-shadow(0 6px 12px rgba(0,0,0,.175));
 background-clip: padding-box;

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

 // 高度为1px的水平分隔线
 .divider {
 .nav-divider(@dropdown-divider-bg);
 }

 // Links within the dropdown menu
 > li > a {
 display: block;
 padding: 3px 20px;
 clear: both;
 font-weight: normal;
 line-height: @line-height-base;
 color: @dropdown-link-color;
 white-space: nowrap; // 防止链接换行
 }
}

// Hover/Focus state
.dropdown-menu > li > a {
 &:hover,
 &:focus {
 text-decoration: none;
 color: @dropdown-link-hover-color;
 background-color: @dropdown-link-hover-bg;
 }
}

// Active state
.dropdown-menu > .active > a {
 &,
 &:hover,
 &:focus {
 color: @dropdown-link-active-color;
 text-decoration: none;
 outline: 0;
 background-color: @dropdown-link-active-bg;
 }
}

// 显示下拉菜单
.open {
 > .dropdown-menu {
 display: block; // 显示
 }

 // Remove the outline when :focus is triggered
 > a {
 outline: 0;
 }
}

// Menu positioning
.dropdown-menu-right {
 left: auto; // Reset the default from `.dropdown-menu`
 right: 0;
}
// `.pull-right` nav component.
.dropdown-menu-left {
 left: 0;
 right: auto;
}

// Dropdown section headers
.dropdown-header {
 display: block;
 padding: 3px 20px;
 font-size: @font-size-small;
 line-height: @line-height-base;
 color: @dropdown-header-color;
 white-space: nowrap; // as with > li > a
}

// 非下拉菜单区域
.dropdown-backdrop {
 position: fixed;
 left: 0;
 right: 0;
 bottom: 0;
 top: 0;
 z-index: (@zindex-dropdown - 10); //确保点击下拉菜单时,不会关闭下拉菜单
}

// 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: @caret-width-base dashed;
 border-bottom: @caret-width-base solid ~"\9"; // IE8
 content: "";
 }
 // Different positioning for bottom up menu
 .dropdown-menu {
 top: auto;
 bottom: 100%;
 margin-bottom: 2px;
 }
}

// Component alignment
//
// Reiterate per navbar.less and the modified component alignment there.

@media (min-width: @grid-float-breakpoint) {
 .navbar-right {
 .dropdown-menu {
  .dropdown-menu-right();
 }
 // Necessary for overrides of the default right aligned menu.
 // Will remove come v4 in all likelihood.
 .dropdown-menu-left {
  .dropdown-menu-left();
 }
 }
}

该下拉菜单组件的行为是:当触发按钮被点击,在其下方显示下拉菜单,点击非下拉菜单区域时,隐藏下拉菜单。

实现原理:
 1.开始时只显示触发按钮,.dropdown包装默认下拉菜单关闭,.dropdown-menu默认隐藏 display:none

2.当触发按钮被点击,.dropdown后面添加类.open。在.open中 .dropdown-menu的display值是block。所以添加/删除.open类表示下拉菜单的显示/隐藏。

3.点击非下拉菜单区域时,.dropdown删除类.open,即隐藏下拉菜单。非下拉菜单区域的实现的原理是,固定定位,平铺,z-index比下拉菜单小,这样确保点击下拉菜单时,不会隐藏下拉菜单。

JavaScript代码

/* ========================================================================
 * Bootstrap: dropdown.js v3.3.6
 * http://getbootstrap.com/javascript/#dropdowns
 * ========================================================================
 * Copyright 2011-2016 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */

+function ($) {
 'use strict';

 // DROPDOWN CLASS DEFINITION
 // =========================

 var backdrop = '.dropdown-backdrop'
 var toggle = '[data-toggle="dropdown"]'
 var Dropdown = function (element) {
 $(element).on('click.bs.dropdown', this.toggle)
 }

 Dropdown.VERSION = '3.3.6'

 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()
 }

 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

  if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return

  $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))

  if (e.isDefaultPrevented()) return

  $this.attr('aria-expanded', 'false')
  $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))
 })
 }

 Dropdown.prototype.toggle = function (e) {
 var $this = $(this)

 if ($this.is('.disabled, :disabled')) return

 var $parent = getParent($this)
 var isActive = $parent.hasClass('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
  $(document.createElement('div'))
   .addClass('dropdown-backdrop')
   .insertAfter($(this))
   .on('click', clearMenus)
  }

  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($.Event('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

 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) {
  if (e.which == 27) $parent.find(toggle).trigger('focus')
  return $this.trigger('click')
 }

 var desc = ' li:not(.disabled):visible a'
 var $items = $parent.find('.dropdown-menu' + desc)

 if (!$items.length) return

 var index = $items.index(e.target)

 if (e.which == 38 && index > 0)     index--   // up
 if (e.which == 40 && index < $items.length - 1) index++   // down
 if (!~index)         index = 0

 $items.eq(index).trigger('focus')
 }

 // DROPDOWN PLUGIN DEFINITION
 // ==========================

 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
 }

 // APPLY TO STANDARD DROPDOWN ELEMENTS
 // ===================================

 $(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)
 .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
 .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)

}(jQuery);

Javascript代码结构可分为三个部分:
 1.类定义 1-125行
 2.插件定义 126-144行
 3.解决冲突 148-153行
 4.应用到标准的下拉菜单元素 155-166行

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Bootstrap每天必学之面板

    1.面板 面板(Panels)是Bootstrap框架新增的一个组件,其主要作用就是用来处理一些其他组件无法完成的功能.同样在不同的版本中具有不同的源码: ☑ Less版本:对应的源码文件是 panels.less ☑ Sass版本:对应的源码文件是 _panels.scss ☑ 编译后的Bootstrap:对应bootstrap.css文件第4995行-第5302行 2.面板–基础面板 基础面板非常简单,就是一个div容器运用了"panel"样式,产生一个具有边框的文本显示块.由于&

  • Bootstrap每天必学之工具提示(Tooltip)插件

    当您想要描述一个链接的时候,工具提示(Tooltip)就显得非常有用.工具提示(Tooltip)插件是受 Jason Frame 写的 jQuery.tipsy 的启发.工具提示(Tooltip)插件做了很多改进,例如不需要依赖图像,而是改用 CSS 实现动画效果,用 data 属性存储标题信息. 如果您想要单独引用该插件的功能,那么您需要引用 tooltip.js.或者,正如Bootstrap 插件概览 一章中所提到,您可以引用 bootstrap.js 或压缩版的 bootstrap.min

  • Bootstrap每天必学之按钮(Button)插件

    按钮(Button)在 Bootstrap 按钮 一章中介绍过.通过按钮(Button)插件,您可以添加进一些交互,比如控制按钮状态,或者为其他组件(如工具栏)创建按钮组. 如果您想要单独引用该插件的功能,那么您需要引用 button.js.或者,正如 Bootstrap 插件概览 一章中所提到,您可以引用 bootstrap.js 或压缩版的 bootstrap.min.js. 一.加载状态 如需向按钮添加加载状态,只需要简单地向 button 元素添加 data-loading-text="

  • Bootstrap每天必学之日期控制

    一个成熟的框架,日期控制是少不了的,在网上也有很多日期控制可以选择,而主框架用了bootstrap,日期控制也当前要用它自己的, 控件地址:http://www.bootcss.com/p/bootstrap-datetimepicker/index.htm GitHub上开源地址:https://github.com/smalot/bootstrap-datetimepicker 在使用datetimepicker之前,先要经过以下几个步骤 1.引用JS脚本库 <script src="

  • Bootstrap每天必学之警告框插件

    警告框(Alert)消息大多是用来想终端用户显示诸如警告或确认消息的信息.使用警告框(Alert)插件,您可以向所有的警告框消息添加可取消(dismiss)功能. 如果您想要单独引用该插件的功能,那么您需要引用 alert.js.或者,正如 Bootstrap 插件概览 一章中所提到,您可以引用 bootstrap.js 或压缩版的 bootstrap.min.js. 一.用法 您可以有以下两种方式启用警告框的可取消(dismissal)功能: 1.通过 data 属性:通过数据 API(Dat

  • 浅谈bootstrap源码分析之scrollspy(滚动侦听)

    源码文件: Scrollspy.js 实现功能 1.当滚动区域内设置的hashkey距离顶点到有效位置时,就关联设置其导航上的指定项 2.导航必须是 .nav > li > a 结构,并且a上href或data-target要绑定hashkey 3.菜单上必须有.nav样式 4.滚动区域的data-target与导航父级Id(一定是父级)要一致 <div id="selector" class="navbar navbar-default">

  • Bootstrap每天必学之模态框(Modal)插件

    本节课我们主要学习一下 Bootstrap 中的模态框插件, 这是一款交互式网站非常常见的弹窗功能插件. 更多关于Bootstrap模态框插件内容请点击专题<Bootstrap Modal使用教程>进行学习,希望大家喜欢,下面继续 一.基本使用 使用模态框的弹窗组件需要三层 div 容器元素,分别为 modal(模态声明层). dialog(窗口声明层).content(内容层).在内容层里面,还有三层,分别为 header(头部).body(主体).footer(注脚). //基本实例 &l

  • 基于Bootstrap实现Material Design风格表单插件 附源码下载

    Jquery Material Form Plugin是一款基于Bootstrap的Material Design风格的jQuery表单插件.该表单通过自定义样式和jQuery来将Bootstrap的表单修改为扁平风格的表单,并带有浮动标签特效. 在线预览         源码下载 使用方法 使用该Material Design风格表单需要在页面中引入jquery,bootstrap相关文件和materialFormStyles.css.materialForm.js文件. <link rel=

  • 浅谈bootstrap源码分析之tab(选项卡)

    实现tab选项卡的应用,此插件相对比较简单 源码文件: tab.js 实现原理 1.单击一个元素时,首先将原来高亮的元素取消 2.然后给被单击元素进行高亮 3.如果单击元素是下拉框中某个选项,则选中本身,还要选中下拉框 5.如果定义了动画,先执行动画,然后回调 源码分析: 1.Show方法,是在单击一个元素的时候触发,会触发如下四个事件 1.1.Hiden.bs.tab:隐藏上一个元素 1.2.Show.bs.tab:显示当前元素 1.3.Hideen.bs.tab:隐藏上一个元素完成 1.4.

  • Bootstrap表单布局样式源代码

    Bootstrap提供了三种表单布局:垂直表单,内联表单和水平表单 创建垂直或基本表单: •·向父 <form> 元素添加 role="form". •·把标签和控件放在一个带有 class .form-group 的 <div> 中.这是获取最佳间距所必需的. •·向所有的文本元素 <input>.<textarea> 和 <select> 添加 class .form-control 内联表单: 内联表单中所有元素都向左对

随机推荐