架不住移動端的兼容的誘惑,bootstrap隨着學習,它的強大以及可以讓我學習的地方衆多。今天又來寫寫button.js這個相對簡單的按鈕插件,寫完這篇文章,估計我對js又有新的提高和鞏固。本文只是作爲本人筆記,看法,不保證對,也很膚淺,大神繞道。
個人認爲知識點:jQuery插件:能夠進一步學會如何封裝某個js庫的插件,能夠學習更多平時不常用卻比較重要的js語法基礎
bootstrap的js源碼:能夠更好的理解bootstrap框架動態效果展示
bootstrap的css3和js的配合使用:它大量使用css3動畫效果,transition,transform用的666
雖然bootstrap使用頻率很高,也相對好用,但還是非常多人反對直接使用它,理由各式各樣,但無可否認,它是最受歡迎的前端樣式框架,既然大受歡迎,對於我這種菜鳥來說,還是值得好好研讀。研究它的使用,研究它的源碼,研究框架分工,各個分工的實現,less代碼編譯工具使用等等。
迴歸正題,buttonjs源碼分析筆記開始。
首先jQuery插件通用寫法,匿名立即執行函數,傳入實參jQuery,形參$。這給我們一個啓示,製作某個js庫的依賴插件庫,都是要傳入那個js庫的參數;由於js語言的特性,爲了避免變量污染,都需要立即執行函數隔離變量,需要暴露的變量註冊爲全局變量。
+function($){
//........
}(jQuery);
嚴格模式:最直接的目的是逐步淘汰掉以前js語法中的糟粕;消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行爲;消除代碼運行的一些不安全之處,保證代碼運行的安全;提高編譯器效率,增加運行速度;爲未來新版本的Javascript做好鋪墊。隨着es6標準的逐步落地實現,隨着各個大項目,開源框架vue、 angular等使用es6,養成使用嚴格模式習慣,是非常重要的。
'use strict';
使用嚴格模式,會禁止使用某些關鍵字,變量必須事先var 聲明等等,在某些程度上改變js原來的宗旨:寬鬆,動態,腳本。時過境遷,畢竟以前js承擔任務不多,現在瀏覽器發展,前端作用越來越強,js做出限制以及增加是必須的,不過感覺越來越向強類型語言靠攏的感覺。
//button類構造函數
var Button = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, Button.DEFAULTS, options) //jQuery的擴展方法,插件必備:將options合併到Button.DEFAULTS中
this.isLoading = false //是否加載
}
$.extend是我們在寫插件的過程中常用的方法,該方法有一些重載原型extend(dest,src1,src2,src3...)
它的含義是將src1,src2,src3...合併到dest中,返回值爲合併後的dest,由此可以看出該方法合併後,是修改了dest的結構的。如果想要得到合併的結果卻又不想修改dest的結構,
可以如下使用:
var aa=$.extend({},{x:"x",y:'y'},{x:"x2",z:"z"})
結果展示
aa={x:'x2',y:'y',z:'z'}
也即是源碼中的樣子
this.options = $.extend({}, Button.DEFAULTS, options)
button的loading狀態,通過獲取button當中的value值或者html值,然後在loading時候將button設置爲disabled=true,禁止點擊等待下載,下載完畢允許點擊//設置button狀態
Button.prototype.setState = function (state) {
var d = 'disabled' //禁用
var $el = this.$element
var val = $el.is('input') ? 'val' : 'html' //如果是input控件,存取input內容,否則存取html內容
var data = $el.data()//data() 方法向被選元素附加數據,或者從被選元素獲取數據。
state += 'Text' //拼接Text字符串,便於理解字面意思
if (data.resetText == null) $el.data('resetText', $el[val]())//如果data沒有resetText綁定,則創建
// push to event loop to allow forms to submit
//$.proxy()該方法通常用於向上下文指向不同對象的元素添加事件。保證了this指向,避免作用域改變而導致錯誤
setTimeout($.proxy(function () {
$el[val](data[state] == null ? this.options[state] : data[state])
if (state == 'loadingText') {
this.isLoading = true
$el.addClass(d).attr(d, d).prop(d, true)//添加class="disabled" disabled="true"
} else if (this.isLoading) {
this.isLoading = false
$el.removeClass(d).removeAttr(d).prop(d, false)//移除上面添加的屬性
}
}, this), 0)
}
button切換,例如radio checkbox 和其它類型的input,把單選框和複選框分別區分,其它的都是輸入框,判斷完成
//按鈕效果切換,radio checkbox
Button.prototype.toggle = function () {
var changed = true
var $parent = this.$element.closest('[data-toggle="buttons"]')//closest表示從this.$element開始檢索擁有data-toggle="buttons"屬性的標籤
//區分radio checkbox按鈕,active是規定好的類樣式
if ($parent.length) {
var $input = this.$element.find('input')
if ($input.prop('type') == 'radio') {
if ($input.prop('checked')) changed = false
$parent.find('.active').removeClass('active')
this.$element.addClass('active')
} else if ($input.prop('type') == 'checkbox') {
if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
this.$element.toggleClass('active')
}
$input.prop('checked', this.$element.hasClass('active'))
if (changed) $input.trigger('change')
} else {
this.$element.attr('aria-pressed', !this.$element.hasClass('active'))//arai盲人無障礙閱讀屬性
this.$element.toggleClass('active')
}
}
bootstrap的button插件自定義:
① 暴露類名, 可以通過這個爲插件做自定義擴展
// 擴展的方式
// 設置 : $.fn.button.Constructor.newMethod = function(){}
// 使用 : $btn.button("newMethod");
② 無衝突處理
③ 事件代理, 智能初始化
以上是開發自定義插件必要過程,由於沒什麼經驗,具體不做分析,百度去吧
// BUTTON PLUGIN DEFINITION
// ========================
function Plugin(option) {
return this.each(function () {
var $this = $(this) //保存this作用域
// 判斷是否初始化過的依據
var data = $this.data('bs.button') //data() 方法向被選元素附加數據,或者從被選元素獲取數據。
var options = typeof option == 'object' && option //如果option爲object類型和option參數值,返回true,否則返回false
if (!data) $this.data('bs.button', (data = new Button(this, options)))// 如果沒有初始化過, 就初始化它,爲元素附加Button實例
if (option == 'toggle') data.toggle()
else if (option) data.setState(option)
})
}
var old = $.fn.button
$.fn.button = Plugin
// ① 暴露類名, 可以通過這個爲插件做自定義擴展
$.fn.button.Constructor = Button
// BUTTON NO CONFLICT
// ==================
//② 無衝突處理
$.fn.button.noConflict = function () {
$.fn.button = old
return this
}
// BUTTON DATA-API
// ===============
// ③ 事件代理, 智能初始化
$(document)
.on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
var $btn = $(e.target).closest('.btn')
Plugin.call($btn, 'toggle')
if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) {
// Prevent double click on radios, and the double selections (so cancellation) on checkboxes
e.preventDefault()
// The target component still receive the focus
if ($btn.is('input,button')) $btn.trigger('focus')
else $btn.find('input:visible,button:visible').first().trigger('focus')
}
})
.on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
$(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
})
雖然沒有完全看懂,但也學到jQuery一些API的使用,知道bootstrap的button控制流程,下次繼續研究,加油