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

介绍

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

原生的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>

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

扩展

组件在使用的过程中还会遇到各种问题,或者各种需求,比如新增一个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为例写一个前端组件,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • JS表格组件神器bootstrap table详解(基础版)

    一.Bootstrap Table的引入 关于Bootstrap Table的引入,一般来说还是两种方法: 1.直接下载源码,添加到项目里面来. 由于Bootstrap Table是Bootstrap的一个组件,所以它是依赖Bootstrap的,我们首先需要添加Bootstrap的引用. 2.使用我们神奇的Nuget 打开Nuget,搜索这两个包 Bootstrap已经是最新的3.3.5了,我们直接安装即可. 而Bootstrap Table的版本竟然是0.4,这也太坑爹了.所以博主建议Boot

  • JS表格组件神器bootstrap table详解(强化版)

    一.Bootstrap Table的引入 关于Bootstrap Table的引入,一般来说还是两种方法: 1.直接下载源码,添加到项目里面来. 由于Bootstrap Table是Bootstrap的一个组件,所以它是依赖Bootstrap的,我们首先需要添加Bootstrap的引用. 2.使用我们神奇的Nuget 打开Nuget,搜索这两个包 Bootstrap已经是最新的3.3.5了,我们直接安装即可. 而Bootstrap Table的版本竟然是0.4,这也太坑爹了.所以博主建议Boot

  • JS组件Bootstrap Table布局详解

    Bootstrap 提供了一个清晰的创建表格的布局.下表列出了 Bootstrap 支持的一些表格元素: 表格类 下表样式可用于表格中: <tr>, <th> 和 <td> 类 下表的类可用于表格的行或者单元格: 基本的表格 如果您想要一个只带有内边距(padding)和水平分割的基本表,请添加 class .table,如下面实例所示: <div class="row"> <table class="table"

  • JS组件Bootstrap Table使用方法详解

    最近客户提出需求,想将原有的管理系统,做下优化,通过手机也能很好展现,想到2个方案: a方案:保留原有的页面,新设计一套适合手机的页面,当手机访问时,进入m.zhy.com(手机页面),pc设备访问时,进入www.zhy.com(pc页面) b方案:采用bootstrap框架,替换原有页面,自动适应手机.平板.PC 设备 采用a方案,需要设计一套界面,并且要得重新写适合页面的接口,考虑到时间及成本问题,故项目采用了b方案 一.效果展示 二.BootStrap table简单介绍 bootStra

  • JS组件Bootstrap Table使用实例分享

    学习使用bootstrap表格是对客户端进行分页的时候,在朋友的帮助下,找到了文档http://bootstrap-table.wenzhixin.net.cn/examples/                  找到了传到后台的每页条数Limit,和记录开始数Offset.              开始封装,分享一下我的代码,从bootstrap table 获取页码和页数,并交给后台处理. $('#table').bootstrapTable({ url: '<%=path%>/Fee

  • JS组件系列之Bootstrap table表格组件神器【终结篇】

    bootstrap table系列: JS表格组件神器bootstrap table详解(基础版) JS组件系列之Bootstrap table表格组件神器[终结篇] JS组件系列之Bootstrap table表格组件神器[二.父子表和行列调序] Bootstrap Table是轻量级的和功能丰富的以表格的形式显示的数据,支持单选,复选框,排序,分页,显示/隐藏列,固定标题滚动表,响应式设计,Ajax加载JSON数据,点击排序的列,卡片视图等.那么本文给大家介绍JS组件系列之Bootstrap

  • JS组件Bootstrap Table表格多行拖拽效果实现代码

    前言:前天刚写了篇JS组件Bootstrap Table表格行拖拽效果,今天接到新的需要,需要在之前表格行拖拽的基础上能够同时拖拽选中的多行.用了半天时间研究了下,效果是出来了,但是感觉不尽如人意.先把它分享出来,以后想到更好的办法再优化吧. 一.效果展示 1.拖动前 2.拖动中 3.拖动后 4.撤销回到拖动前状态 二.需求分析 通过上篇我们知道,如果要实现拖拽,必须要有一个可以拖拽的标签,或者叫容器,比如上篇里面的tr就是一个拖拽的容器,那么如果要实现选择行的拖拽,那么博主的第一反应是将选中的

  • JS组件Bootstrap Table表格行拖拽效果实现代码

    一.业务需求及实现效果 项目涉及到订单模块,那天突然接到一个需求,说是两种不同状态的订单之间要实现插单的效果,页面上呈现方式是:左右两个Table,左边Table里面是状态为1的订单,右边Table里面是状态为2订单,左边Table里面的行数据拖动到右边Table里面指定行的位置,拖动完成后,左边表格减少一行,右边表格增加一行.除此之外,还需要撤销操作(相当于Ctrl + Z操作),能够返回到上一步的状态.可能描述会让大家模拟两可,反正已经实现了,先来看看效果图吧. 1.先看看拖动之前的效果 2

  • JS组件系列之Bootstrap table表格组件神器【二、父子表和行列调序】

    Bootstrap Table是轻量级的和功能丰富的以表格的形式显示的数据,支持单选,复选框,排序,分页,显示/隐藏列,固定标题滚动表,响应式设计,Ajax加载JSON数据,点击排序的列,卡片视图等.今天就结合Bootstrap table的父子表和行列调序的用法再来介绍下它稍微高级点的用法. bootstrap table系列: JS表格组件神器bootstrap table详解(基础版) JS组件系列之Bootstrap table表格组件神器[终结篇] JS组件系列之Bootstrap t

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

    介绍 本文以Bootstrap标签页组件为例,介绍如何编写或者封装一个前端组件,以下是实现效果: 原生的Bootstrap-tab组件主要有html,css组成,开发者使用时,需要写很多代码,不易于使用,对bootstrap-tab封装后,可以更方便地使用,同时提供关闭.增加tab页.指定当前选中页.即使加载等功能,这样组件可以适配更多的场景. 原生bootstrap-tab组件使用可参考https://www.runoob.com/bootstrap/bootstrap-tab-plugin.

  • 使用ElementUI写一个前端分页查询的实例

    目录 前言 前端分页 查询 写在最后 前言 一般我们在做项目的时候,都是通过调用后台接口去做增删改查,但是也有例外,有些某些特定场景下,会让前端去做好增删改查. 那么我们今天就来做一下这个需求. 使用的框架是Vue,UI框架是使用率很高的ElementUI. 那么我们开干吧. 前端分页 假定我们的数据是通过点击一个添加按钮,弹出一个添加弹窗,输入好数据后,点击确定按钮,把数据保存到表格中去. 那么就是这样的 { data () { return { page: { pageOffset: 1,

  • 在React中写一个Animation组件为组件进入和离开加上动画/过度效果

    在React中写一个Animation组件为组件进入和离开加上动画/过度效果 问题 在单页面应用中,我们经常需要给路由的切换或者元素的挂载和卸载加上过渡效果,为这么一个小功能引入第三方框架,实在有点小纠结.不如自己封装. 思路 原理 以进入时 opacity: 0 --> opacity: 1  ,退出时 opacity: 0 --> opacity: 1 为例 元素挂载时 1.挂载元素dom 2.设置动画 opacity: 0 --> opacity: 1 元素卸载时 1.设置动画 o

  • 尝试自己动手用react来写一个分页组件(小结)

    本文介绍了尝试自己动手用react来写一个分页组件(小结),分享给大家,具体如下: 分页效果 在线预览 github地址 效果截图(样式可自行修改): 构建项目 create-react-app react-paging-component 分页组件 1.子组件 创建 Pagecomponent.js 文件 核心代码: 初始化值 constructor(props) { super(props) this.state = { currentPage: 1, //当前页码 groupCount:

  • react写一个select组件的实现代码

    之前一直用的antd的Select组件,但在有些端并不适用,而原生的select样式修改不灵活,遂产生自己写一个组件的想法.观察select组件: <select onChange={(value) => {this.value=value}} <option value='1'>man</option> <option value='0'>woman</option> </select> 可以看出数据都是在option中,有值val

  • React手写一个手风琴组件示例

    目录 知识点 结构分析 AccordionItem子组件 Accordion容器组件 知识点 emotion语法 react语法 css语法 typescript类型语法 结构分析 根据上图,我们来分析一下,一个手风琴组件应该包含一个手风琴容器组件和多个手风琴子元素组件.因此,假设我们实现好了所有的逻辑,并写出使用demo,那么代码应该如下: <Accordion defaultIndex="1" onItemClick={console.log}> <Accordi

  • elementui源码学习之仿写一个el-divider组件

    目录 正文 组件需求分析 组件中用到的知识点 函数式组件 函数式组件的两种定义方式 解决一像素太粗的问题 组件封装 组件封装的效果图 组件封装的代码 正文 本篇文章记录仿写一个el-divider组件细节,从而有助于大家更好理解饿了么ui对应组件具体工作细节.本文是elementui源码学习仿写系列的又一篇文章,后续空闲了会不断更新并仿写其他组件.源码在github上,大家可以拉下来,npm start运行跑起来,结合注释有助于更好的理解. github仓库地址如下:github.com/shu

  • 动手写一个angular版本的Message组件的方法

    学习一个框架或库的最好方法是看官方文档,并着手去写例子.最近在利用空闲的时间学习angular,那今天就尝试写一个message组件,并通过message服务动态加载message组件. 我所参与的项目基本是用jquery完成的.之前,在项目中自己动手写过一个简单的message插件,样子如下图. 那现在就使用angular(版本5.0.0)来实现message组件. message组件 message组件要根据传入的类型.消息和duration来显示.创建三个文件:message.compon

  • vue写一个组件

    写一个vue组件 我下面写的是以.vue结尾的单文件组件的写法,是基于webpack构建的项目.如果还不知道怎么用webpack构建一个vue的工程的,可以移步到vue-cli. 一个完整的vue组件会包括一下三个部分: template:模板 js: 逻辑 css : 样式 每个组件都有属于自己的模板,js和样式.如果将一个页面比喻成一间房子的话,组件就是房子里的客厅.卧室.厨房.厕所.如果把厨房单独拿出来的话,组件又可以是刀.油烟机...等等.就是说页面是由组件构成的,而组件也可以是组件构成

  • 使用JSX 建立 Markup 组件风格开发的示例(前端组件化)

    目录 JSX 环境搭建 初始化 NPM 安装 webpack 安装 Babel 配置 webpack 安装 Babel-loader 模式配置 引入 JSX JSX 基本用法 JSX 基础原理 实现 createElement 函数 实现自定义标签 它会包含以下方法: 这里我们一起从 0 开始搭建一个组件系统.首先通过上一篇<前端组件化基础知识>和<用 JSX 建立组件 Parser(解析器)>中知道,一个组件可以通过 Markup 和 JavaScript 访问的一个环境. 所以

随机推荐