boostrap之tab

一、前言

這節介紹下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
     // tab菜單可能存在二級菜單,'ul:not(.dropdown-menu)' => 找到外層的ul元素
     var $ul      = $this.closest('ul:not(.dropdown-menu)')

     // 讀取data-target屬性數據,如果不存在則讀取href屬性數據並格式化爲CSS選擇符
     var selector = $this.data('target')
     if (!selector) {
       selector = $this.attr('href')
       selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
     }

     // 如果已經被選中則直接返回
     if ($this.parent('li').hasClass('active')) return

     // 之前被選中的最後一個tab a觸發hide事件,relatedTarget爲將要顯示的元素;將要顯示的tab a觸發show事件,relatedTarget爲之前被選中的
     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')
     var transition = callback
       && $.support.transition
       && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)

     function next() {
       // 如果是tab的下拉菜單的話,那麼$active是dropdown的li元素
       $active
         .removeClass('active')
         .find('> .dropdown-menu > .active')
           .removeClass('active')
         .end()
         .find('[data-toggle="tab"]')
           .attr('aria-expanded', false)

       element
         .addClass('active')
         .find('[data-toggle="tab"]')
           .attr('aria-expanded', true)

       if (transition) {
         element[0].offsetWidth // reflow for transition
         element.addClass('in')
       } else {
         element.removeClass('fade')
       }

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

       callback && callback()
     }

     // 如果是菜單元素直接執行next,否則等$active移除in再執行next方法
     $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 () {
       var $this = $(this)
       var data  = $this.data('bs.tab')

       if (!data) $this.data('bs.tab', (data = new Tab(this)))
       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
   // ============
   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)
 }(jQuery);

三、component-animations.less源碼

 //
 // Component animations(組件動畫)
 // --------------------------------------------------

 // Heads up!
 //
 // We don't use the `.opacity()` mixin here since it causes a bug with text
 // fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.
 .fade {
   opacity: 0;
   .transition(opacity .15s linear);
   &.in {
     opacity: 1;
   }
 }

 // 默認摺疊內容隱藏
 .collapse {
   display: none;
   &.in      { display: block; }
   tr&.in    { display: table-row; }
   tbody&.in { display: table-row-group; }
 }

 .collapsing {
   position: relative;
   height: 0;
   overflow: hidden;
   .transition-property(~"height, visibility");
   .transition-duration(.35s);
   .transition-timing-function(ease);
 }

四、應用 & 源碼分析

1、應用

 <hr>
 <p class="active-tab"><strong>激活的標籤頁</strong><span></span></p>
 <p class="previous-tab"><strong>前一個激活的標籤頁</strong><span></span></p>
 <hr>
 <ul id="myTab" class="nav nav-tabs">
   <li class="active">
     <a href="#home" data-toggle="tab">菜鳥教程</a>
   </li>
   <li><a href="#ios" data-toggle="tab">iOS</a></li>
   <li class="dropdown">
     <a href="#" 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" tabindex="-1" data-toggle="tab">jmeter</a></li>
       <li><a href="#ejb" 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 a:last').tab('show');

     $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
       // 獲取已激活的標籤頁的名稱
       var activeTab = $(e.target).text();
       // 獲取前一個激活的標籤頁的名稱
       var previousTab = $(e.relatedTarget).text();
       $(".active-tab span").html(activeTab);
       $(".previous-tab span").html(previousTab);
     });
   })
 </script>

2、源碼分析

1、show
tab 插件可能內嵌下拉菜單插件 dropdown,所以需要判斷是否是 tab 插件鏈接還是存在於 dropdown
2、activate
核心方法,爲點擊相應元素添加 active 類,並移除前 active 類元素;如果具有 fade 類(見component-animations.less源碼),則在過渡執行完成後再回調

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章