Bootstrap tabs 源码分析

前言:

阅读建议:去github下载一个完整dom然后把,本篇代码复制进去然后运行就好了以地址

tab组件是非常简单的一种组件,因为这是一个系列,所以就顺便看了,其实它写的这个还算不错的,很有条例,也算是插件的规范写法,研究一下也不错

/* ========================================================================
 * Bootstrap: tab.js v3.3.7
 * http://getbootstrap.com/javascript/#tabs
 * ========================================================================
 * Copyright 2011-2016 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */


+function ($) {
  'use strict';

  // TAB CLASS DEFINITION
  // ====================

  var Tab = function (element) {//传入一个选择器
    // jscs:disable requireDollarBeforejQueryAssignment
    this.element = $(element)
    // jscs:enable requireDollarBeforejQueryAssignment
  }

  Tab.VERSION = '3.3.7'

  Tab.TRANSITION_DURATION = 150

  Tab.prototype.show = function () {
    var $this    = this.element//把a标签赋值给$this
    var $ul      = $this.closest('ul:not(.dropdown-menu)')//closest 仅供插件开发者使用的方法,jquery1.7后就不建议使用了                冲a标签处向上寻找ul包裹元素
    var selector = $this.data('target')//取出target

    if (!selector) {//没有target的话,
      selector = $this.attr('href')//把当前触发的a标签的某点,付给他
      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
    }

    if ($this.parent('li').hasClass('active')) return//发现已经时active了,则返回,没有泽继续向下执行

    var $previous = $ul.find('.active:last a')
    var hideEvent = $.Event('hide.bs.tab', {
      relatedTarget: $this[0]
    })
    var showEvent = $.Event('show.bs.tab', {
      relatedTarget: $previous[0]
    })

    $previous.trigger(hideEvent)
    $this.trigger(showEvent)

    if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return

    var $target = $(selector)

    this.activate($this.closest('li'), $ul)
    this.activate($target, $target.parent(), function () {
      $previous.trigger({
        type: 'hidden.bs.tab',
        relatedTarget: $this[0]
      })
      $this.trigger({
        type: 'shown.bs.tab',
        relatedTarget: $previous[0]
      })
    })
  }

  Tab.prototype.activate = function (element, container, callback) {
    var $active    = container.find('> .active')//得到先前li.active 
    var transition = callback
      && $.support.transition
      && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)

    function next() {
      $active
        .removeClass('active')//把原来的active去除
        .find('> .dropdown-menu > .active')
          .removeClass('active')//在把menu下拉菜单下的active去掉
        .end()//退到上一层
        .find('[data-toggle="tab"]')//寻找他下面的a标签,
          .attr('aria-expanded', false)//aria-expanded 属性赋值为false

      element
        .addClass('active')//给当前触发的li负上active
        .find('[data-toggle="tab"]')//找到地下a标签
          .attr('aria-expanded', true)//赋值为true

      if (transition) {//有毁掉函数的时候
        element[0].offsetWidth // reflow for transition
        element.addClass('in')
      } else {
        element.removeClass('fade')
      }

      if (element.parent('.dropdown-menu').length) {//父元素时.dropdown-menu时执行
        element
          .closest('li.dropdown')
            .addClass('active')
          .end()
          .find('[data-toggle="tab"]')
            .attr('aria-expanded', true)
      }

      callback && callback()
    }

    $active.length && transition ?
      $active
        .one('bsTransitionEnd', next)
        .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
      next()

    $active.removeClass('in')
  }


  // TAB PLUGIN DEFINITION
  // =====================

  function Plugin(option) {
    return this.each(function () {//加each是jquery插件的标配,意为选中多个dom时挨个处理
      var $this = $(this)
      var data  = $this.data('bs.tab')//先取一下bs.tab   这一步是为了缓存Tab对象的,这是必须的,不可能点击一下tab就new Tab(this),

      if (!data) $this.data('bs.tab', (data = new Tab(this)))//如果没有data,那么吧点击的a标签传入tab,然后把Tab对象赋值给data
      if (typeof option == 'string') data[option]()//如果传入的是字符串,则执行相应的方法
    })
  }

  var old = $.fn.tab

  $.fn.tab             = Plugin
  $.fn.tab.Constructor = Tab


  // TAB NO CONFLICT
  // ===============

  $.fn.tab.noConflict = function () {//这个防冲突的代码,为了规范,应该加上
    $.fn.tab = old
    return this
  }


  // TAB DATA-API   自动给你初始化了,这样就可以不用写js代码了
  // ============

  var clickHandler = function (e) {
    e.preventDefault()
    Plugin.call($(this), 'show')
  }

  $(document)
    .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
    .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)//pill这个是给胶囊导航用的,其实tab和pill原理都一样,只是名字不一样而已

}(jQuery);