命名规范
通常情况下采用下面的模式进行命名:
jquery.pluginName.js
min版则采用与之类似的命名规划,并添加一个min标记:
jquery.pluginName.min.js
扩展jquery的三种方法
1. 通过一个简单的、jquery函数prototype属性的别名(jquery.fn)进行扩展
这种方法其是实添加一个函数,例如each、ajax这样的
2. 使用jquery.extend()方法
这是平常使用的方式
3. 使用jquery ui widget factory进行扩展
这是jquery ui中的,不过它可以脱离jquery ui运行。这种方法可以创建有状态的插件。
之前写的折叠面板
css
1 .km-accdion{ 2 width: 100%; 3 overflow: hidden; 4 } 5 6 .km-accdion ul{ 7 margin: 0; 8 padding: 0; 9 } 10 11 .km-accdion ul li{ 12 margin: 0; 13 padding: 0; 14 list-style: none; 15 } 16 17 .km-accdion-menu{ 18 border-top: 1px solid #cccccc; 19 border-bottom: 1px solid #cccccc; 20 } 21 22 .km-accdion-menu a{ 23 display: block; 24 color: #555; 25 text-decoration: none; 26 background-color: #aaaaaa; 27 padding: .5em 1em; 28 } 29 30 .km-accdion-menu a:hover{ 31 color: white; 32 } 33 34 .km-accdion-menu-item{ 35 display: none; 36 } 37 38 .km-accdion-menu-item a{ 39 display: block; 40 color: #555; 41 text-decoration: none; 42 background-color: #dddddd; 43 padding: .5em 1em; 44 border-width: 1px; 45 border-color: transparent; 46 border-top-style: solid; 47 background-clip: padding-box; 48 } 49 50 .km-accdion-menu-item a:hover{ 51 color: #fff; 52 background-color: #cccccc; 53 } 54 55 .km-accdion-menu-item-show{ 56 display: block !important; 57 } 58 59 .km-accdion-menu-item-show a{ 60 display: block; 61 color: #555; 62 text-decoration: none; 63 background-color: #dddddd; 64 padding: .5em 1em; 65 } 66 67 .km-accdion-menu-item-show a:hover{ 68 color: #fff; 69 background-color: #cccccc; 70 }
js
1 /** 2 * 手风琴插件 3 * TODO 多层嵌套 4 * TODO 前面加小图标 5 * TODO 去除对 reset.css 的依赖 6 */ 7 ;(function ($) { 8 'use strict'; 9 10 /** 11 * 默认配置 12 * event : 触发手风琴切换的事件名称 13 * onlyOneMenuItem : 是否只显示一个菜单,true 表示只显示一个,false 表示每个菜单都可单独打开与关闭 14 * @type {{event: string, onlyOneMenuItem: boolean}} 15 */ 16 var defaultOption = { 17 event: 'click', 18 onlyOneMenuItem : true 19 }; 20 21 var accdionClass = { 22 accdionItem : '.km-accdion-item', 23 accdionItemStr : 'km-accdion-item', 24 25 accdionMenu : '.km-accdion-menu', 26 accdionMenuStr : 'km-accdion-menu', 27 28 accdionMenuItem : '.km-accdion-menu-item', 29 accdionMenuItemStr : 'km-accdion-menu-item' 30 }; 31 32 /** 33 * 隐藏其它菜单 34 * @param currentItem 当前所在的 $('.km-accdion-item') 对象 35 */ 36 function hideOtherMenuItem( currentItem ){ 37 /** 38 * 39 * 其它菜单隐藏 40 * 思路: 41 * 1. 每次点击当前item的时候为其设置一个唯一ID,值为 currentMenuItemId + 时间戳 + 一个随机数,如:currentMenuItemId1513119262000578 42 * 2. 通过 $.siblings方法查找当前插件实例中的每个item,并加上not过滤,过滤值为当前item的id 43 * 3. 通过slideUp方法隐藏 44 * 45 */ 46 var currentItemId = 'currentMenuItemId' + Date.parse(new Date().toDateString()) + Math.round(Math.random() * 10000); 47 currentItem.attr('id',currentItemId); 48 currentItem.siblings(accdionClass.accdionItem).not('#'+currentItemId).find(accdionClass.accdionMenuItem).slideUp(500); 49 } 50 51 $.fn.kmaccdion = function (options) { 52 53 var option = $.extend({},defaultOption,options); 54 55 return this.each(function () { 56 var self = $(this); 57 58 self.find(accdionClass.accdionItem).each(function () { 59 var currentItem = $(this); 60 currentItem.find(accdionClass.accdionMenu).on(option.event, function () { 61 // 当前菜单的显示与隐藏 62 var currentMenuItem = $(this); 63 currentMenuItem.next(accdionClass.accdionMenuItem).slideToggle(300); 64 65 if( option.onlyOneMenuItem ){ 66 hideOtherMenuItem(currentItem); 67 } 68 69 }); 70 }); 71 72 }); 73 }; 74 })(jQuery);
引用
1 <!-- category --> 2 <div id="category-container"> 3 <div class="km-accdion"> 4 <div class="km-accdion-item"> 5 <ul class="km-accdion-menu-item-show"> 6 <li><a href="#">全部</a></li> 7 </ul> 8 </div> 9 <div class="km-accdion-item"> 10 <div class="km-accdion-menu"> 11 <a href="#">移动开发</a> 12 </div> 13 <ul class="km-accdion-menu-item"> 14 <li><a href="#">移动开发</a></li> 15 <li><a href="#">Android 开发</a></li> 16 <li><a href="#">ISO 开发</a></li> 17 </ul> 18 </div> 19 <div class="km-accdion-item"> 20 <div class="km-accdion-menu"> 21 <a href="#">前端开发</a> 22 </div> 23 <ul class="km-accdion-menu-item"> 24 <li><a href="#">前端开发</a></li> 25 <li><a href="#">HTML/CSS</a></li> 26 <li><a href="#">JavaScript</a></li> 27 <li><a href="#">HTML5/CSS3</a></li> 28 <li><a href="#">ECMAScript 6</a></li> 29 <li><a href="#">JQuery</a></li> 30 </ul> 31 </div> 32 </div> 33 </div> 34 <!-- // category -->
效果
不足
1. 面板插件绑定了面板内容的展示,无法自定义
2. 样式比较难看【好吧,以我目前的CSS水平来说,能写/抄出这样的效果已经很不错了】
3. 没有图标
4. 无法加载远程数据【这是最无法接受的】
5. 不具备通用性【也是无法接受的】
6. 面板切换方法太过低级
jquery插件开发最佳实践
总的来说jquery插件开发有如下最佳实践:
1. 在jquery名称空间中只为插件声明单个名称
2. 为插件中的事件和数据定义名称空间
3. 接收一个options参数以控制插件的行为
4. 为插件的默认设置提供公开的访问
5. 保护私有函数的私有性
6. 公开需要公开的函数
7. 支持链式调用
这里就不考虑metadata插件了,因为$.data已经提供了类似的功能
当然,现在只是开发折叠面板、下拉菜单、tab选项面板之类的简单插件,如果要开发datagrid等插件,我认为需要使用jquery ui widget factory。
一个自定义事件的例子
这个例子实现的功能很简单,
在一个div中有一个按钮
div默认有边框和padding
单击按钮可以改变边框颜色或者去掉padding
当边框改变之后,触发自定义事件【重难点】
js
1 ;(function ($, window, document, undefined) { 2 3 /** 4 * 插件的私有方法 5 * 初始化 6 * @param jq jquery 对象,和你使用$('#one')获得的对象是一样的 7 */ 8 function init(jq) { 9 // 初始化步骤: 10 // 1. 初始化插件样式,这里就不写那么多了 11 console.log('私有方法:init'); 12 13 // 2. 自定义事件 14 // 绑定事件,我这里是直接绑定在插件本身上面 15 jq.on('onBorderChanged.kmAccordion', jq.config.onBorderChanged); 16 17 // 3. 绑定单击事件逻辑,实现点击按钮,改变padding和border 18 jq.find('button').click(function () { 19 var text = jq.find('button').text(); 20 // 私有方法调用公有方法 21 jq.kmAccordion('changeBorder',text); 22 }); 23 } 24 25 function sayNo() { 26 console.log('私有方法:sayNo'); 27 } 28 29 /** 30 * 插件实现代码 31 * @param options 如果是json对象,则创建[初始化]插件,如果是字符串,则用来调用插件的公开方法 32 * @param param 当前options是字符串时,代表传递给插件公开方法的参数。当然,你可以不传 33 * @returns {*} 34 */ 35 $.fn.kmAccordion = function (options, param) { 36 // console.log(this); 37 // 如果是方法调用 38 if (typeof options === 'string') { 39 return $.fn.kmAccordion.methods[options](this, param); 40 } 41 42 // 获得配置,这里为了得到用户的配置项,覆盖默认配置项,并保存到当前jquery插件实例中 43 var _opts = $.extend({}, $.fn.kmAccordion.defaults, options); 44 var jq = this; 45 jq.config = _opts; 46 47 // 链式调用 48 return this.each(function () { 49 // console.log(this); 50 // 调用私有方法,初始化插件 51 init(jq); 52 }); 53 }; 54 55 56 /** 57 * 插件的公开方法 58 */ 59 $.fn.kmAccordion.methods = { 60 changeBorder: function (jq, text) { 61 // 公有方法可以相互调用 62 jq.kmAccordion('sayHello'); 63 64 // 公有方法调用私有方法,但是对插件外部来说,私有方法是不可见的 65 sayNo(); 66 67 // 实现逻辑 68 jq.css({'border-color':jq.config.color}); 69 if( jq.config.clearPadding ){ 70 jq.css({'padding':'0'}); 71 } 72 console.log('按钮文本:'+text); 73 74 // 触发事件 75 jq.config.onBorderChanged.call(jq, jq.css('border-color')); 76 77 // 返回jquery对象,支持链式调用 78 return jq; 79 }, 80 sayHello : function(jq){ 81 console.log('hello'); 82 return jq; 83 }, 84 options: function (jq) { 85 // 这个就不需要支持链式调用了 86 return jq.config; 87 } 88 }; 89 90 91 /** 92 * 插件的默认配置 93 */ 94 $.fn.kmAccordion.defaults = { 95 // 属性 96 clearPadding: true, 97 color: 'blue', 98 99 /** 100 * 自定义事件 101 * @param color 事件触发时的参数 102 */ 103 onBorderChanged: function (color) { 104 console.log('默认事件实现,当然,你也可以不实现,留空:' + color); 105 } 106 }; 107 })(jQuery, window, document);
调用
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <style> 7 div{ 8 border: 1px solid red; 9 padding: 20px; 10 float: left; 11 margin: 20px; 12 } 13 </style> 14 </head> 15 <body> 16 <div id="one"> 17 <button>第一个按钮</button> 18 </div> 19 <div id="two"> 20 <button>第二个按钮</button> 21 </div> 22 23 <button id="btnone">调用第一个插件的方法</button> 24 <button id="btntwo">调用第二个插件的方法</button> 25 26 27 28 <script type="text/javascript" charset="UTF-8" src="./external/jquery.min.js"></script> 29 <script type="text/javascript" charset="UTF-8" src="one.js"></script> 30 <script type="text/javascript" charset="UTF-8"> 31 $(function () { 32 // 实例化插件,改变一下默认配置 33 var one = $('#one').kmAccordion({ 34 clearPadding:false, 35 color : 'black', 36 onBorderChanged : function (color) { 37 console.log('自定义事件:'+color); 38 } 39 }); 40 // 实例化插件,使用默认配置 41 var two = $('#two').kmAccordion(); 42 43 $('#btnone').click(function () { 44 console.log('外部调用 公开方法 1'); 45 // 调用插件的方法,同时会触发自定义事件 46 one.kmAccordion('changeBorder',one.find('button').text()); 47 }); 48 49 $('#btntwo').click(function () { 50 console.log('外部调用 公开方法 2'); 51 // 这个也调用一下 52 two.kmAccordion('changeBorder',two.find('button').text()); 53 }); 54 55 }); 56 </script> 57 </body> 58 </html>
效果
默认运行效果
单击第一个div中的按钮
刷新页面,单击第二个div中的按钮
刷新页面,单击:《调用第一个插件的方法》按钮
刷新页面,单击:《调用第二个插件的方法》按钮
可以看到效果还是不错的。
注意事项
要时刻注意this的指向
把36行和49行注释打开,刷新页面,就会看到原因
上面例子的演变流程
基于MVC的JavaScript Web富应用开发
KM项目插件体系规划
N-2
研究 jquery ui 源码
N-1
研究Bootstrap js组件源码
终极目标
基于es5/6构建KM项目前端JS UI库