JavaScript插件化开发教程 (三)
一,开篇分析
前面两篇文章我们主要讲述了以“jQuery的方式如何开发插件”,以及过程化设计与面向对象思想设计相结合的方式是
如何设计一个插件的,两种方式各有利弊取长补短,本系列文章是以学习为导向的,具体场景大家自己定夺使用方式。那么今天从这篇文章开始,我们就以实例的方式带着大家由浅入深的开发属于自己的插件库。嘿嘿嘿,废话少说,进入正题。直接上实际效果图:
大家看到了吧,这是一个选项卡插件,在我们日常做那种单页应用("SPA")的时候或许会接触到,就拿今天的例子来说吧,
我们做一个基于BS结构的系统,会有若干模块组成,它们是构建系统的全部组成,通过这个插件我们可以有效地管理我们模块
的体验形式以及用户可交互性,下面就具体分析一下吧。
(二),实例分析
(1),首先确定这个插件做什么事。下面看一下插件的调用方式,以及配置参数说明。如下代码:
bigbear.ui.createTab($("#tab"),{
buttonText : "添加模块" ,
result : [
{
text : "向导提示" ,
url : "help.html" ,
showClose : "0" ,
status : "1"
} ,
{
text : "学生信息" ,
url : "info.html" ,
showClose : "1" ,
status : "1"
} ,
{
text : "学生分类" ,
url : "category.html" ,
showClose : "1" ,
status : "1"
} ,
{
text : "大熊君{{bb}}" ,
url : "bb.html" ,
showClose : "1" ,
status : "1"
} ,
{
text : "Beta测试模块" ,
url : "test.html" ,
showClose : "1" ,
status : "1"
}
]
}) ;
“bigbear.ui.createTab”里面包含两个参数,第一个是dom节点对象,第二个是插件参数选项,"buttonText "代表“Tab“插件中,操作按钮的文字描述。
”result“是一个数组,里面包含的是选项卡项目的属性,包括文字描述,点击选项卡项目时做请求使用的url,”showClose“代表选项卡的选项是否显示关闭按钮。
”status“代表选项的状态,默认为打开状态,可能会有关闭状态,分别表示为:1-打开,0-关闭。
(2),所涉的功能有哪些
通过可选参数,动态生成相关选项条目,如下来个例子:
bigbear.ui.createTab($("#tab"),{
buttonText : "添加模块" ,
result : [
{
text : "jQuery源码分析" ,
url : "help.html" ,
showClose : "0" ,
status : "1"
} ,
{
text : "大熊君{{bb}}}" ,
url : "bb.html" ,
showClose : "1" ,
status : "1"
}
]
}) ;
效果如下所示:
可自由添加以及删除条目选项,如下效果所示:
上图为其中一种情况,无模块的时候,会提示信息。
这是第二种情况,之前删除的可以恢复。
(三),完整代码以供学习,本代码已经过测试,包括目录结构以及相关的文件。
(1),html
<body> <div class="dxj-ui-hd"> 大熊君{{bb}} - DXJ UI ------ Tab </div> <div class="dxj-ui-bd"> <div id="tab"> <div class="title"> <div class="adder"> + 添加学生信息 </div> <div class="items"> <!--<div><span class="del">X</span>欢迎页</div> <div><span class="del">X</span>用户管理</div> <div><span class="del">X</span>Bigbear</div>--> </div> </div> <div class="console-panel"> </div> <div class="content"> <!--<div class="c"> <div class="input-content"><span>姓名:</span><input type="text" /></div> <div class="input-content"><span>备注:</span><textarea></textarea></div> </div> <div class="input-content"><input type="button" value="保存" /></div> --> </div> </div> </div> </body>
(2),css文件代码
代码如下:
.dxj-ui-hd { padding:0px ; margin : 0 auto; margin-top:30px; width:780px; height:60px; line-height: 60px; background: #3385ff; color:#fff; font-family: "微软雅黑" ; font-size: 28px; text-align: center; font-weight:bold; } .dxj-ui-bd { padding:0px ; margin : 0 auto; width:778px; padding-top : 30px ; padding-bottom : 30px ; overflow: hidden; border:1px solid #3385ff; } .dxj-ui-bd #tab { padding:0px ; margin : 0 auto; width:720px; overflow: hidden; } .dxj-ui-bd #tab .title { width:720px; overflow: hidden; border-bottom:2px solid #3385ff; } .dxj-ui-bd #tab .title .adder { width:160px; height:32px; line-height: 32px; background: #DC143C; color:#fff; font-family: "微软雅黑" ; font-size: 14px; text-align: center; font-weight:bold; float : left; cursor:pointer; } .dxj-ui-bd #tab .title .items { height:32px; margin-left:20px; width:540px; overflow: hidden; float : left; } .dxj-ui-bd #tab .title .items div { padding:0px; margin-left:10px; width:96px; height:32px; line-height: 32px; background: #3385ff; color:#fff; font-family: arial ; font-size: 12px; text-align: center; position:relative; float : left; cursor:pointer; } .dxj-ui-bd #tab .title .items div span.del { width:16px; height:16px; line-height: 16px; display:block; background: #DC143C; position:absolute; right:0 ; top:0; cursor:pointer; } .dxj-ui-bd #tab .content { width:716px; padding-top:30px; overflow: hidden; border:2px solid #3385ff; border-top:0px; min-height:130px; text-align:center; } .dxj-ui-bd #tab .content table { margin : 0 auto ; } .dxj-ui-bd #tab .content div.c { padding-top : 20px ; padding-left:20px; background:#eee; height:140px; } .dxj-ui-bd #tab .content div.c .input-content { margin-top : 10px ; font-family: arial ; font-size: 12px; } .dxj-ui-bd #tab .console-panel { width:716px; padding-top:20px; padding-bottom:20px; overflow: hidden; border:2px solid #3385ff; border-top:0px; border-bottom:2px solid #3385ff; background:#fff; display:none; } .active { font-weight:bold ; }
(3),Js代码如下:
$(function(){ bigbear.ui.createTab($("#tab"),{ buttonText : "添加模块" , result : [ { text : "向导提示" , url : "help.html" , showClose : "0" , status : "1" } , { text : "学生信息" , url : "info.html" , showClose : "1" , status : "1" } , { text : "学生分类" , url : "category.html" , showClose : "1" , status : "1" } , { text : "大熊君{{bb}}" , url : "bb.html" , showClose : "1" , status : "1" } , { text : "Beta测试模块" , url : "test.html" , showClose : "1" , status : "1" } ] }) ; }) ; (function($){ var win = window ; var bb = win.bigbear = win.bigbear || { ui : {} } ; var ui = bb.ui = {} ; var Tab = function(elem,opts){ this.elem = elem ; this.opts = opts ; } ; var tabProto = Tab.prototype ; tabProto._deleteItem = function(item){ var that = this ; this.getElem().find(".title .items div") .eq(item["index"]) .fadeOut(function(){ that._resetContent() ; that._updateStatus(item) ; that._triggerItem(item["index"] + 1) ; that.getElem().find(".title .adder").trigger("click") ; }) ; } ; tabProto._triggerItem = function(next){ var nextStatus = this._getStatus(next) ; var items = this.getElem().find(".title .items div") ; next = items.eq(next) ; if(next.size() && "1" == nextStatus){ //后继dom节点存在 next.trigger("click") ; } else{ items.eq(0).trigger("click") ; } } ; tabProto._getStatus = function(index){ var status = "" ; $.each(this.getOpts()["result"],function(i,item){ if(index == item["index"]){ status += item["status"] ; return false ; } }) ; return status ; } ; tabProto._updateStatus = function(item){ var status = item["status"] ; item["status"] = ("1" == status) ? "0" : "1" ; } ; tabProto.init = function(){ var that = this ; this.getElem().find(".title .adder") .text("+" + this.getOpts()["buttonText"]) .on("click",function(){ that._toggleConsolePanel(function(){ var root = that.getElem().find(".console-panel").empty() ; $.each(that.getOpts()["result"],function(i,item){ if("0" == item["status"]){ var elem = $("<div style='float:left';></div>") .data("item",item) .appendTo(root) ; $("<input type='radio' name='addmod' />").appendTo(elem) ; $("<span></span>").text(item["text"]).appendTo(elem) ; } }) ; if(root.find("div").size()){ $("<input type='button' value='添加模块' style='margin-left:20px'/>") .on("click",function(){ var data = root.find("input[type=radio]:checked").parent().data("item") ; that._updateStatus(data) ; that.getElem().find(".title .items div").eq(data["index"]).fadeIn().trigger("click") ; that.getElem().find(".title .adder").trigger("click") ; }) .appendTo(root) ; } else{ root.text("暂无任何可添加的项目!") ; } }) ; }) ; $.each(this.getOpts()["result"],function(i,item){ item["index"] = i ; that._render(item) ; }) ; this.getElem().find(".title .items div") .eq(0) .trigger("click") ; // 假定是必须有一项,否则插件意义就不大了! } ; tabProto._toggleConsolePanel = function(callback){ this.getElem().find(".console-panel").slideToggle(function(){ $.isFunction(callback) && callback() ; }) ; } ; tabProto._resetContent = function(){ this.getElem().find(".content").html("") ; } ; tabProto._setContent = function(html){ this.getElem().find(".content").html(html) ; } ; tabProto._getContent = function(url){ return $.ajax({ url : url }) ; } ; tabProto._render = function(data){ var that = this ; var item = $("<div></div>") .text(data["text"]) .on("click",function(){ that._setCurrent(data["index"]) ; that._getContent(data["url"]).done(function(result){ that._setContent(result) ; }) .fail(function(){ throw new Error("Net Error !") ; }); }) .appendTo(this.getElem().find(".title .items")) ; if("1" == data["showClose"]){ $("<span class='del'>X</span>") .on("click",function(){ if(win.confirm("是否删除此项?")){ that._deleteItem(data) ; return false ; // 阻止冒泡 } }) .appendTo(item) ; } } ; tabProto._setCurrent = function(index){ var items = this.getElem().find(".title .items div").removeClass("active") ; items.eq(index).addClass("active") ; var contents = this.getElem().find(".content .c").hide() ; contents.eq(index).show() ; } ; tabProto.getElem = function(){ return this.elem ; } ; tabProto.getOpts = function(){ return this.opts ; } ; ui.createTab = function(elem,opts){ var tab = new Tab(elem,opts) ; tab.init() ; return tab ; } ; })(jQuery) ;
(四),最后总结
(1),面向对象的思考方式合理分析功能需求。
(2),以类的方式来组织我们的插件逻辑。
(3),不断重构上面的实例,如何进行合理的重构那?不要设计过度,要游刃有余,推荐的方式是过程化设计与面向对象思想设计相结合。
(4),思考一下上面例子中,选项卡中的选项是否可以独立成单独的类那?比如“Item”,那么“Tab”类如何修改那?带着问题去思考吧。。。
感谢您的阅读,您的支持是我写博客动力。