经过这段时间的奋斗,jq tab的基础功能已经在昨天完成,不包括样式。 到目前为止,算是完成了v0.1版本。
接下来要完成的功能包括:
1.设置要显示的item属性(现在每个数据单元被称为json)
2.添加样式,让这个jq tab好看一点,不过应该还是最先实现公司那种样式。
下面贴上这个基础版本的代码,包括演示html:
js:
这里引用了jquery 1.72 min文件,这个就自己下载去吧。
View Code
1 /** 2 * @author Steven 3 先不考虑页面resize的情况,做好设置先 4 */ 5 6 /*-------------- Common Functions ------------------*/ 7 function stopPropagation(e) { 8 e = e || window.event; 9 if (e.stopPropagation) { //W3C阻止冒泡方法 10 e.stopPropagation(); 11 } else { 12 e.cancelBubble = true; //IE阻止冒泡方法 13 } 14 } 15 function removeEleFromArr(arr,obj) { 16 if (!arr) { 17 return false; 18 } 19 var removeIndexArr = []; 20 for (var i = 0; i < arr.length; i++) { 21 if (arr[i] == obj) { 22 removeIndexArr.push(i); 23 } 24 } 25 for (var i = removeIndexArr.length - 1; i >= 0; i--) { 26 arr.splice(removeIndexArr[i], 1); 27 } 28 } 29 30 function removeArrFromArr(arr1, arr2) { 31 if (!arr1 || !arr2) { 32 return false; 33 } 34 var copyArr1 = arrayCopy(arr1); 35 for (var i = 0; i < arr2.length; i++) { 36 removeEleFromArr(copyArr1, arr2[i]); 37 } 38 return copyArr1; 39 } 40 41 function arrayCopy(arr) { 42 if (!!arr && typeof arr.slice == "function") { 43 return arr.slice(); 44 } 45 else { 46 return null; 47 } 48 } 49 //window.onresize = function () { 50 // var me = jqTabMaster.prototype, 51 // config = me.config; 52 // config.isResize = true; 53 // alert("resize"); 54 //} 55 56 /*--------------------------------------------------*/ 57 var jqTabMaster = function () { 58 }; 59 jqTabMaster.prototype.config = { 60 containerID: "", 61 size: 0, 62 items: [], 63 allItemsIndex:[], 64 showItemsIndex: [], 65 notShowItemsIndex: [], 66 "", 67 callback: null 68 }; 69 70 71 jqTabMaster.prototype.packingItems = function (items) { 72 for (var i = 0; i < items.length; i++) { 73 items[i]["__jqTabIndex"] = i; 74 } 75 return items; 76 } 77 jqTabMaster.prototype.getContinueousIndexArr = function (startIndex, endIndex) { 78 var me = jqTabMaster.prototype, 79 config = me.config, 80 items = config.items, 81 max = items.length - 1, 82 startIndex = (typeof startIndex == 'undefined' || isNaN(startIndex)) ? 0 : startIndex, 83 endIndex = (typeof endIndex == 'undefined' || isNaN(endIndex)) ? max : endIndex, 84 indexArr = []; 85 86 for (var i = startIndex; i <= endIndex; i++) { 87 indexArr.push(i); 88 } 89 90 return indexArr; 91 } 92 // 传入需要显示的items的索引数组,根据该数组来显示 93 jqTabMaster.prototype.buildUl = function (indexArr, className) { 94 if (!indexArr) { 95 return false; 96 } 97 98 var me = jqTabMaster.prototype, 99 config = me.config, 100 items = config.items, 101 currentIndex, 102 callback = config.callback, 103 ul = document.createElement("ul"), 104 li; 105 106 for (var i = 0; i < indexArr.length; i++) { 107 currentIndex = indexArr[i]; 108 109 li = document.createElement("li"); 110 li.innerHTML = items[currentIndex].text; 111 li.index = items[currentIndex].__jqTabIndex; 112 ul.appendChild(li); 113 } 114 if (!!className) { 115 ul.className = className; 116 } 117 118 me.bindTabsClickEvent(ul, callback); 119 return ul; 120 } 121 122 jqTabMaster.prototype.bindTabsClickEvent = function (ul, callback) { 123 if (!!ul && ul.tagName == "UL" && typeof callback == "function") { 124 ul.onclick = function (ev) { 125 var ev = ev || window.event, 126 target = ev.target || ev.srcElement, 127 me = jqTabMaster.prototype, 128 config = me.config, 129 currentIndex; 130 if (target.tagName == "LI") { 131 currentIndex = target.index; 132 callback.call(this, config.items[currentIndex]); 133 } 134 else { 135 return false; 136 } 137 } 138 } 139 } 140 jqTabMaster.prototype.unbindTabsClickEvent = function (ul) { 141 if (!!ul && ul.tagName == "UL" && typeof callback == "function") { 142 ul.onclick = null; 143 } 144 } 145 146 jqTabMaster.prototype.initial = function (option) { 147 var me = jqTabMaster.prototype, 148 allItemsIndex = me.getContinueousIndexArr(0, option.items.length - 1); 149 150 this.config.containerID = option.containerID; 151 this.config.size = option.size; 152 this.config.items = me.packingItems(option.items); 153 this.config.allItemsIndex = allItemsIndex; 154 this.config.width = option.width; 155 this.config.callback = option.callback; 156 157 var container = $("#" + this.config.containerID), 158 iLen, 159 ul, 160 needShowIndexArr = [], 161 showItemsIndex = [], 162 moreItemsCount = 0; 163 164 container.hide(); 165 container.addClass('container'); 166 container.css('width', this.config.width); 167 container.css('overflow', 'hidden'); 168 /*增加”更多“按钮,默认为disabled*/ 169 container.css({ 'width': '-=40px' }); 170 var moreBtn = $("<div id='divMoreBtn' class='no-more'><span>更多</span></div>"); 171 moreBtn.insertAfter(container); 172 173 /* 添加可显示的tabs */ 174 iLen = Math.min(this.config.items.length, this.config.size); 175 needShowIndexArr = me.getContinueousIndexArr(0, iLen - 1); 176 ul = me.buildUl(needShowIndexArr); 177 container.append(ul); 178 179 container.show(); 180 /* Get Real Size 181 由于这里要调用offsetTop来判断是否超出边界,调用offset的参数会比较耗性能, 182 现在是用从头开始便利数组获取的,之后可以使用二分法来进行快速查找,以提高性能。 183 * */ 184 var displayInfo = me.getRealDisplayInfo(); 185 this.config.showItemsIndex = displayInfo.showItemsIndex; 186 this.config.notShowItemsIndex = displayInfo.notShowItemsIndex; 187 188 /* 当tab没有完全显示时 */ 189 if ((this.config.items.length - this.config.showItemsIndex.length) > 0) { 190 $("#divMoreBtn").toggleClass('no-more'); 191 /*绑定显示更多tab事件*/ 192 $("#divMoreBtn").bind('click', function (ev) { 193 me.showMoreTabs(ev); 194 }); 195 } 196 } 197 198 jqTabMaster.prototype.getRealDisplayInfo = function () { 199 var me = jqTabMaster.prototype, 200 config = me.config, 201 items = config.items, 202 lis = $("#" + config.containerID + " ul li"), 203 showItemsIndex = [], 204 allItemsIndex = me.config.allItemsIndex, 205 notShowItemsIndex = [], 206 originOffsetTop = null; 207 208 209 lis.each(function (i) { 210 if (originOffsetTop == null || originOffsetTop == this.offsetTop) { 211 originOffsetTop = this.offsetTop; 212 showItemsIndex.push(this.index); 213 } 214 else if (this.offsetTop != originOffsetTop) { 215 return false; 216 } 217 }); 218 219 notShowItemsIndex = removeArrFromArr(allItemsIndex, showItemsIndex); 220 221 return { showItemsIndex: showItemsIndex, notShowItemsIndex: notShowItemsIndex }; 222 } 223 224 /* 225 * params: 226 * ev: window.event 227 * me: jqTabMaster.prototype 228 */ 229 jqTabMaster.prototype.showMoreTabs = function (ev) { 230 var me = jqTabMaster.prototype, 231 config = me.config, 232 ev = ev || window.event, 233 target = $("#divMoreBtn"), 234 /* 需要显示的items索引数组 */ 235 needShowIndexArr = [], 236 /* 容器的offset Left、top和尺寸信息 */ 237 offset, offsetLeft, offsetTop, 238 width, height, 239 /* 弹出层的绝对坐标和宽度 */ 240 moreX, moreY, 241 moreWidth, 242 /* 空隙值 */ 243 spaceX = 10, spaceY = 5, 244 /* more tabs*/ 245 ul; 246 247 offset = target.offset(); 248 offsetLeft = offset.left; 249 offsetTop = offset.top; 250 width = parseInt(target.css('width')); 251 height = parseInt(target.css('height')); 252 253 254 moreWidth = 200; 255 moreX = offsetLeft + width - moreWidth; 256 moreY = offsetTop + height + spaceY; 257 /* 这样计算能让弹出层的右边和more按钮的右边对齐*/ 258 moreX < 0 && (moreWidth += (moreX - spaceX)) && (moreX = spaceX); 259 260 261 var moreDiv = $("<div>"), 262 moreTabsContainer = $("<div>"), 263 moreSetting = $("<div>"); 264 moreDiv.attr('id', 'divMore'); 265 moreDiv.addClass("more-div"); 266 moreDiv.css({ 267 'width': moreWidth + 'px', 268 'left': moreX + 'px', 269 'top': moreY + 'px' 270 }); 271 272 moreTabsContainer.addClass('more-tabs-container'); 273 274 needShowIndexArr = this.config.notShowItemsIndex; 275 ul = me.buildUl(needShowIndexArr); 276 moreTabsContainer.append(ul); 277 278 moreSetting.addClass('more-setting'); 279 moreSetting.html('设置'); 280 moreSetting.toggle( 281 function () { 282 this.innerHTML = "保存"; 283 var uls = $("#divMore ul,#" + config.containerID + " ul"); 284 for (var i = 0; i < uls.length; i++) { 285 uls[i].onclick = null; 286 me.buildSettingBtns(uls[i]); 287 } 288 me.unbindEventsWhenSettingTabs(); 289 }, function () { 290 this.innerHTML = "设置"; 291 var uls = $("#divMore ul,#" + config.containerID + " ul"); 292 uls.each(function (i) { 293 me.bindTabsClickEvent(uls[i], me.config.callback); 294 me.removeSettingBtns(uls[i]); 295 }); 296 me.bindEventsWhenMoreTabsShow(); 297 }); 298 299 moreDiv.append(moreTabsContainer).append(moreSetting); 300 301 $(document.body).append(moreDiv); 302 stopPropagation(ev); 303 304 /* more tabs显示后,重新绑定事件*/ 305 me.bindEventsWhenMoreTabsShow(); 306 307 /*阻止选中文本*/ 308 me.bankSelect(); 309 } 310 jqTabMaster.prototype.hideMoreTabs = function () { 311 $("#divMore").remove(); 312 313 /* more tabs隐藏后重新绑定事件*/ 314 jqTabMaster.prototype.bindEventsWhenMoreTabsHide(); 315 } 316 jqTabMaster.prototype.unbindEventsWhenSettingTabs = function () { 317 $(document).unbind('click'); 318 $("#divMoreBtn").unbind('click'); 319 } 320 jqTabMaster.prototype.bindEventsWhenMoreTabsHide = function () { 321 $(document).unbind('click'); 322 $("#divMoreBtn").unbind('click') 323 .bind('click', function () { 324 jqTabMaster.prototype.showMoreTabs(); 325 }); 326 } 327 jqTabMaster.prototype.bindEventsWhenMoreTabsShow = function () { 328 /* 刷新more按钮的click事件。再点击click时也隐藏more*/ 329 $("#divMoreBtn").unbind('click') 330 .bind('click', function () { 331 jqTabMaster.prototype.hideMoreTabs(); 332 }); ; 333 334 /* 点击more tabs容器外地地方时,隐藏more tabs*/ 335 $(document).unbind('click').bind('click', function (ev) { 336 var me = jqTabMaster.prototype, 337 ev = ev || window.event, 338 target = ev.target || ev.srcElement, 339 isDivMore = target.id == "divMore", 340 isBelong2DivMore = $(target).parents("#divMore").length > 0; 341 342 if (!isBelong2DivMore) { 343 me.hideMoreTabs(); 344 } 345 else { 346 stopPropagation(ev); 347 return false; 348 } 349 }); 350 } 351 352 jqTabMaster.prototype.buildSettingBtns = function (ul) { 353 if (!ul || ul.tagName != "UL") { 354 return false; 355 } 356 357 var me = jqTabMaster.prototype, 358 config = me.config, 359 isBelong2DivMore = $(ul).parents("#divMore").length > 0, 360 isBelong2TabsContainer = $(ul).parents("#" + config.containerID).length > 0, 361 optType = isBelong2DivMore ? "add" : "remove", 362 lis = ul.getElementsByTagName("li"), 363 li, 364 iLen = lis.length, 365 spnOpt; 366 367 for (var i = 0; i < iLen; i++) { 368 li = lis[i]; 369 (function (li, me) { 370 me.bindSettingOpt(li, optType); 371 })(li, me); 372 } 373 } 374 jqTabMaster.prototype.bindSettingOpt = function (li, optType) { 375 var me = jqTabMaster.prototype, 376 spnOpt = document.createElement("span"), 377 className = "opt"; 378 379 spnOpt.className = className; 380 spnOpt.innerHTML = optType == "add" ? "+" : "-"; 381 382 if (optType == "add") { 383 spnOpt.onclick = function () { 384 me.addTab($(li)[0]); 385 } 386 } 387 else { 388 spnOpt.onclick = function () { 389 me.removeTab($(li)[0]); 390 } 391 }; 392 $(li).append(spnOpt); 393 } 394 395 jqTabMaster.prototype.removeSettingBtns = function (ul) { 396 if (!ul || ul.tagName != "UL") { 397 return false; 398 } 399 400 var spnOpts = $(ul).find("li span"); 401 spnOpts.remove(); 402 } 403 404 jqTabMaster.prototype.addTab = function (li) { 405 var me = jqTabMaster.prototype, 406 config = me.config, 407 maxSize = config.size, 408 realSize = config.showItemsIndex.length; 409 410 if (realSize >= maxSize) { 411 return false; 412 } 413 414 var showItemsIndex = config.showItemsIndex, 415 notShowItemsIndex = config.notShowItemsIndex, 416 items = config.items, 417 showedTabs = $("#" + config.containerID + " ul li"), 418 lastShowTab = realSize > 0 ? showedTabs.eq(realSize - 1) : null, 419 moreTabsUl = $("#divMore ul"), 420 moreTabsLi = moreTabsUl.find("li"), 421 newLi = document.createElement("li"), 422 currentLiIndex = li.index, 423 currentItem = items[currentLiIndex], 424 addSuccessful = false; 425 426 /* Build New Li need to insert */ 427 newLi.index = currentLiIndex; 428 newLi.innerHTML = currentItem.text; 429 newLi = $(newLi); 430 me.bindSettingOpt(newLi, "remove"); 431 432 if (!!lastShowTab) { 433 newLi.insertAfter(lastShowTab); 434 } 435 else { 436 $("#" + config.containerID + " ul").append(newLi); 437 } 438 addSuccessful = !lastShowTab || lastShowTab.offset().top == newLi.offset().top; 439 if (!addSuccessful) { 440 newLi.remove(); 441 } 442 else { 443 showItemsIndex.push(currentLiIndex); 444 removeEleFromArr(notShowItemsIndex, currentLiIndex); 445 $(li).remove(); 446 } 447 } 448 jqTabMaster.prototype.removeTab = function (li) { 449 var me = jqTabMaster.prototype, 450 config = me.config, 451 items = config.items, 452 showItemsIndex = config.showItemsIndex, 453 notShowItemsIndex = config.notShowItemsIndex, 454 realSize = config.showItemsIndex.length, 455 456 showedTabs = $("#divMore ul li"), 457 lastShowTab = showedTabs.eq(notShowItemsIndex.length - 1), 458 currentLiIndex = li.index, 459 currentItem = items[currentLiIndex], 460 newLi = document.createElement("li"), 461 move2Index = 0; 462 463 newLi.index = currentLiIndex; 464 newLi.innerHTML = currentItem.text; 465 newLi = $(newLi); 466 me.bindSettingOpt(newLi, "add"); 467 468 $(li).remove(); 469 removeEleFromArr(showItemsIndex, currentLiIndex); 470 471 newLi.insertAfter(lastShowTab); 472 notShowItemsIndex.push(currentLiIndex); 473 } 474 475 jqTabMaster.prototype.getNotShowItemsIndex = function () { 476 477 } 478 jqTabMaster.prototype.getItemRank = function (num, arr) { 479 var me = jqTabMaster.prototype, 480 config = me.config, 481 showItemsIndex = config.showItemsIndex, 482 notShowItemsIndex = config.notShowItemsIndex, 483 items = config.items, 484 allItemsIndex = []; 485 486 for (var i = 0; i < items.length; i++) { 487 488 } 489 } 490 491 /* 禁止文本被选中 */ 492 jqTabMaster.prototype.bankSelect = function () { 493 $("#divMore,#divMoreBtn,#" + jqTabMaster.prototype.config.containerID).bind('selectstart', function () { 494 return false; 495 }) 496 .css({ 497 '-moz-user-select': 'none', 498 '-webkit-user-select': 'none', 499 'user-select': 'none' 500 }); 501 }
css:
View Code
1 div{ 2 border:solid 1px #000; 3 } 4 5 .container{ 6 border:solid 1px #000; 7 display:block; 8 float:left; 9 height:50px; 10 margin:0px; 11 padding:0px; 12 } 13 14 .container ul{ 15 float:left; 16 list-style:none; 17 margin:12px 5px 5px 5px; 18 padding:0px; 19 text-align: left; 20 } 21 .container ul li{ 22 border:solid 1px #f00; 23 display:inline-block; 24 margin: 5px 5px 105px 5px; 25 overflow:visible; 26 text-align:center; 27 width:40px; 28 29 } 30 31 #divMoreBtn{ 32 border:solid 1px #000; 33 display:block; 34 float:left; 35 height:50px; 36 margin:0px 0px 0px 5px; 37 vertical-align:bottom; 38 width:35px; 39 } 40 41 #divMoreBtn span{ 42 display:block; 43 line-height:60px; 44 text-align:center; 45 vertical-align:middle; 46 } 47 48 .no-more{ 49 background-color#111; 50 color:#ddd; 51 } 52 .more{ 53 background-color:#fff; 54 color:#000; 55 } 56 .more-div{ 57 height:420px; 58 position:absolute; 59 } 60 .more-div .more-tabs-container{ 61 height:360px; 62 margin:5px; 63 overflow-x:none; 64 overflow-y:auto; 65 position:relative; 66 } 67 .more-div .more-setting{ 68 color:#f00; 69 display:block; 70 font-weight:bold; 71 line-height:40px; 72 margin:8px 5px; 73 height:40px; 74 text-align:center; 75 position:relative; 76 vertical-align: bottom; 77 } 78 79 .more-tabs-container ul{ 80 margin:0px; 81 padding: 0px; 82 clear:both; 83 float:left; 84 list-style:none; 85 } 86 87 .more-tabs-container ul li{ 88 border:solid 1px #DDDDDD; 89 display:block; 90 list-style:none; 91 line-height:30px; 92 height:30px; 93 margin:2px 10px 5px 10px; 94 text-align:center; 95 vertical-align:middle; 96 width:120px; 97 } 98 .opt 99 { 100 border:solid #333333 1px; 101 color:red; 102 cursor:pointer; 103 display:inline-block; 104 float:right; 105 font-weight:bold; 106 height:13px; 107 margin:2px 1px; 108 line-height:13px; 109 vertical-align:middle; 110 width:10px; 111 }
html:
View Code
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 2 "http://www.w3.org/TR/html4/loose.dtd"> 3 <html xmlns="http://www.w3.org/1999/xhtml"> 4 <head> 5 <meta http-equiv="Content-Type" content="text/html; charset=gbk2312" /> 6 <title>Jq Tab Demo</title> 7 <link href="css/jqTab.css" rel="stylesheet" /> 8 </head> 9 <body> 10 <h1>jq Tab</h1> 11 12 13 <div id="div1"> 14 </div> 15 <script type="text/javascript" src="js/jquery-1.7.2.min.js"></script> 16 <script type="text/javascript" src="js/jqTab.js"></script> 17 <script type="text/javascript"> 18 var jqTab = new jqTabMaster(); 19 jqTab.initial({ 20 containerID: "div1", 21 size: 6, 22 items: [ 23 { value: "1", text: "1" }, 24 { value: "2", text: "2" }, 25 { value: "3", text: "3" }, 26 { value: "4", text: "4" }, 27 { value: "5", text: "5" }, 28 { value: "6", text: "6" }, 29 { value: "7", text: "7" }, 30 { value: "8", text: "8" }, 31 { value: "9", text: "9" }, 32 { value: "10", text: "10" } 33 ], 34 "50%", 35 callback: callback 36 }); 37 function callback(data) { 38 alert(data.__jqTabIndex); 39 } 40 41 </script> 42 </body> 43 </html>
这篇就这么多。 补2012.05.25