Tabs

Tabs are used to navigate between views within the same context. This JavaScript solution is based on showing and hiding panels depending on the active tab. It also takes care of managing tabs classes correctly to show the proper state when clicking a tab. Finally, it adds the -animated class to the component by default (this removes the bottom border of the active element), and it is replaced by a sliding element that emulates the bottom border, that moves from the last active tab to the currently active tab when clicked.

Base

Content for tab a

Content for tab b

Content for tab c

<ul class="a-tabs" id="example-tabs">
  <li class="-active"><a href="#a">Tab a</a></li>
  <li><a href="#b">Tab b</a></li>
  <li><a href="#c">Tab c</a></li>
</ul>

<div class="a-tabs-panel -active" id="a">
  <p class="-text">Content for tab a</p>
</div>
<div class="a-tabs-panel" id="b">
  <p class="-text">Content for tab b</p>
</div>
<div class="a-tabs-panel" id="c">
  <p class="-text">Content for tab c</p>
</div>

<script>chi.tab(document.getElementById('example-tabs'));</script>

Keep default link behavior

By default, Chi JavaScript enabled tabs will ignore default link behavior. To preserve it, specify a target property on the link.

Content for tab a

Content for tab b

Content for tab c

<ul class="a-tabs" id="example-tabs-2">
  <li class="-active"><a href="#a">Tab a</a></li>
  <li><a href="#b">Tab b</a></li>
  <li><a href="#c">Tab c</a></li>
  <li><a href="https://assets.ctl.io/chi/" target="_self">External Link</a></li>
</ul>

<div class="a-tabs-panel -active" id="a">
  <p class="-text">Content for tab a</p>
</div>
<div class="a-tabs-panel" id="b">
  <p class="-text">Content for tab b</p>
</div>
<div class="a-tabs-panel" id="c">
  <p class="-text">Content for tab c</p>
</div>

<script>chi.tab(document.getElementById('example-tabs-2'));</script>

Vertical

The script also handles vertical tabs.

Content for tab a

Content for tab a-1

Content for tab a-2

Content for tab a-3

Content for tab b

Content for tab c

<ul class="a-tabs -vertical" id="example-tabs-3">
  <li class=" -active">
    <a href="#a3">Tab a</a>
    <ul class="a-tabs__subtabs">
      <li>
        <a href="#a3-1">Tab a-1</a>
      </li>
      <li>
        <a href="#a3-2">Tab a-2</a>
      </li>
      <li>
        <a href="#a3-3">Tab a-3</a>
      </li>
    </ul>
  </li>
  <li>
    <a href="#b3">Tab b</a>
  </li>
  <li>
    <a href="#c3">Tab c</a>
  </li>
</ul>
<div class="a-tabs-panel" id="a3">
  <p class="-text">
    Content for tab a
  </p>
</div>
<div class="a-tabs-panel" id="a3-1">
  <p class="-text">
    Content for tab a
  </p>
</div>
<div class="a-tabs-panel" id="a3-2">
  <p class="-text">
    Content for tab a
  </p>
</div>
<div class="a-tabs-panel" id="a3-3">
  <p class="-text">
    Content for tab a
  </p>
</div>
<div class="a-tabs-panel" id="b3">
  <p class="-text">
    Content for tab b
  </p>
</div>
<div class="a-tabs-panel" id="c3">
  <p class="-text">
    Content for tab c
  </p>
</div>

<script>chi.tab(document.getElementById('example-tabs3'));</script>

Preventing memory leaks

Tab components have a dispose function to free all resources attached to the element, such as event listeners and object data. You should call this method when you want to remove the component.

var elem = document.getElementById('example-tabs');
var tabComponent = chi.tab(elem);
// do stuff
tabComponent.dispose();

TipIt is safe to call the tab method more than once, as it will return any previously created tabs component associated to the trigger.

var elem = document.getElementById('example-tabs');
var tabComponent = chi.tab(elem);
var elem2 = document.getElementById('example-tabs');
var tabComponent2 = chi.tab(elem2);
tabComponent === tabComponent2; // returns true

tabComponent.dispose(); // Only have to do it once.

