插件的建立機制
默認狀況下,boostrap.js文件被頁面加載執行完成後,boostrap會自動根據html元素的data-toggle屬性和相關class建立插件對象。有時候,咱們不但願boostrap插件的行爲影響到整個頁面,但願能夠精準地在一個 指定的html元素上建立插件。要想達到這個目的,須要兩個步驟:
1. 關閉插件的默認行爲。
2. 在指定的html元素從新創插件。
第一步只須要很簡單的一行代碼就能夠達到目的:
$(document).off(".data-api");
這行代碼是關閉.data-api名字空間下的全部事件處理器,而boostrap全部插件的事件類型都是在.data-api名字空間下的,因此,這樣就可讓全部插件失效。
第二步就比較麻煩,須要搞清楚bootstrap默認是怎樣建立插件的。下面以collapse插件爲例看看插件的建立過程:
function getTargetFromTrigger($trigger) {
var href
var target = $trigger.attr('data-target')
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
return $(target)
}
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.collapse')
var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
if (typeof option == 'string') data[option]()
})
}
var old = $.fn.collapse
$.fn.collapse = Plugin
$.fn.collapse.Constructor = Collapse
$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
var $this = $(this)
if (!$this.attr('data-target')) e.preventDefault()
var $target = getTargetFromTrigger($this)
var data = $target.data('bs.collapse')
var option = data ? 'toggle' : $this.data()
Plugin.call($target, option)
})
經過上面的一段代碼,能夠獲得4個重要的結論:
1. Plugin函數表名,bootstrap插件其實是一個jquery插件。
2. document元素全部子元素中,帶有屬性data-toggle="collapse"的元素上註冊了click事件.
3. 帶有data-toggle="collapse"屬性的元素只是觸發器(trigger), data-target指向的元素纔是collapse插件建立的目標元素。
4. trigger第一次執行click事件處理器時纔會建立collapse插件。
有了這4個結論,就很容易想到,只需把click.bs.collapse.data-api事件註冊到指定的知道的元素上便可隨意控制collapse插件的影響範圍,即:把document替換成你想要的element。
collapse插件是一個很典型的bootstrap插件,在boostrap中,絕大多數插件都和它相似,經過在document上註冊事件做爲插建立的入口。所以能夠說,boostrap插件的手動建立過程,就是在指定的元素上從新註冊事件。
下面來具體看一下每個插件的建立和和關閉。
Modal
默認狀況下,在帶有data-toggle="modal"的元素上註冊click.bs.modal.data-api的處理函數,當觸發這個元素的click事件後,會在data-target或href屬性指向的元素上建立Modal插件。對真正的modal來講,這個元素只是一個trigger, model以後的行爲和它再無關係。modal建立能夠忽略這個trigger, $(elem).modal("show")便可在指定的元素上建立,$(elem).modal("hide")關閉。
Dropdown
建立:
$(elem).dropdown();
if(!$(document).data("dropdown_inited")){
$(document).data("dropdown_inited", true);
$(document).dropdown();
}
droapdown方法會把Dropdown的toggle方法註冊到elem的click事件上,toggle方法實現了dropdwon的全部行爲。只是這樣還不行,由於這個插件還有一個行爲:點擊其餘任何地方都會致使原來已經彈出的dropdwon收起,因此還要在document建立一個用於處理全局點擊事件的drapdown。不要擔憂這個全局的dropdown會形成不良影響,它的惟一做用是收起已經打開的dropdown。
關閉:
$(elem).off("click.bs.dropdown");
Tab
建立
function clickHandler(e) {
e.preventDefault()
let Plugin = $(this).tab;
Plugin.call($(this), 'show')
}
$(elem)
.on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
.on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
關閉
$(elem)
.off('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
.off('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
Tooltip, Popover
建立
$(elem).tooltip();
$(elem).popover();
關閉
默認狀況下會在elem上註冊mouseenter, mouseleave或focusin, focusout或click事件,關閉是須要註銷這幾個事件。這些事件的註冊行爲取決於options的trigger屬性,默認值是"hover focus"
function off(plugin){
let triggers = plugin.options.trigger.split(' ')
for (var i = triggers.length; i--;) {
let trigger = triggers[i]
if (trigger == 'click') {
plugin.$element.off('click.' + plugin.type)
} else if (trigger != 'manual') {
let eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
let eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
plugin.$element.off(eventIn + '.' + plugin.type)
plugin.$element.off(eventOut + '.' + plugin.type)
}
}
plugin.$element.data("bs"+plugin.type, null);
}
let plugin =$(elem).data('bs.tooltip')
if(plugin) off(plugin);
plugin = $(elem).data('bs.popover');
if(plugin off(plugin);
Popover是從Tooltip繼承而來,它們的行爲處理方式是同樣的。
Alert
建立
$(elem).alert();
關閉
$(elem).off("click", [data-dismiss="alert"]);
Button
建立
$(elem).button();
關閉
button插件不需關閉,bootstrap沒有在它在元素上註冊事件處理,形成干擾。
Collapse
建立:
collapse默認在trigger上註冊click事件處理器函數,這個函數先從trigger的data-taget或href上獲得建立collapse的目標元素,而後在這個目標元素上建立collapse
function getTargetFromTrigger($trigger){
let href
let target = $trigger.attr('data-target')
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
return $(target)
}
function clickHandler(e){
let $this = $(this)
if (!$this.attr('data-target')) e.preventDefault()
let $target = getTargetFromTrigger($this)
let data = $target.data('bs.collapse')
let option = data ? 'toggle' : $this.data()
let Plugin = elem.collapse;
Plugin.call($target, option)
}
$(elem).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', clickHandler);
關閉
$(elem).off(".collapse.data-api", '[data-toggle="collapse"]', clickHandler);
let triggers = $(elem).find('[data-toggle="collapse"]');
triggers.foreach(function(){
let $this = $(this)
let $target = getTargetFromTrigger($this)
$target.data('bs.collapse', null);
});
Carousel
建立
trigger的點擊事件處理函數
function clickHandler(e){
let href
let $this = $(this)
let $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
if (!$target.hasClass('carousel')) return
let options = $.extend({}, $target.data(), $this.data())
let slideIndex = $this.attr('data-slide-to')
if (slideIndex) options.interval = false
let Plugin = $this.carousel;
Plugin.call($target, options)
if (slideIndex) {
$target.data('bs.carousel').to(slideIndex)
}
e.preventDefault()
}
添加事件處理函數
$(elem)
.on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
.on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
;
關閉:
銷燬trigger關聯的元素上的carousel
function destroy(triggers){
triggers.foreach(function(){
let href
let $this = $(this)
let $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, ''));
if (!$target.hasClass('carousel')) return
$target.foreach(function(){
let $item = $(this);
$item.data('bs.carousel', null);
});
});
}
關閉事件處理
$(elem)
.off('click.bs.carousel.data-api', '[data-slide]', clickHandler)
.off('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
;
銷燬全部trigger關聯元素上的carousel
let triggers = elem.find("[data-slide]");
destroy(triggers);
triggers = elem.find("[data-slide-to]");
destroy(triggers);