以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中substr,substring,indexOf,lastIndexOf的用法小结
Dec 27 Javascript
Javascript实现可旋转的圆圈实例代码
Aug 04 Javascript
js实现(全选)多选按钮的方法【附实例】
Mar 30 Javascript
js判断checkbox是否选中个数的方法(超简单)
Aug 19 Javascript
Angular 4.x 路由快速入门学习
May 03 Javascript
jQuery实现选中行变色效果(实例讲解)
Jul 06 jQuery
JavaScript中一些特殊的字符运算
Aug 17 Javascript
详谈vue+webpack解决css引用图片打包后找不到资源文件的问题
Mar 06 Javascript
使用 vue 实例更好的监听事件及vue实例的方法
Apr 22 Javascript
Node.js安装详细步骤教程(Windows版)详解
Sep 01 Javascript
vscode 使用Prettier插件格式化配置使用代码详解
Aug 10 Javascript
IDEA配置jQuery, $符号不再显示黄色波浪线的问题
Oct 09 jQuery
基于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
通过table标签,PHP输出EXCEL的实现方法
2013/07/24 PHP
深入解析PHP 5.3.x 的strtotime() 时区设定 警告信息修复
2013/08/05 PHP
PHPCMS手机站伪静态设置详细教程
2017/02/06 PHP
php回调函数处理数组操作示例
2020/04/13 PHP
Laravel服务容器绑定的几种方法总结
2020/06/14 PHP
ParseInt函数参数设置介绍
2014/01/02 Javascript
Nodejs中调用系统命令、Shell脚本和Python脚本的方法和实例
2015/01/01 NodeJs
详解JavaScript操作HTML DOM的基本方式
2015/10/21 Javascript
JavaScript表单验证实例之验证表单项是否为空
2016/01/10 Javascript
Vue结合原生js实现自定义组件自动生成示例
2017/01/21 Javascript
vue 中自定义指令改变data中的值
2017/06/02 Javascript
Node.js+jade抓取博客所有文章生成静态html文件的实例
2017/09/19 Javascript
Three.js如何实现雾化效果示例代码
2017/09/27 Javascript
vue.js动画中的js钩子函数的实现
2018/07/06 Javascript
微信小程序自定义导航教程(兼容各种手机)
2018/12/12 Javascript
jQuery利用cookie 实现本地收藏功能(不重复无需多次命名)
2019/11/07 jQuery
Vue快速实现通用表单验证功能
2019/12/05 Javascript
VUE动态生成word的实现
2020/07/26 Javascript
Vue实现图书管理案例
2021/01/20 Vue.js
[01:11]回顾历届DOTA2国际邀请赛中国区预选赛
2017/06/26 DOTA
centos 下面安装python2.7 +pip +mysqld
2014/11/18 Python
Python的dict字典结构操作方法学习笔记
2016/05/07 Python
Django自定义认证方式用法示例
2017/06/23 Python
Python实现随机选择元素功能
2017/09/14 Python
python调用外部程序的实操步骤
2019/03/04 Python
python接口自动化测试之接口数据依赖的实现方法
2019/04/26 Python
调试Django时打印SQL语句的日志代码实例
2019/09/12 Python
小程序canvas中文字设置居中锚点
2019/04/16 HTML / CSS
世界上最大的字体市场:MyFonts
2020/01/10 全球购物
CK澳大利亚官网:Calvin Klein澳大利亚
2020/12/12 全球购物
新闻编辑专业毕业自荐书范文
2014/02/05 职场文书
计算机维护专业推荐信
2014/02/27 职场文书
Django cookie和session的应用场景及如何使用
2021/04/29 Python
oracle覆盖导入dmp文件的2种方法
2021/05/21 Oracle
OpenCV中resize函数插值算法的实现过程(五种)
2021/06/05 Python
golang操作rocketmq的示例代码
2022/04/06 Golang