以BootStrap Tab为例写一个前端组件


Posted in Javascript onJuly 25, 2017

介绍

本文以Bootstrap标签页组件为例,介绍如何编写或者封装一个前端组件,以下是实现效果:

以BootStrap Tab为例写一个前端组件

原生的Bootstrap-tab组件主要有html,css组成,开发者使用时,需要写很多代码,不易于使用,对bootstrap-tab封装后,可以更方便地使用,同时提供关闭、增加tab页、指定当前选中页、即使加载等功能,这样组件可以适配更多的场景。

原生bootstrap-tab组件使用可参考https://www.runoob.com/bootstrap/bootstrap-tab-plugin.html

其中官网一段实例代码是:

<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技术爱好者快速入门并建立自己的网站。菜鸟先飞早入行——学的不仅是技术,更是梦想。</p>
  </div>
  <div class="tab-pane fade" id="ios">
    <p>iOS 是一个由苹果公司开发和发布的手机操作系统。最初是于 2007 年首次发布 iPhone、iPod Touch 和 Apple
      TV。iOS 派生自 OS X,它们共享 Darwin 基础。OS X 操作系统是用在苹果电脑上,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)是一个创建高度可扩展性和强大企业级应用程序的开发架构,部署在兼容应用程序服务器(比如 JBOSS、Web Logic 等)的 J2EE 上。
    </p>
  </div>
</div>
<script>
  $(function () {
    $('#myTab li:eq(1) a').tab('show');
  });
</script>

那么如何封装或者开发一个组件呢?

组件开发步骤

Step1:结构化静态代码,梳理核心的问题

在组件开发流程中,可能拿到前端设计的静态代码(html+css的组合),这时候要拆解代码结构,使得结构能够模板化。其次梳理核心问题,bootstrap-tab组件化之后,应该能够动态加载tab内容,这个可以通过jquery.load方法解决,这样可以做到主页面和子页面解耦。

读懂了静态代码,理解了结构和核心问题就可以写代码了,首先搭建组件的架子。

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中,这样可以避免重复创建对象。一些经典的开源前端组件都是这样写法,比如Bootstrap-treeview,大家有时间可以看看它的源码。

以上的写法使用原型链的写法。定义了默认配置,结构模板,初始化入口。

编写代码

在组件的代码骨架里,填充模板代码,这里使用占位符{0},{1}等表示外部传入的变量,然后在init方法中校验外部传入数据的合法性,然后构建组件,并且绑定关闭事件、点击事件。

在开发前端组件的时候,往往不知道默认参数应该有什么,可以在开发的时候,用到就加上去,这里加了两个默认参数,一个showIndex是默认显示的tab页索引,一个loadAlltab是否一次性把所有的页面数据加载完。

具体的逻辑请看下面的代码: 

//默认配置
  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页数据");
      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>

通过配置各种参数,看看组件是否满足了预期的要求。

以BootStrap Tab为例写一个前端组件

扩展

组件在使用的过程中还会遇到各种问题,或者各种需求,比如新增一个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");
  }
  //根据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版本已经开源,详见我的Github地址:

bootstrap-tab:https://github.com/bill1012/bootstrap-tab

总结

以上所述是小编给大家介绍的以BootStrap Tab为例写一个前端组件,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
js给onclick事件赋值,动态传参数实例解说
Mar 28 Javascript
JQueryEasyUI Layout布局框架的使用
Apr 08 Javascript
node.js中的定时器nextTick()和setImmediate()区别分析
Nov 26 Javascript
JavaScript中的this关键字使用方法总结
Mar 13 Javascript
JS实现的数组全排列输出算法
Mar 19 Javascript
JavaScript实现的链表数据结构实例
Apr 02 Javascript
javascript类型系统 Array对象学习笔记
Jan 09 Javascript
JS读取XML文件数据并以table形式显示数据的方法(兼容IE与火狐)
Jun 02 Javascript
JS自定义函数对web前端上传的文件进行类型大小判断
Oct 19 Javascript
JS实现向iframe中表单传值的方法
Mar 24 Javascript
详解vue-router 动态路由下子页面多页共活的解决方案
Dec 22 Javascript
Array.filter中如何正确使用Async
Nov 04 Javascript
基于Bootstrap的标签页组件及bootstrap-tab使用说明
Jul 25 #Javascript
js事件委托和事件代理案例分享
Jul 25 #Javascript
基于JavaScript实现多级菜单效果
Jul 25 #Javascript
简单谈谈React中的路由系统
Jul 25 #Javascript
老生常谈js中的MVC
Jul 25 #Javascript
教你5分钟学会用requirejs(必看篇)
Jul 25 #Javascript
浅谈Vue.js 1.x 和 2.x 实例的生命周期
Jul 25 #Javascript
You might like
PHP5 安装方法
2006/10/09 PHP
PHP实现的增强性mhash函数
2015/05/27 PHP
Javascript 写的简单进度条控件
2008/01/22 Javascript
javascript在事件监听方面的兼容性小结
2010/04/07 Javascript
FireFox下XML对象转化成字符串的解决方法
2011/12/09 Javascript
Javascript 加载和执行-性能提高篇
2012/12/28 Javascript
js实现上传文件添加和删除文件选择框
2016/10/24 Javascript
jQuery实现6位数字密码输入框
2016/12/29 Javascript
Vuejs 用$emit与$on来进行兄弟组件之间的数据传输通信
2017/02/23 Javascript
详解Vue 事件驱动和依赖追踪
2017/04/22 Javascript
微信小程序 检查接口状态实例详解
2017/06/23 Javascript
vue实现PC端录音功能的实例代码
2019/06/05 Javascript
vue监听用户输入和点击功能
2019/09/27 Javascript
python函数返回多个值的示例方法
2013/12/04 Python
详解Python的Twisted框架中reactor事件管理器的用法
2016/05/25 Python
Python根据已知邻接矩阵绘制无向图操作示例
2018/06/23 Python
Python关于excel和shp的使用在matplotlib
2019/01/03 Python
解决Pycharm界面的子窗口不见了的问题
2019/01/17 Python
Python3 tkinter 实现文件读取及保存功能
2019/09/12 Python
Python使用grequests(gevent+requests)并发发送请求过程解析
2019/09/25 Python
美国网上花店:JustFlowers
2017/02/12 全球购物
德国运动鞋网上商店:Afew Store
2018/01/05 全球购物
蔻驰西班牙官网:COACH西班牙
2019/01/16 全球购物
路德维希•贝克(LUDWIG BECK)中文官网:德国大型美妆百货
2020/09/19 全球购物
招商业务员岗位职责
2013/12/16 职场文书
违反学校规定检讨书
2014/01/18 职场文书
标准毕业生自荐信
2014/06/24 职场文书
三八节标语
2014/06/27 职场文书
分居协议书范本(律师见证版)
2014/11/26 职场文书
我们的节日中秋节活动总结
2015/03/23 职场文书
企业法人代表证明书
2015/06/18 职场文书
2016年最美孝心少年事迹材料
2016/02/26 职场文书
PHP 技巧 * SVG 保存为图片(分享图生成)
2021/04/02 PHP
TV动画《史上最强大魔王转生为村民A》番宣CM公布
2022/04/01 日漫
《LOL》“克隆大作战”久违归来 幻灵战队皮肤上线
2022/04/03 其他游戏
SpringBoot全局异常处理方案分享
2022/05/25 Java/Android