架不住移动端的兼容的诱惑,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控制流程,下次继续研究,加油