title: jQuery UI Tabs plugin can't handle 2 tiers of tabs
body: <p>I'm working on a legacy project that uses jQuery UI tabs to display user generated data in a structured way. The system does not cap the number of tabs a user can create (but usually there are about 30 in the second tier) and jQuery UI Tabs just wraps the tabs, which looks very unprofessional.</p>
  
  <p><a href="https://i.stack.imgur.com/xrLGA.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/xrLGA.png" alt="Wrapping tabs in my project."></a></p>
  
  <p>I stumbled over Paul Blundell's beautiful plugin <a href="https://github.com/paul-blundell/OverflowTabs" rel="nofollow noreferrer">OverflowTabs</a> from 2014 (here is his JSFiddle: <a href="http://jsfiddle.net/o1wLbtj4/" rel="nofollow noreferrer">http://jsfiddle.net/o1wLbtj4/</a>, ), but unfortunately I can't get it to work right with my 2 tiers of tabs. I'm using jQuery 1.10.2 and jQuery UI 1.10.4.
  <a href="https://i.stack.imgur.com/tyXWG.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/tyXWG.png" alt="Gathered tabs in a dropdown menu in Paul Blundell&#39;s JSFiddle."></a></p>
  
  <p>I've made a simplified JSFiddle which shows what I'm trying to do: open a dialogue window that contains data in 2 tiers of tabs. When there are too many tabs, both tiers are supposed to push overflowing tabs into a menu that opens a dropdown when clicked, but at the moment (on the tier with names) only the last tab is displayed and the dropdown button doesn't work and reads 0: <a href="http://jsfiddle.net/megahra/6s2xwgec/18/" rel="nofollow noreferrer">http://jsfiddle.net/megahra/6s2xwgec/18/</a></p>
  
  <p>As far as I can tell the plugin properly removes tabs from the first tier and adds them to the overflow div (_hideTab function), but for some reason later this is overwritten. I suspect that the plugin can't deal with the second tier properly, but I don't know where to fix that.</p>
  
  <p>I'd be really grateful if anyone could point me in the right direction!</p>
  
  <p>EDIT:<br>
  Thanks to Twisty's help the plugin now works for multiple tiers of tabs and I fixed a few bugs.<br>
  <strong>Here is a working JSFiddle:</strong> <a href="https://jsfiddle.net/megahra/uexr4qfw/289/" rel="nofollow noreferrer">https://jsfiddle.net/megahra/uexr4qfw/289/</a><br>
  - Known bug: this plugin is not applied to tabs in divs with <code>style="display: none"</code> at time of initialization.</p>
  
  <pre><code>$.widget("ui.tabs", $.ui.tabs, {
    options: {
      overflowTabs: false,
      tabPadding: 25,
      containerPadding: 0,
      dropdownSize: 50
    },
  
    _create: function() {
      this._super("_create");
      this.tabsWidth = 0;
      this.containerWidth = 0;
      this.overflowTabsId = "id" + this._createUniqueId();
  
      $(this.element).addClass(this.overflowTabsId);
  
      if (!this.options.overflowTabs)
        return;
  
      // update the tabs
      this.updateOverflowTabs();
  
      // Detect a window resize and check the tabs again
      var that = this;
      var el = this.element;
  
      $(window).resize(function() {
        // Add a slight delay after resize, to fix Maximise issue.
        setTimeout(function() {
          that.updateOverflowTabs();
        }, 150);
      });
  
      // Detect dropdown click
      $(el).on("click", '&gt; .overflow-selector', function(e) {
        $('&gt; .ui-tabs-overflow', el).toggleClass('hide');
  
        $overflowTabs = $('.ui-tabs-overflow').not($('&gt; .ui-tabs-overflow', el));
  
        //if there is more than the currently clicked one, close all others
        if($overflowTabs) {
          $overflowTabs.toggleClass('hide', true);
          }
      });
  
      //Detect tab click
      $('li a').on("click", function(e) {
          //close dropdown if open
          $('&gt; .ui-tabs-overflow', el).toggleClass('hide', true);
          //ToDo: apply overflowTabs plugin to content of new tab (if it contains tabs)
  
      });
    },
  
    refresh: function() {
      this._super("refresh");
      this.updateOverflowTabs();
    },
  
    updateOverflowTabs: function() {
      var failsafe = 0;
      this._calculateWidths();
      var el = this.element;
  
      // Loop until tabsWidth is less than the containerWidth
      while (this.tabsWidth &gt; this.containerWidth -10 &amp;&amp; failsafe &lt; 30) {
        this._hideTab();
        this._calculateWidths();
        failsafe++;
      }
  
      // Finish now if there are no tabs in the overflow list
      if ($('&gt; .ui-tabs-overflow li', el).length === 0)
        return;
  
      // Reset
      failsafe = 0;
  
      // Get the first tab in the overflow list
      var next = this._nextTab();
  
      // Loop until we cannot fit any more tabs
      while (next.totalSize &lt; this.containerWidth &amp;&amp; $('&gt; .ui-tabs-overflow li', el).length &gt; 0 &amp;&amp; failsafe &lt; 30) {
        this._showTab(next.tab);
        this._calculateWidths();
        next = this._nextTab();
        failsafe++;
      }
  
      $('&gt; .overflow-selector .total', el).html($('&gt; .ui-tabs-overflow li', el).length);
    },
  
    _calculateWidths: function() {
      var width = 0;
      $(this.element).find('&gt; .ui-tabs-nav &gt; li').each(function() {
        width += $(this).outerWidth(true);
      });
      this.tabsWidth = width;
      this.containerWidth = $(this.element).parent().width() - this.options.containerPadding - this.options.dropdownSize;
    },
    _hideTab: function() {
      if (!$(this.element).find('&gt; .ui-tabs-overflow').length) {
        $(this.element).find('&gt; .ui-tabs-nav').after('&lt;ul class="ui-tabs-overflow hide"&gt;&lt;/ul&gt;');
        $(this.element).find('&gt; .ui-tabs-overflow').after('&lt;div class="overflow-selector"&gt;&amp;#8595 &lt;span class="total"&gt;0&lt;/span&gt;&lt;/div&gt;');
        //calculate position of overflow-selector relativ to tab row (overflow-selector is 15px high)
        let topOffset = ($(this.element).find('&gt; .ui-tabs-nav').innerHeight() * 0.48) - 12;
        $(this.element).find('&gt; .overflow-selector').css('top', topOffset);
      }
      var lastTab = $('&gt; .ui-tabs-nav li', this.element).last();
      lastTab.prependTo($('&gt; .ui-tabs-overflow', this.element));
    },
    _showTab: function(tab) {
      tab.appendTo($('&gt; .ui-tabs-nav', this.element));
  
      // Check to see if overflow list is now empty
      if ($(this.element).find('&gt; .ui-tabs-overflow li').size() == 0) {
        $(this.element).find('&gt; .ui-tabs-overflow').remove();
        $(this.element).find('&gt; .overflow-selector').remove();
      }
    },
    _nextTab: function() {
      var result = {};
      var firstTab = $(this.element).find('&gt; .ui-tabs-overflow li').first();
      result.tab = firstTab;
      result.totalSize = this.tabsWidth + this._textWidth(firstTab) + this.options.tabPadding;
      return result;
    },
    _textWidth: function(element) {
      var self = $(element),
        children = self.children(),
        calculator = $('&lt;span style="display: inline-block;" /&gt;'),
        width;
  
      children.wrap(calculator);
      width = children.parent().width();
      children.unwrap();
  
      return width;
    },
  
    _createUniqueId: function() {
      // Math.random should be unique because of its seeding algorithm.
      // Convert it to base 36 (numbers + letters), and grab the first 9 characters
      // after the decimal.
      return '_' + Math.random().toString(36).substr(2, 9);
      }
  });
  </code></pre>