以BootStrap Tab為例寫一個前端組件
介紹
本文以Bootstrap標簽頁組件為例,介紹如何編寫或者封裝一個前端組件,以下是實現(xiàn)效果:

原生的Bootstrap-tab組件主要有html,css組成,開發(fā)者使用時,需要寫很多代碼,不易于使用,對bootstrap-tab封裝后,可以更方便地使用,同時提供關閉、增加tab頁、指定當前選中頁、即使加載等功能,這樣組件可以適配更多的場景。
原生bootstrap-tab組件使用可參考https://www.runoob.com/bootstrap/bootstrap-tab-plugin.html
其中官網(wǎng)一段實例代碼是:
<ul id="myTab" class="nav nav-tabs">
<li class="active"><a href="#home" rel="external nofollow" data-toggle="tab">
菜鳥教程</a>
</li>
<li><a href="#ios" rel="external nofollow" data-toggle="tab">iOS</a></li>
<li class="dropdown">
<a href="#" rel="external nofollow" id="myTabDrop1" class="dropdown-toggle"
data-toggle="dropdown">Java <b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="myTabDrop1">
<li><a href="#jmeter" rel="external nofollow" tabindex="-1" data-toggle="tab">
jmeter</a>
</li>
<li><a href="#ejb" rel="external nofollow" tabindex="-1" data-toggle="tab">
ejb</a>
</li>
</ul>
</li>
</ul>
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade in active" id="home">
<p>菜鳥教程是一個提供最新的web技術站點,本站免費提供了建站相關的技術文檔,幫助廣大web技術愛好者快速入門并建立自己的網(wǎng)站。菜鳥先飛早入行——學的不僅是技術,更是夢想。</p>
</div>
<div class="tab-pane fade" id="ios">
<p>iOS 是一個由蘋果公司開發(fā)和發(fā)布的手機操作系統(tǒng)。最初是于 2007 年首次發(fā)布 iPhone、iPod Touch 和 Apple
TV。iOS 派生自 OS X,它們共享 Darwin 基礎。OS X 操作系統(tǒng)是用在蘋果電腦上,iOS 是蘋果的移動版本。</p>
</div>
<div class="tab-pane fade" id="jmeter">
<p>jMeter 是一款開源的測試軟件。它是 100% 純 Java 應用程序,用于負載和性能測試。</p>
</div>
<div class="tab-pane fade" id="ejb">
<p>Enterprise Java Beans(EJB)是一個創(chuàng)建高度可擴展性和強大企業(yè)級應用程序的開發(fā)架構,部署在兼容應用程序服務器(比如 JBOSS、Web Logic 等)的 J2EE 上。
</p>
</div>
</div>
<script>
$(function () {
$('#myTab li:eq(1) a').tab('show');
});
</script>
那么如何封裝或者開發(fā)一個組件呢?
組件開發(fā)步驟
Step1:結構化靜態(tài)代碼,梳理核心的問題
在組件開發(fā)流程中,可能拿到前端設計的靜態(tài)代碼(html+css的組合),這時候要拆解代碼結構,使得結構能夠模板化。其次梳理核心問題,bootstrap-tab組件化之后,應該能夠動態(tài)加載tab內(nèi)容,這個可以通過jquery.load方法解決,這樣可以做到主頁面和子頁面解耦。
讀懂了靜態(tài)代碼,理解了結構和核心問題就可以寫代碼了,首先搭建組件的架子。
Step2:組件骨架
/**
* Bootstrap tab組件封裝
* @author billjiang qq:475572229
* @created 2017/7/24
*
*/
(function ($, window, document, undefined) {
'use strict';
var pluginName = 'tabs';
//入口方法
$.fn[pluginName] = function (options) {
var self = $(this);
if (this == null)
return null;
var data = this.data(pluginName);
if (!data) {
data = new BaseTab(this, options);
self.data(pluginName, data);
}
return data;
};
var BaseTab = function (element, options) {
this.$element = $(element);
this.options = $.extend(true, {}, this.default, options);
this.init();
}
//默認配置
BaseTab.prototype.default = {
}
//結構模板
BaseTab.prototype.template = {
}
//初始化
BaseTab.prototype.init = function () {
}
})(jQuery, window, document)
搭建了以上組件的骨架,并對組件命名為tabs,這樣就可以通過$("#tab-container").data("tabs")獲取組價的方法和屬性。在入口方法中,會將初始化后的對象緩存到頁面html中,這樣可以避免重復創(chuàng)建對象。一些經(jīng)典的開源前端組件都是這樣寫法,比如Bootstrap-treeview,大家有時間可以看看它的源碼。
以上的寫法使用原型鏈的寫法。定義了默認配置,結構模板,初始化入口。
編寫代碼
在組件的代碼骨架里,填充模板代碼,這里使用占位符{0},{1}等表示外部傳入的變量,然后在init方法中校驗外部傳入數(shù)據(jù)的合法性,然后構建組件,并且綁定關閉事件、點擊事件。
在開發(fā)前端組件的時候,往往不知道默認參數(shù)應該有什么,可以在開發(fā)的時候,用到就加上去,這里加了兩個默認參數(shù),一個showIndex是默認顯示的tab頁索引,一個loadAlltab是否一次性把所有的頁面數(shù)據(jù)加載完。
具體的邏輯請看下面的代碼:
//默認配置
BaseTab.prototype.default = {
showIndex: 0, //默認顯示頁索引
loadAll: true,//true=一次全部加在頁面,false=只加在showIndex指定的頁面,其他點擊時加載,提高響應速度
}
//結構模板
BaseTab.prototype.template = {
ul_nav: '<ul class="nav nav-tabs"></ul>',
ul_li: '<li><a href="#{0}" rel="external nofollow" data-toggle="tab"><span>{1}</span></a></li>',
ul_li_close: '<i class="fa fa-remove closeable" title="關閉"></i>',
div_content: '<div class="tab-content"></div>',
div_content_panel: '<div class="tab-pane fade" id="{0}"></div>'
}
//初始化
BaseTab.prototype.init = function () {
if (!this.options.data || this.options.data.length == 0) {
console.error("請指定tab頁數(shù)據(jù)");
return;
}
//當前顯示的顯示的頁面是否超出索引
if (this.options.showIndex < 0 || this.options.showIndex > this.options.data.length - 1) {
console.error("showIndex超出了范圍");
//指定為默認值
this.options.showIndex = this.default.showIndex;
}
//清除原來的tab頁
this.$element.html("");
this.builder(this.options.data);
}
//使用模板搭建頁面結構
BaseTab.prototype.builder = function (data) {
var ul_nav = $(this.template.ul_nav);
var div_content = $(this.template.div_content);
for (var i = 0; i < data.length; i++) {
//nav-tab
var ul_li = $(this.template.ul_li.format(data[i].id, data[i].text));
//如果可關閉,插入關閉圖標,并綁定關閉事件
if (data[i].closeable) {
var ul_li_close = $(this.template.ul_li_close);
ul_li.find("a").append(ul_li_close);
ul_li.find("a").append(" ");
}
ul_nav.append(ul_li);
//div-content
var div_content_panel = $(this.template.div_content_panel.format(data[i].id));
div_content.append(div_content_panel);
}
this.$element.append(ul_nav);
this.$element.append(div_content);
this.loadData();
this.$element.find(".nav-tabs li:eq(" + this.options.showIndex + ") a").tab("show");
}
BaseTab.prototype.loadData = function () {
var self = this;
//tab點擊即加載事件
//設置一個值,記錄每個tab頁是否加載過
this.stateObj = {};
var data = this.options.data;
//如果是當前頁或者配置了一次性全部加載,否則點擊tab頁時加載
for (var i = 0; i < data.length; i++) {
if (this.options.loadAll || this.options.showIndex == i) {
if (data[i].url) {
$("#" + data[i].id).load(data[i].url);
this.stateObj[data[i].id] = true;
} else {
console.error("id=" + data[i].id + "的tab頁未指定url");
this.stateObj[data[i].id] = false;
}
} else {
this.stateObj[data[i].id] = false;
(function (id, url) {
self.$element.find(".nav-tabs a[href='#" + id + "']").on('show.bs.tab', function () {
if (!self.stateObj[id]) {
$("#" + id).load(url);
self.stateObj[id] = true;
}
});
}(data[i].id, data[i].url))
}
}
//關閉tab事件
this.$element.find(".nav-tabs li a i.closeable").each(function (index, item) {
$(item).click(function () {
var href = $(this).parents("a").attr("href").substr(1);
$(this).parents("li").remove();
$("#" + href).parent().remove();
})
});
}
測試
編寫一個前端界面,測試組件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tab組件</title>
</head>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" rel="external nofollow" >
<link rel="stylesheet" href="font-awesome/css/font-awesome.min.css" rel="external nofollow" >
<link rel="stylesheet" href="../css/bootstrap-tab.css" rel="external nofollow" >
<body>
<div id="tabContainer"></div>
</body>
<script src="jquery/jquery-1.8.3.min.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>
<script src="../js/bootstrap-tab.js"></script>
<script>
$("#tabContainer").tabs({
data: [{
id: 'home',
text: '百度一下',
url: "tab_first.html",
closeable:true
}, {
id: 'admineap',
text: 'AdminEAP',
url: "tab_second.html"
}, {
id: 'edit',
text: '編輯人員',
url: "tab_content.html",
closeable:true
}],
showIndex:1,
loadAll:false
})
</script>
</html>
通過配置各種參數(shù),看看組件是否滿足了預期的要求。

