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行
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持。