The navigation component is a combination of Chi tabs and dropdowns components. You must use chi.navigation function to instantiate the whole group of components, and there is no need for instantiating the tabs nor the dropdowns independently. The navigation components will manage the lifecycle of the descendants' tabs and dropdowns.

Although navigation reproduces all the functionality that tabs and dropdowns have, like the sliding border or the animated chevron in the dropdowns, it also adds some other new functionalities like the automatic overflow menu, or a wait-for-animation option.

<ul id="navigationExample-1" class="a-tabs">
  <li class="m-dropdown -active">
    <a class="m-dropdown__trigger" href="#">Active tab, click me</a>
    <div class="m-dropdown__menu">
      <a class="m-dropdown__menu-item" href="#">Elem 1</a>
      <a class="m-dropdown__menu-item" href="#">Elem 2</a>
      <div>
        <a class="m-dropdown__menu-item m-dropdown__trigger" href="#">Elem 3 more</a>
        <div class="m-dropdown__menu">
          <a class="m-dropdown__menu-item" href="#">Elem 3.1</a><a class="m-dropdown__menu-item" href="#">Elem 3.2</a><a class="m-dropdown__menu-item" href="#">Elem 3.3</a><a class="m-dropdown__menu-item" href="#">Elem 3.4</a>
        </div>
      </div>
      <a class="m-dropdown__menu-item" href="#">Elem 4</a>
    </div>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li class="m-dropdown">
    <a class="m-dropdown__trigger" href="#">Tab Dropdown</a>
    <div class="m-dropdown__menu">
      <a class="m-dropdown__menu-item" href="#">Element 1</a>
      <a class="m-dropdown__menu-item" href="#">Element 2</a>
      <a class="m-dropdown__menu-item" href="#">Element 3</a>
    </div>
  </li>
</ul>
const navigationElem = document.getElementById('#navigationExample-1');
chi.navigation(navigationElem);

Options

This component accepts options to configure its behavior.

Option
Default
Description
overflowMenu
true
Only works for horizontal navigation components. It encloses overflowed tabs into a dropdown located at the end of the navigation component.
overflowMenuLabel
More...
Text for the overflow menu dropdown.
waitForAnimations
false
Makes the browser wait for the sliding border to move to the clicked link before following links. It only works with common links.

Overflow menu encloses overflowed tabs into a dropdown menu at the end of the navigation component. Any instantiation of a new horizontal Navigation component will automatically support this Overflow Menu. You can prevent this by using the option overflowMenu: false. The label using in the activator of this menu can be changed with the overflowMenuLabel option.

<ul id="navigationExample-3-enabled" class="a-tabs">
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li class="m-dropdown">
    <a class="m-dropdown__trigger" href="#">Tab Dropdown</a>
    <div class="m-dropdown__menu">
      <a class="m-dropdown__menu-item" href="#">Element 1</a>
      <a class="m-dropdown__menu-item" href="#">Element 2</a>
      <a class="m-dropdown__menu-item" href="#">Element 3</a>
    </div>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li class="m-dropdown">
    <a class="m-dropdown__trigger" href="#">Tab Dropdown</a>
    <div class="m-dropdown__menu">
      <a class="m-dropdown__menu-item" href="#">Element 1</a>
      <a class="m-dropdown__menu-item" href="#">Element 2</a>
      <a class="m-dropdown__menu-item" href="#">Element 3</a>
    </div>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
</ul>
const navigationElem = document.getElementById('#navigationexample-3-enabled');
chi.navigation(navigationElem);
<ul id="navigationExample-3-disabled" class="a-tabs">
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li class="m-dropdown">
    <a class="m-dropdown__trigger" href="#">Tab Dropdown</a>
    <div class="m-dropdown__menu">
      <a class="m-dropdown__menu-item" href="#">Element 1</a>
      <a class="m-dropdown__menu-item" href="#">Element 2</a>
      <a class="m-dropdown__menu-item" href="#">Element 3</a>
    </div>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li class="m-dropdown">
    <a class="m-dropdown__trigger" href="#">Tab Dropdown</a>
    <div class="m-dropdown__menu">
      <a class="m-dropdown__menu-item" href="#">Element 1</a>
      <a class="m-dropdown__menu-item" href="#">Element 2</a>
      <a class="m-dropdown__menu-item" href="#">Element 3</a>
    </div>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
