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 相关文章推荐
JS类定义原型方法的两种实现的区别评论很多
Sep 12 Javascript
Jquery判断$(&quot;#id&quot;)获取的对象是否存在的方法
Sep 25 Javascript
js 遍历json返回的map内容示例代码
Oct 29 Javascript
jquery使用append(content)方法注意事项分享
Jan 06 Javascript
深入剖析JavaScript中的枚举功能
Mar 06 Javascript
jQuery基于ajax实现带动画效果无刷新柱状图投票代码
Aug 10 Javascript
JS Array.slice 截取数组的实现方法
Jan 02 Javascript
概述如何实现一个简单的浏览器端js模块加载器
Dec 07 Javascript
vue数字类型过滤器的示例代码
Sep 07 Javascript
基于elementUI使用v-model实现经纬度输入的vue组件
May 12 Javascript
Vue.js中provide/inject实现响应式数据更新的方法示例
Oct 16 Javascript
vue和iview实现Scroll 数据无限滚动功能
Oct 31 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数据库操作面向对象的优点
2006/10/09 PHP
克隆一个新项目的快捷方式
2013/04/10 PHP
Zend Framework教程之配置文件application.ini解析
2016/03/10 PHP
PHP生成随机密码4种方法及性能对比
2020/12/11 PHP
DHTML Slide Show script图片轮换
2008/03/03 Javascript
编写兼容IE和FireFox的脚本
2009/05/18 Javascript
JS 的应用开发初探(mootools)
2009/12/19 Javascript
js获取当前select 元素值的代码
2010/04/19 Javascript
javascript中IE浏览器不支持NEW DATE()带参数的解决方法
2012/03/01 Javascript
NodeJs中的非阻塞方法介绍
2012/06/05 NodeJs
jQuery解析XML文件同时动态增加js文件的方法
2015/06/01 Javascript
JavaScript学习笔记之DOM基础 2.4
2015/08/14 Javascript
BootStrapTable服务器分页实例解析
2016/12/20 Javascript
JS 组件系列之Bootstrap Table 冻结列功能IE浏览器兼容性问题解决方案
2017/06/30 Javascript
深入理解angular2启动项目步骤
2017/07/15 Javascript
jquery动态赋值id与动态取id方法示例
2017/08/21 jQuery
vue删除html内容的标签样式实例
2018/09/13 Javascript
react组件从搭建脚手架到在npm发布的步骤实现
2019/01/09 Javascript
JavaScript实现身份证验证代码实例
2019/08/26 Javascript
Vue实现圆环进度条的示例
2021/02/06 Vue.js
python实现的二叉树算法和kmp算法实例
2014/04/25 Python
通过源码分析Python中的切片赋值
2017/05/08 Python
Python守护线程用法实例
2017/06/23 Python
Python编译为二进制so可执行文件实例
2019/12/23 Python
python3 使用traceback定位异常实例
2020/03/09 Python
Python基于opencv的简单图像轮廓形状识别(全网最简单最少代码)
2021/01/28 Python
Python3.9.0 a1安装pygame出错解决全过程(小结)
2021/02/02 Python
钉钉企业内部H5微应用开发详解
2020/05/12 HTML / CSS
Rowdy Gentleman服装和配饰:美好时光
2019/09/24 全球购物
三星法国官方网站:Samsung法国
2019/10/31 全球购物
工程造价自荐信
2013/10/09 职场文书
开办化妆品公司创业计划书
2013/12/26 职场文书
房地产促销活动方案
2014/03/01 职场文书
学校食品安全实施方案
2014/06/14 职场文书
python状态机transitions库详解
2021/06/02 Python
Tomcat安装使用及部署Web项目的3种方法汇总
2022/08/14 Servers