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 相关文章推荐
JavaScript对象模型-执行模型
Apr 28 Javascript
Ajax执行顺序流程及回调问题分析
Dec 10 Javascript
node.js中使用q.js实现api的promise化
Sep 17 Javascript
jQuery+AJAX实现网页无刷新上传
Feb 22 Javascript
node.js操作mongodb学习小结
Apr 25 Javascript
简易的JS计算器实现代码
Oct 18 Javascript
详解百度百科目录导航树小插件
Jan 08 Javascript
详解微信小程序 通过控制CSS实现view隐藏与显示
May 24 Javascript
SelectPage v2.4 发布新增纯下拉列表和关闭分页功能
Sep 07 Javascript
JavaScript遍历DOM元素的常见方式示例
Feb 16 Javascript
微信小程序错误this.setData报错及解决过程
Sep 18 Javascript
JS typeof fn === 'function' &amp;&amp; fn()详解
Aug 22 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
浅析PHP中Collection 类的设计
2013/06/21 PHP
在PHP中运行Linux命令并启动SSH服务的例子
2014/06/12 PHP
PHP中通过fopen()函数访问远程文件示例
2014/11/18 PHP
Linux中为php配置伪静态
2014/12/17 PHP
php实现字符串翻转的方法
2015/03/27 PHP
php关键字仅替换一次的实现函数
2015/10/29 PHP
thinkphp3.x中display方法及show方法的用法实例
2016/05/19 PHP
JQuery调webservice实现邮箱验证(检测是否可用)
2013/05/21 Javascript
Javascript学习笔记之函数篇(五) : 构造函数
2014/11/23 Javascript
jQuery插件实现多级联动菜单效果
2015/12/01 Javascript
AngularJS 入门教程之HTML DOM实例详解
2016/07/28 Javascript
深入浅出ES6新特性之函数默认参数和箭头函数
2016/08/01 Javascript
QRCode.js:基于JQuery的生成二维码JS库的使用
2017/06/23 jQuery
Node.js pipe实现源码解析
2017/08/12 Javascript
Angular HMR(热模块替换)功能实现方法
2018/04/04 Javascript
父组件中vuex方法更新state子组件不能及时更新并渲染的完美解决方法
2018/04/25 Javascript
vue填坑之webpack run build 静态资源找不到的解决方法
2018/09/03 Javascript
axios全局注册,设置token,以及全局设置url请求网段的方法
2018/09/25 Javascript
JSONObject与JSONArray使用方法解析
2020/09/28 Javascript
Python字符遍历的艺术
2008/09/06 Python
python发送邮件功能实现代码
2016/07/15 Python
Python实现的文本简单可逆加密算法示例
2017/05/18 Python
python使用epoll实现服务端的方法
2018/10/16 Python
python 中Arduino串口传输数据到电脑并保存至excel表格
2019/10/14 Python
详解python中docx库的安装过程
2019/11/08 Python
python词云库wordCloud使用方法详解(解决中文乱码)
2020/02/17 Python
Python 实现自动完成A4标签排版打印功能
2020/04/09 Python
python中的对数log函数表示及用法
2020/12/09 Python
意大利单身交友网站:Meetic
2020/07/12 全球购物
中英文自我评价常用句型
2013/12/19 职场文书
标准的毕业生自荐信
2014/04/20 职场文书
2014公司党员自我评价范文
2014/09/11 职场文书
2015年保洁工作总结范文
2015/04/28 职场文书
2015年医院保卫科工作总结
2015/07/23 职场文书
Windows11里微软已经将驱动程序安装位置A盘删除
2021/11/21 数码科技
提高系统的吞吐量解决数据库重复写入问题
2022/04/23 MySQL