</ul>
const navigationElem = document.getElementById('#navigationexample-3-disabled');
chi.navigation(
  navigationElem,
  {overflowMenu: false}
);

Browsers stop any execution of JavaScript as soon as a link is clicked and it starts to fetch the destination URL. For this reason, the sliding border animation will not be perceived by the user when an external link is clicked, as the animation will not be done, an this can be confusing for the user. To prevent this possible confusion, this component has the option to wait for the animation to finish and, then, it will redirect the user to the destination URL. You can enable this behavior by setting the waitForAnimations option to true.

<ul id="navigationexample-4-enabled" class="a-tabs">
  <li class="-active">
    <a href="/">Tab Link</a>
  </li>
  <li>
    <a href="/">Tab Link</a>
  </li>
  <li>
    <a href="/">Tab Link</a>
  </li>
  <li>
    <a href="/">Tab Link</a>
  </li>
  <li>
    <a href="/">Tab Link</a>
  </li>
  <li>
    <a href="/">Tab Link</a>
  </li>
</ul>
const navigationElem = document.getElementById('#navigationexample-4-enabled');
chi.navigation(
  navigationElem,
  {waitForAnimations: true}
);
<ul id="navigationexample-4-disabled" class="a-tabs">
  <li class="-active">
    <a href="/">Tab Link</a>
  </li>
  <li>
    <a href="/">Tab Link</a>
  </li>
  <li>
    <a href="/">Tab Link</a>
  </li>
  <li>
    <a href="/">Tab Link</a>
  </li>
  <li>
    <a href="/">Tab Link</a>
  </li>
  <li>
    <a href="/">Tab Link</a>
  </li>
</ul>
const navigationElem = document.getElementById('#navigationexample-4-disabled');
chi.navigation(
  navigationElem,
  {waitForAnimations: false}
);

As navigation component is built from other primitive Chi components, most of these components behavior can be replicated on the navigation component.

I.e. Use the -animate class on the dropdowns to make the chevron rotate when activated.

<ul class="a-tabs">
  <li class="m-dropdown -active">
    <a class="m-dropdown__trigger -animate" href="#">Active Tab</a>
    <div class="m-dropdown__menu" x-placement="bottom-start">
      <a class="m-dropdown__menu-item" href="#">Elem 1</a><a class="m-dropdown__menu-item" href="#">Elem 2</a>
      <div>
        <a class="m-dropdown__menu-item m-dropdown__trigger" href="#">Elem 3 more</a>
        <div class="m-dropdown__menu">
          <a class="m-dropdown__menu-item" href="#">Elem 3.1</a><a class="m-dropdown__menu-item" href="#">Elem 3.2</a><a class="m-dropdown__menu-item" href="#">Elem 3.3</a><a class="m-dropdown__menu-item" href="#">Elem 3.4</a>
        </div>
      </div><a class="m-dropdown__menu-item" href="#">Elem 4</a>
    </div>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li>
    <a href="#">Tab Link</a>
  </li>
  <li class="m-dropdown">
    <a class="m-dropdown__trigger" href="#">Tab Link</a>
    <div class="m-dropdown__menu">
      <a class="m-dropdown__menu-item" href="#">Element 1</a><a class="m-dropdown__menu-item" href="#">Element 2</a><a class="m-dropdown__menu-item" href="#">Element 3</a>
    </div>
  </li>
</ul>

Preventing memory leaks

Navigation components have a dispose function to free all resources attached to the element, such as event listeners and object data. You should call this method when you want to remove the component. There is no need to call to tab and dropdown inner components dispose function as the navigation one will free resources of all internal components automatically.

var navigationElem = document.getElementById('#navigationElementId');
var navigationComponent = chi.navigation(navigationElem);
// do stuff
navigationComponent.dispose();

TipIt is safe to call the navigation factory function more than once, as it will return any previously created navigation component associated to the element.

var elem = document.getElementById('navigation-1');
var navigationComponent = chi.navigation(elem);
var elem2 = document.getElementById('navigation-1');
var navigationComponent2 = chi.navigation(elem2);
navigationComponent === navigationComponent2; // returns true

navigationComponent.dispose(); // Only have to do it once.