仿网易邮箱5.0(三):panel.js
今天我们首先来接触第一个用到的插件--Neter.Panel(登录框),在这个插件中,主要的操作有:添加一个标签,更新标签,删除标签。
还是先上代码:
View Code
1 /** 2 * 面板插件,支持多标签,很像TabPanel,但与之不同的在于标签宽度是平分 3 * 并且不支持url,内容仅能为html(当然,可以自己在此基础之上进行扩展) 4 * @author Ly 5 * @date 2012/11/14 6 */ 7 ;Neter.namespace('Neter.Panel'); 8 9 /** 10 * @class 11 * @name Neter.Panel 12 * @param {Object} options 自定义配置信息 13 <pre> 14 options = { 15 width : 340, 16 height : 390, 17 defaultTag : 0, 18 container : document.body, // 面板容器,即将面板放于哪个元素之内,默认为body 19 bodies : [{ // 面板主体,至少包含一个元素 20 tag : '', 21 content : '' 22 }], 23 activeType : 'hover' // 激活标签的方式,hover/click,默认为hover,即鼠标滑过则切换 24 } 25 </pre> 26 */ 27 ;Neter.Panel = function(options) { 28 var _this = this; 29 30 this.defaults = { 31 width : 340, 32 height : 390, 33 defaultTag : 0, 34 container : document.body, // 面板容器,即将面板放于哪个元素之内,默认为body 35 bodies : [{ // 面板主体,至少包含一个元素 36 tag : '', 37 content : '' 38 }], 39 activeType : 'hover' // 激活标签的方式,hover/click,默认为hover,即鼠标滑过则切换 40 }; 41 42 Neter.apply(this.defaults, options, { 43 // 边框宽度 44 BORDER_WIDTH : 2 45 }); 46 47 this.handler = { 48 container : $(this.defaults.container), 49 panel : null, 50 tagBar : null, 51 viewContainer : null, 52 bodies : [], 53 previous : { 54 tag : null, 55 view : null 56 } 57 }; 58 59 this.defaults.container = null; 60 61 this.method = { 62 /** 63 * 创建插件框架 64 * @ignore 65 */ 66 create : function() { 67 var defaults = _this.defaults, 68 handler = _this.handler; 69 70 // 创建面板容器 71 handler.panel = $('<div></div>').addClass('neter-panel') 72 .appendTo(handler.container); 73 74 // 创建标签栏 75 handler.tagBar = $('<div></div>').addClass('neter-panel-tag-bar') 76 .appendTo(handler.panel); 77 78 // 创建主体 79 handler.viewContainer = $('<div></div>').addClass('neter-panel-view-container') 80 .appendTo(handler.panel); 81 82 return this; 83 }, 84 /** 85 * 初始化布局,当添加与删除标签时也需要调用此方法来重新进行页面布局初始化 86 * @ignore 87 */ 88 initLayout : function() { 89 var defaults = _this.defaults, 90 handler = _this.handler, 91 width = defaults.width - defaults.BORDER_WIDTH, 92 height = defaults.height - defaults.BORDER_WIDTH, 93 tagBar = handler.tagBar.css({ width : width }), 94 bodies = handler.bodies, 95 tagWidth = tagBar.width() / bodies.length, 96 viewContainer = handler.viewContainer; 97 98 handler.panel.css({ width : width, height : height }); 99 100 handler.viewContainer.css({ 101 width : width, 102 height : height - tagBar.outerHeight() 103 }); 104 105 $.each(bodies, function(index, value) { 106 value.tag.css({ borderRightWidth : '1px', width : tagWidth - 1 }); 107 }); 108 109 tagBar.find('.neter-panel-tag:last').css({ 110 borderRightWidth : 0, 111 width : tagWidth 112 }); 113 114 return this; 115 }, 116 /** 117 * 插入面板标签,如果参数都省略,则取this.defaults.bodies中的内容 118 * @ignore 119 * @param {Number} index 新加入的标签位置,值为-1时直接添加到最后 120 * @param {Object} options 新加入的内容,{ tag : '', content : '' } 121 */ 122 insert : function(index, options) { 123 var defaults = _this.defaults, 124 handler = _this.handler, 125 tagBar = handler.tagBar, 126 viewContainer = handler.viewContainer, 127 bodies = handler.bodies; 128 129 arguments.length && defaults.bodies.push(options); 130 131 $.each(defaults.bodies, function(i, options) { 132 var tag = $('<div></div>').addClass('neter-panel-tag').html(options.tag), 133 view = $('<div></div>').addClass('neter-panel-view').append(options.content); 134 135 if (typeof index === 'number') { 136 index = !~index ? bodies.length : index; 137 var tmp = tagBar.find('.neter-panel-tag')[index]; 138 139 tmp ? tag.insertBefore(tmp) : tagBar.append(tag); 140 141 (tmp = viewContainer.find('.neter-panel-view')[index]) 142 ? view.insertBefore(tmp) 143 : viewContainer.append(view); 144 145 bodies.splice(index, 0, { tag : tag, view : view, options : options }); 146 } else { 147 tagBar.append(tag); 148 viewContainer.append(view); 149 bodies.splice(i, 0, { tag : tag, view : view, options : options }); 150 } 151 }); 152 153 defaults.bodies = []; 154 155 return this; 156 }, 157 /** 158 * 更新标签内容 159 * @ignore 160 * @param {Number} index 要更新的标签所处的位置 161 * @param {Object} options 新标签的内容,{tag : '', content : '' } 162 */ 163 update : function(index, options) { 164 var defaults = _this.defaults, 165 handler = _this.handler, 166 dest = handler.bodies[index]; 167 168 if (dest) { 169 dest.tag.html(options.tag); 170 dest.view.empty().append(options.content); 171 dest.options = options; 172 } 173 174 return this; 175 }, 176 /** 177 * 删除指定的标签 178 * @ignore 179 * @param {Number} index 要删除的标签,可一次性删除多个标签 180 */ 181 remove : function(index) { 182 var defaults = _this.defaults, 183 handler = _this.handler, 184 dest = handler.bodies[index]; 185 186 if (dest) { 187 dest.tag.remove(); 188 dest.view.empty().remove(); 189 190 handler.bodies[index].tag = null; 191 handler.bodies[index].view = null; 192 handler.bodies.splice(index, 1); 193 } 194 195 return this; 196 }, 197 /** 198 * 给标签绑定切换事件,至于激活标签的方式由this.defaults.activeType来指定 199 * @ignore 200 */ 201 bindEvents : function() { 202 var defaults = _this.defaults, 203 handler = _this.handler, 204 current = handler.current; 205 206 handler.tagBar.on(defaults.activeType, 'div.neter-panel-tag', function() { 207 _this.method.active(this); 208 }); 209 210 return this; 211 }, 212 /** 213 * 激活标签 214 * @ignore 215 * @param {HTMLElement} current 要切换到的标签,是一个dom对象 216 */ 217 active : function(current) { 218 var defaults = _this.defaults, 219 handler = _this.handler, 220 previous = handler.previous; 221 222 previous.tag && previous.tag.removeClass('neter-panel-tag-current'); 223 previous.view && previous.view.hide(); 224 225 previous.tag = $(current).addClass('neter-panel-tag-current'); 226 227 $.each(handler.bodies, function(index, value) { 228 if (value.tag.get(0) === current) { 229 previous.view = value.view.show(); 230 } 231 }); 232 233 return this; 234 } 235 }; 236 }; 237 238 ;Neter.apply(Neter.Panel.prototype, { 239 /** 240 * 渲染插件 241 * @function 242 * @name Neter.Panel.prototype.render 243 * @return {Neter.Panel} 返回插件引用 244 */ 245 render : function() { 246 this.method.create().insert().initLayout().bindEvents(); 247 248 this.active(this.defaults.defaultTag); 249 250 return this; 251 }, 252 /** 253 * 获取Panel本身 254 * @function 255 * @name Neter.Panel.prototype.get 256 * @return {jQueryDOM} 返回插件DOM对象,经过jQuery封装。 257 */ 258 get : function() { 259 return this.handler.panel; 260 }, 261 /** 262 * 获取Panel视图 263 * @function 264 * @name Neter.Panel.prototype.getView 265 * @return {jQueryDOM} 返回插件视图DOM对象,经过jQuery封装。 266 */ 267 getView : function() { 268 return this.handler.viewContainer; 269 }, 270 /** 271 * 激活标签 272 * @function 273 * @name Neter.Panel.prototype.active 274 * @param {Number} [index=0] 要激活的标签,从0开始,默认为0 275 * @return {Neter.Panel} 返回插件引用 276 */ 277 active : function(index) { 278 this.method.active(this.handler.tagBar.find('>div').get(index || 0)); 279 280 return this; 281 }, 282 /** 283 * 插入一个标签,默认为放在最后 284 * @function 285 * @name Neter.Panel.prototype.insert 286 * @param {Number} index 新加入的标签位置,默认为最后 287 * @param {Object} options 新加入的内容,{ tag : '', content : '' } 288 * @return {Neter.Panel} 返回插件引用 289 */ 290 insert : function(index, options) { 291 // 当仅有一个options参数时 292 if (typeof index == 'object') { 293 index = -1; 294 options = index; 295 } 296 index = typeof index === 'number' ? index : -1; 297 298 if (!options || !options.hasOwnProperty('tag') || !options.hasOwnProperty('content')) { return this; } 299 300 this.method.insert(index, options).initLayout(); 301 302 return this; 303 }, 304 /** 305 * 更新标签内容 306 * @function 307 * @name Neter.Panel.prototype.update 308 * @param {Number} index 要更新的标签所处的位置 309 * @param {Object} options 新标签的内容,{tag : '', content : '' } 310 * @return {Neter.Panel} 返回插件引用 311 */ 312 update : function(index, options) { 313 if (index < 0 || !options || !options.hasOwnProperty('tag') || !options.hasOwnProperty('content')) { return this; } 314 315 this.method.update(index, options); 316 317 return this; 318 }, 319 /** 320 * 删除指定的标签 321 * @function 322 * @name Neter.Panel.prototype.remove 323 * @param {Number} index 要删除的标签,可一次性删除多个标签 324 * @return {Object} null 325 */ 326 remove : function(index) { 327 var _this = this; 328 $.each([].slice.call(arguments, 0).sort().reverse(), function(i, index) { 329 index > -1 && _this.method.remove(index); 330 }); 331 332 this.method.initLayout(); 333 334 // 删除标签后,切换到当前的标签,这样可以使得删除当前标签后不至于没有被显示的标签 335 this.active(this.defaults.defaultTag); 336 337 return null; 338 } 339 });
既然要仿网易的标签,那么操作上也要支持嘛,网易的登录框标签的切换是鼠标悬念,因此在这里也提供了一个配置参数activeType,默认就是hover。
下面就是仿制过程了。:)
1、先来看一下HTML结构:
1 <div id="center"> 2 <div id="container"> 3 <div id="login"></div> 4 </div> 5 </div> 6 7 <!-- #account块是用来初始化登录框的 --> 8 <div id="account"> 9 <div class="field-container"><input type="text" id="username" class="neter-input-gray" placeholder="邮箱帐号或手机号" /></div> 10 <div class="field-container"><input type="password" id="password" class="neter-input-gray" placeholder="密码" /></div> 11 <div class="field-container"> 12 <input type="checkbox" id="autoLoginOptions" autocomplete="off" /><label for="autoLoginOptions">最近两周自动登录</label> 13 </div> 14 <div class="field-container"> 15 <input id="loginBtn" type="button" value="登 录" class="neter-button-large neter-button-large-primary" /> 16 <input id="regBtn" type="button" value="注 册" class="neter-button-large reg" /> 17 </div> 18 </div>
以上这块代码就是整个登录页面的HTML代码。不过这里仅做了关于邮箱名登录的,至于手机的自己再搞定。
之所以div#center里面还有#container与#login,主要是为了展现一下网易邮箱登录页面中的刷新切换背景图片。下面的#account就是给panel中的账号登录准备的。
2、在js中实例化Neter.Panel。
1 ;$(function() { 2 var panel = new Neter.Panel({ 3 container : $('#login'), 4 bodies : [{ 5 tag : '邮箱账号登录', 6 content : $('#account') 7 }, { 8 tag : '手机号登录', 9 content : '手机号登录面板...' 10 }] 11 }).render(); 12 });
简单吧,这样就ok了。效果图如下: