我一定要征服移動端佈局,深入分析bootstrap就成爲了捷徑之一。路雖長,我雖笨,但架不住傻逼似得堅持。加油!!
tabjs是bootstrap選項卡動態,裏面常見的下拉框等效果,今天來試試源碼難度,順帶溫習jQuery的API,何樂不爲,反正目前我都是小項目,jQuery,zepto,挺好!
插件外殼,立即執行函數,傳形參$和實參jQuery
+function ($) {}(jQuery);
偷個懶,直接放詳細註釋的源碼,由於能力不夠,可能會有些錯誤,歡迎指正
/* ========================================================================
* Bootstrap: tab.js v3.3.7
* http://getbootstrap.com/javascript/#tabs
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// TAB CLASS DEFINITION
// ====================
var Tab = function (element) {
// jscs:disable requireDollarBeforejQueryAssignment
this.element = $(element)
// jscs:enable requireDollarBeforejQueryAssignment
}
Tab.VERSION = '3.3.7'
Tab.TRANSITION_DURATION = 150 //過渡時間css3的transition
//tab原型show函數
Tab.prototype.show = function () {
var $this = this.element
var $ul = $this.closest('ul:not(.dropdown-menu)') //closest() 方法獲得匹配選擇器的第一個祖先元素,從當前元素開始沿 DOM 樹向上。
var selector = $this.data('target') //data() 方法向被選元素附加數據,或者從被選元素獲取數據。
if (!selector) {//如果selector沒有數據
selector = $this.attr('href') //attr() 方法設置或返回被選元素的屬性值,返回href地址
//正則替換部分:任意個.不對#號記錄,以任意非空白字符結束。使用空字符串替換.#號前面字符
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
//如果當前li已經觸發,返回
if ($this.parent('li').hasClass('active')) return
var $previous = $ul.find('.active:last a') //選擇最後active的後代元素 a
//jQuery.Event 構造器暴露出來,然後通過$.trigger來觸發hide.bs.tab事件
var hideEvent = $.Event('hide.bs.tab', {
relatedTarget: $this[0]
})
var showEvent = $.Event('show.bs.tab', {
relatedTarget: $previous[0]
})
//trigger在每一個匹配的元素上觸發hideEvent自定義事件
$previous.trigger(hideEvent)
$this.trigger(showEvent)
//isDefaultPrevented()根據事件對象中是否調用過 event.preventDefault() 方法來返回一個布爾值。
if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
var $target = $(selector)
//傳參進入原型activate函數,實現隱藏顯示效果
this.activate($this.closest('li'), $ul)
this.activate($target, $target.parent(), function () {
$previous.trigger({
type: 'hidden.bs.tab',
relatedTarget: $this[0]
})
$this.trigger({
type: 'shown.bs.tab',
relatedTarget: $previous[0]
})
})
}
Tab.prototype.activate = function (element, container, callback) {
var $active = container.find('> .active')//container後代active元素
var transition = callback
&& $.support.transition //jQuery.support 屬性包含表示不同瀏覽器特性或漏洞的屬性集。
&& ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)
//下一個
function next() {
$active
.removeClass('active') //移除active類
.find('> .dropdown-menu > .active') //選擇後代dropdown-menu的後代active元素
.removeClass('active')//移除active類
.end()//主要是在利用 jQuery 的鏈條屬性(命令鏈)通過 end(),我們可以把所有方法調用串聯在一起 恢復選擇上一級元素
.find('[data-toggle="tab"]') //在.find('> .dropdown-menu > .active')上找到data-toggle="tab"屬性的元素
.attr('aria-expanded', false) //無障礙閱讀設置爲false
element
.addClass('active') //在傳入元素上增加active類
.find('[data-toggle="tab"]') //選擇data-toggle="tab"屬性的元素
.attr('aria-expanded', true) //設置無障礙閱讀爲true
//如果transition存在
if (transition) {
element[0].offsetWidth // reflow for transition
element.addClass('in') //增加in類
} else {
element.removeClass('fade') //移除fade類
}
//若傳入元素的父級dropdown-menu存在
if (element.parent('.dropdown-menu').length) {
element
.closest('li.dropdown') //closest() 方法獲得匹配選擇器的第一個祖先元素,從當前元素開始沿 DOM 樹向上。
.addClass('active') //增加active類
.end()//主要是在利用 jQuery 的鏈條屬性(命令鏈)通過 end(),我們可以把所有方法調用串聯在一起 恢復選擇上一級元素
.find('[data-toggle="tab"]') //選擇data-toggle="tab"屬性元素
.attr('aria-expanded', true)//無障礙閱讀屬性 true
}
callback && callback() //若callback爲true,則把callback當做函數執行;類似於if(callback){callback()}
}
//若 $active和transition都爲true,則執行$active
$active.length && transition ?
$active
.one('bsTransitionEnd', next) //將next()綁定到bsTransitionEnd事件上
.emulateTransitionEnd(Tab.TRANSITION_DURATION) :
next()
$active.removeClass('in')
}
// TAB PLUGIN DEFINITION
// =====================
function Plugin(option) {
return this.each(function () {//加each是jquery插件的標配,意爲選中多個dom時挨個處理
var $this = $(this)
var data = $this.data('bs.tab')//先取一下bs.tab 這一步是爲了緩存Tab對象的,這是必須的,不可能點擊一下tab就new Tab(this),
if (!data) $this.data('bs.tab', (data = new Tab(this)))//如果沒有data,那麼將點擊的a標籤傳入tab,然後把Tab對象賦值給data
if (typeof option == 'string') data[option]()//如果傳入的是字符串,則執行相應的方法
})
}
var old = $.fn.tab
$.fn.tab = Plugin
$.fn.tab.Constructor = Tab
// TAB NO CONFLICT
// ===============
//防衝突代碼,爲了規範,應該加上
$.fn.tab.noConflict = function () {
$.fn.tab = old
return this
}
// TAB DATA-API
// ============
// TAB DATA-API 自動給你初始化了,這樣就可以不用寫js代碼了
var clickHandler = function (e) {
e.preventDefault()//阻止事件冒泡
Plugin.call($(this), 'show')
}
$(document)
.on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
.on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)//pill這個是給膠囊導航用的,其實tab和pill原理都一樣,只是名字不一樣而已
}(jQuery);
每天努力一點,距離大神就近一點