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


Posted in Javascript onAugust 17, 2016

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行

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

Javascript 相关文章推荐
Track Image Loading效果代码分析
Aug 13 Javascript
JavaScript 加号(+)运算符号
Dec 06 Javascript
Javascript 实用小技巧
Apr 07 Javascript
js实现addClass,removeClass,hasClass的函数代码
Jul 13 Javascript
Jquery中的CheckBox、RadioButton、DropDownList的取值赋值实现代码
Oct 12 Javascript
jsp+javascript打造级连菜单的实例代码
Jun 14 Javascript
Tab切换组件(选项卡功能)实例代码
Nov 21 Javascript
一个JavaScript的求爱小特效
May 09 Javascript
逻辑表达式中与或非的用法详解
Jun 06 Javascript
request请求获取参数的实现方法(post和get两种方式)
Sep 27 Javascript
jquery popupDialog 使用 加载jsp页面的方法
Oct 25 Javascript
微信小程序使用wx.request请求服务器json数据并渲染到页面操作示例
Mar 30 Javascript
AngularJS HTML DOM详解及示例代码
Aug 17 #Javascript
AngularJS表格详解及示例代码
Aug 17 #Javascript
AngularJS过滤器详解及示例代码
Aug 16 #Javascript
AngularJS控制器详解及示例代码
Aug 16 #Javascript
AngularJS表达式讲解及示例代码
Aug 16 #Javascript
谈谈PHP中相对路径的问题与绝对路径的使用
Aug 16 #Javascript
jQuery多文件异步上传带进度条实例代码
Aug 16 #Javascript
You might like
php5.3 不支持 session_register() 此函数已启用的解决方法
2013/11/12 PHP
php通过array_shift()函数移除数组第一个元素的方法
2015/03/18 PHP
PHP大文件切割上传功能实例分析
2019/07/01 PHP
TNC vs BOOM BO3 第三场2.13
2021/03/10 DOTA
关于捕获用户何时点击window.onbeforeunload的取消事件
2011/03/06 Javascript
使用js对select动态添加和删除OPTION示例代码
2013/08/12 Javascript
jQuery如何实现点击页面获得当前点击元素的id或其他信息
2014/01/09 Javascript
jsonp跨域请求数据实现手机号码查询实例分析
2015/12/12 Javascript
详解jquery事件delegate()的使用方法
2016/01/25 Javascript
前端学习笔记style,currentStyle,getComputedStyle的用法与区别
2016/05/28 Javascript
基于Node.js的WebSocket通信实现
2017/03/11 Javascript
js下载文件并修改文件名
2017/05/08 Javascript
vue修改vue项目运行端口号的方法
2017/08/04 Javascript
在Vue中使用Compass的方法
2018/03/02 Javascript
Angular 如何使用第三方库的方法
2018/04/18 Javascript
探索JavaScript中私有成员的相关知识
2019/06/13 Javascript
javascript实现下拉菜单效果
2021/02/09 Javascript
[03:07]【DOTA2亚洲邀请赛】我们,梦开始的地方
2017/03/07 DOTA
Python 正则表达式(转义问题)
2014/12/15 Python
python实现爬取千万淘宝商品的方法
2015/06/30 Python
python实现web方式logview的方法
2015/08/10 Python
Python实现矩阵转置的方法分析
2017/11/24 Python
python 实现对文件夹内的文件排序编号
2018/04/12 Python
对numpy的array和python中自带的list之间相互转化详解
2018/04/13 Python
selenium+python 去除启动的黑色cmd窗口方法
2018/05/22 Python
Linux CentOS Python开发环境搭建教程
2018/11/28 Python
Python操作redis实例小结【String、Hash、List、Set等】
2019/05/16 Python
Pytorch在dataloader类中设置shuffle的随机数种子方式
2020/01/14 Python
CSS3制作漂亮的照片墙的实现代码
2016/06/08 HTML / CSS
HTML5 新表单类型示例代码
2018/03/20 HTML / CSS
导师推荐信范文
2014/05/09 职场文书
社区党建工作汇报材料
2014/08/14 职场文书
离婚协议书范本样本
2014/08/19 职场文书
银行竞聘上岗演讲稿
2014/09/12 职场文书
出差报告格式模板
2014/11/06 职场文书
给领导的感谢信范文
2015/01/23 职场文书