組件在使用的過程中還會遇到各種問題,或者各種需求,比如新增一個tab頁面,比如獲取當前tab的ID或index,這是可以在代碼中按需擴展。
//新增一個tab頁
BaseTab.prototype.addTab=function (obj) {
//nav-tab
var ul_li = $(this.template.ul_li.format(obj.id, obj.text));
//如果可關閉,插入關閉圖標,并綁定關閉事件
if (obj.closeable) {
var ul_li_close = $(this.template.ul_li_close);
ul_li.find("a").append(ul_li_close);
ul_li.find("a").append(" ");
}
this.$element.find(".nav-tabs").append(ul_li);
//div-content
var div_content_panel = $(this.template.div_content_panel.format(obj.id));
this.$element.find(".tab-content").append(div_content_panel);
$("#" + obj.id).load(obj.url);
this.stateObj[obj.id] = true;
if(obj.closeable){
this.$element.find(".nav-tabs li a[href='#" + obj.id + "'] i.closeable").click(function () {
var href = $(this).parents("a").attr("href").substr(1);
$(this).parents("li").remove();
$("#" + href).parent().remove();
})
}
this.$element.find(".nav-tabs a[href='#" + obj.id + "']").tab("show");
}
//根據(jù)id設置活動tab頁
BaseTab.prototype.showTab=function (tabId) {
this.$element.find(".nav-tabs li a[href='#" + tabId + "']").tab("show");
}
//獲取當前活動tab頁的ID
BaseTab.prototype.getCurrentTabId=function () {
var href=this.$element.find(".nav-tabs li.active a").attr("href");
href=href.substring(1);
return href;
}
更完善的bootrap-tab版本已經(jīng)開源,詳見我的Github地址:
bootstrap-tab:https://github.com/bill1012/bootstrap-tab
總結
以上所述是小編給大家介紹的以BootStrap Tab為例寫一個前端組件,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關文章
理解Javascript_01_理解內(nèi)存分配原理分析
在正式開始之前,我想先說兩句,理解javascript系列博文是通過帶領大家分析javascript執(zhí)行時的內(nèi)存分配情況,來解釋javascript原理,具體會涵蓋javascript預加載,閉包原理,面象對象,執(zhí)行模型,對象模型...,文章的視角很特別,也非常深入,希望大家能接受這種形式,并提供寶貴意見。2010-10-10
JS小功能(offsetLeft實現(xiàn)圖片滾動效果)實例代碼
這篇文章主要介紹了offsetLeft實現(xiàn)圖片滾動效果實例代碼,有需要的朋友可以參考一下2013-11-11
微信小程序監(jiān)聽用戶登錄事件的實現(xiàn)方法
這篇文章主要介紹了微信小程序監(jiān)聽用戶登錄事件的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-11-11
JavaScript三種方法解決約瑟夫環(huán)問題的方法
約瑟夫環(huán)問題又稱約瑟夫問題或丟手絹問題,是一道經(jīng)典的算法問題,本篇將以循環(huán)鏈表、有序數(shù)組、數(shù)學遞歸三種方式來解決約瑟夫環(huán)問題。感興趣的可以了解一下2021-09-09

