Вкладки внутри вкладок - ваниль JS - PullRequest
0 голосов
/ 18 марта 2020

Коды для вкладок легко найти, но создание функциональных вкладок внутри содержимого другой вкладки - нет. Особенно если это должно быть в чистом ванили JS. В идеале каждая вкладка должна работать независимо внутри другой, но я не знаю, как реорганизовать код. Boostrap сделал это отлично, но я не позволил использовать его в проекте. Это самое близкое, что я нахожу.

HTML

<ul class="tabs" data-directive="tabs">
    <li><a href="#panel-01" data-behaviour="tab">Tab 1</a></li>
    <li><a href="#panel-02" data-behaviour="tab">Tab 2</a></li>
    <li><a href="#panel-03" data-behaviour="tab">Tab 3</a></li>
</ul>
<div id="panel-01">
    <ul class="tabs" data-directive="tabs">
        <li><a href="#panel-011" data-behaviour="tab">Tab 1</a></li>
        <li><a href="#panel-022" data-behaviour="tab">Tab 2</a></li>
        <li><a href="#panel-033" data-behaviour="tab">Tab 3</a></li>
    </ul>
    <div id="panel-011">
        Tab content 1
    </div>
    <div id="panel-022">
        Tab content 2
    </div>
    <div id="panel-033">
        Tab content 3
    </div>
</div>
<div id="panel-02">
    Tab content 2
</div>
<div id="panel-03">
    Tab content 3
</div>
<script src="custom.js"></script>

CSS

body {
    font:normal 1.25em/1.5 Arial, sans-serif;
}
:focus {
    outline:1px dotted #EF9600;
    outline-offset:1px;
}

.tabs {
    margin: 0;
    padding: 0;
    border-bottom: 1px solid #EEE;
}

.tabs > li {
    display: inline;
}

.tabs > li > a {
    display: inline-block;
    padding: .25em .5em;
    border: 1px solid #EEE;
    border-bottom: 0;
    text-decoration: none;
    background: #EEE;
}

.tabs > li > a.is-selected {
    margin-bottom: -1px;
    border-bottom: 1px solid #FFF;
    background: #FFF;
}

JS

(function (name, context, definition) {
  if (typeof define === 'function' && define.amd) {
      define(definition);
  }
  else if (typeof module !== 'undefined' && module.exports) {
      module.exports = definition();
  }
  else {
      context[name] = definition();
  }
})('Tabs', this, function() {

  'use strict';

  /**
   * Tabs
   * @constructor
   * @param {HTMLElement} element
   */
  function Tabs(element) {
      var i, len;

      this.target = element;
      this.tabs = element.querySelectorAll('[data-behaviour=tab]');
      this.panels = [];

      for (i = 0, len = this.tabs.length; i < len; i++) {
          this.panels.push( document.getElementById(this.tabs[i].hash.replace('#', '')) );
      }

      if (this.selectedIndex === undefined) {
          this._init();
      }
  }

  /**
   * Init
   * @private
   */
  Tabs.prototype._init = function() {
      var i;
      var self = this;

      this.target.setAttribute('role', 'tablist');

      for (i = this.tabs.length - 1; i >= 0; i--) {
          var tab = this.tabs[i];
          var panel = this.panels[i];
          var preSelected = tab.className.match(/\bis-selected\b/);
          var selected = i === 0 || preSelected || window.location.hash.replace('#', '') == panel.id;

          tab.setAttribute('role', 'tab');
          tab.setAttribute('aria-selected', selected);
          tab.setAttribute('aria-controls', tab.hash.replace('#', ''));

          panel.setAttribute('role', 'tabpanel');

          if (selected) {
              this.selectedIndex = i;

              if (!preSelected) {
                  tab.className+= ' is-selected ';
              }
          }
          else {
              panel.style.display = 'none';
          }
      }

      this.clickHandler = function(e) {
          var target = e.srcElement || e.target;

          if (target.getAttribute('role') == 'tab') {

              if (e.preventDefault) {
                  e.preventDefault();
              }
              else {
                  e.returnValue = false;
              }

              self.toggle(target);
          }
      };

      this.keyHandler = function(e) {
          switch(e.keyCode) {
              case 37:
                  if (self.tabs[self.selectedIndex - 1]) {
                      self.toggle(self.tabs[self.selectedIndex - 1]);
                  }
                  break;

              case 39:
                  if (self.tabs[self.selectedIndex + 1]) {
                      self.toggle(self.tabs[self.selectedIndex + 1]);
                  }
                  break;
          }
      };

      if (this.target.addEventListener) {
          this.target.addEventListener('click', this.clickHandler, false);
          this.target.addEventListener('keyup', this.keyHandler, false);
      }
      else {
          this.target.attachEvent('onclick', this.clickHandler);
          this.target.attachEvent('onclick', this.keyHandler);
      }

  };

  /**
   * Toggle
   * @param {HTMLElement} tab
   */
  Tabs.prototype.toggle = function(tab) {
      var i, len;
      var panel = document.getElementById(tab.hash.replace('#', ''));

      tab.focus();

      this.tabs[this.selectedIndex].className = this.tabs[this.selectedIndex].className.replace('is-selected', '');
      this.tabs[this.selectedIndex].setAttribute('aria-selected', false);

      this.panels[this.selectedIndex].style.display = 'none';

      tab.className+= ' is-selected ';
      tab.setAttribute('aria-selected', true);

      panel.style.display = '';

      // Find tab index
      for (i = 0, len = this.tabs.length; i < len; i++) {
          if (tab == this.tabs[i]) {
              break;
          }
      }

      this.selectedIndex = i;
  };

  /**
   * Teardown
   */
  Tabs.prototype.teardown = function() {
      var i, len;

      this.target.removeAttribute('role');

      if (this.target.removeEventListener) {
          this.target.removeEventListener('click', this.clickHandler, false);
          this.target.removeEventListener('click', this.keyHandler, false);
      }
      else {
          // Presume legacy IE
          this.target.detachEvent('onclick', this.clickHandler);
          this.target.detachEvent('onclick', this.keyHandler);
      }

      for (i = 0, len = this.tabs.length; i < len; i++) {
          var tab = this.tabs[i];
          var panel = this.panels[i];

          tab.removeAttribute('role');
          tab.removeAttribute('aria-selected');
          tab.removeAttribute('aria-controls');

          panel.style.display = '';
          panel.removeAttribute('role');
      }

      delete this.selectedIndex;
  };

  return Tabs;

});

// You were going to wrap the above as an AMD/CommonJS module and load on demand, right?
var instance = new Tabs( document.querySelector('[data-directive=tabs]') );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...