Skip to content

Tabs ​

The tab control works on any control-tabs element: a nav-tabs list of tabs and a tab-content element with one tab-pane per tab, matched by their order. A class on the element picks the style. Forms use the same control for their tabs, set up from the fields YAML (see Layout).

Primary tabs ​

primary-tabs is the main style, for the top level sections of a page. Mark the open tab's li and its pane with active; the control moves the class as tabs are picked. Each tab is an <a> element inside its li.

data-linkable keeps the open tab in the address: picking a tab writes the anchor's href hash, such as #tab-activity, to the address bar, and a page opened with that hash opens the tab again, so a reload or a shared link lands on it. Without data-linkable, the anchors can use href="javascript:;".

blade
<div class="control-tabs primary-tabs" data-control="tab" data-linkable>
    <ul class="nav nav-tabs">
        <li class="active">
            <a href="#tab-overview">
                Overview
            </a>
        </li>
        <li>
            <a href="#tab-activity">
                Activity
            </a>
        </li>
        <li>
            <a href="#tab-settings">
                Settings
            </a>
        </li>
    </ul>
    <div class="tab-content">
        <div class="tab-pane active">
            <p class="pt-4 mb-0">
                The overview pane. Pick another tab, then reload the page: the address keeps the tab.
            </p>
        </div>
        <div class="tab-pane">
            <p class="pt-4 mb-0">
                The activity pane.
            </p>
        </div>
        <div class="tab-pane">
            <p class="pt-4 mb-0">
                The settings pane.
            </p>
        </div>
    </div>
</div>

A tab anchor with data-tab-url writes that address to the address bar when its tab is picked, with or without data-linkable.

Secondary tabs ​

secondary-tabs gives a lighter style, for a smaller group of panes inside a page.

blade
<div class="control-tabs secondary-tabs" data-control="tab">
    <ul class="nav nav-tabs">
        <li class="active">
            <a href="javascript:;">
                Details
            </a>
        </li>
        <li>
            <a href="javascript:;">
                Shipping
            </a>
        </li>
        <li>
            <a href="javascript:;">
                Notes
            </a>
        </li>
    </ul>
    <div class="tab-content">
        <div class="tab-pane active">
            <p class="mb-0">
                Secondary tabs suit a smaller group inside a page.
            </p>
        </div>
        <div class="tab-pane">
            <p class="mb-0">
                Ships within two business days.
            </p>
        </div>
        <div class="tab-pane">
            <p class="mb-0">
                No notes yet.
            </p>
        </div>
    </div>
</div>

Pill tabs ​

pill-tabs joins the tabs into a segmented control, which suits switching between views of the same data, such as a week, month and year.

blade
<div class="control-tabs pill-tabs" data-control="tab">
    <ul class="nav nav-tabs">
        <li class="active">
            <a href="javascript:;">
                Week
            </a>
        </li>
        <li>
            <a href="javascript:;">
                Month
            </a>
        </li>
        <li>
            <a href="javascript:;">
                Year
            </a>
        </li>
    </ul>
    <div class="tab-content">
        <div class="tab-pane active">
            <p class="mb-0">
                Pill tabs, for switching views of the same data.
            </p>
        </div>
        <div class="tab-pane">
            <p class="mb-0">
                The month view.
            </p>
        </div>
        <div class="tab-pane">
            <p class="mb-0">
                The year view.
            </p>
        </div>
    </div>
</div>

Underline tabs ​

underline-tabs marks the open tab with a line under its title, for a light row of sections.

blade
<div class="control-tabs underline-tabs" data-control="tab">
    <ul class="nav nav-tabs">
        <li class="active">
            <a href="javascript:;">
                Comments
            </a>
        </li>
        <li>
            <a href="javascript:;">
                History
            </a>
        </li>
    </ul>
    <div class="tab-content">
        <div class="tab-pane active">
            <p class="mb-0">
                Underline tabs, for a light row of sections.
            </p>
        </div>
        <div class="tab-pane">
            <p class="mb-0">
                The history section.
            </p>
        </div>
    </div>
</div>

Overflowing tabs ​

Tabs that do not fit their row scroll sideways, by dragging or with the mouse wheel, and a small arrow at each edge shows there are more tabs that way. There is nothing to set up: every tab control does this with the same helper as the drag scroll control. Opening a tab from JavaScript scrolls the row to it.

blade
@php
    $months = [
        'January', 'February', 'March', 'April', 'May', 'June',
        'July', 'August', 'September', 'October', 'November', 'December',
    ];
@endphp

<div class="control-tabs primary-tabs" data-control="tab">
    <ul class="nav nav-tabs">
        @foreach ($months as $month)
            <li @class(['active' => $loop->first])>
                <a href="javascript:;">
                    {{ $month }} report
                </a>
            </li>
        @endforeach
    </ul>
    <div class="tab-content">
        @foreach ($months as $month)
            <div @class(['tab-pane', 'active' => $loop->first])>
                <p class="pt-4 mb-0">
                    The {{ $month }} pane.
                </p>
            </div>
        @endforeach
    </div>
</div>

Closable tabs ​

data-closable adds a close button to each tab, and a middle click closes a tab too. When the open tab closes, the tab before it opens.

The control's addTab() method opens a new tab, given a title and the pane content as HTML. Fetch the control with jax.fetchControl(element, 'tab'), as the Add a tab button does. Scripts inside the content run when the pane is added.

blade
<div id="uiClosableTabs" class="control-tabs secondary-tabs" data-control="tab" data-closable>
    <ul class="nav nav-tabs">
        <li class="active">
            <a href="javascript:;">
                index.html
            </a>
        </li>
        <li>
            <a href="javascript:;">
                app.css
            </a>
        </li>
    </ul>
    <div class="tab-content">
        <div class="tab-pane active">
            <p class="mb-0">
                Close a tab with its cross, or with a middle click.
            </p>
        </div>
        <div class="tab-pane">
            <p class="mb-0">
                The stylesheet tab.
            </p>
        </div>
    </div>
</div>

<button
    type="button"
    class="btn-solid theme-secondary mt-4"
    onclick="jax.fetchControl(document.getElementById('uiClosableTabs'), 'tab').addTab('untitled.txt', '<p class=&quot;mb-0&quot;>Added from JavaScript.</p>')">
    Add a tab
</button>

Modified tabs ​

A tab:modified event dispatched from inside a pane marks its tab as modified, which swaps the close button for a dot. With data-close-confirmation, closing a modified tab asks first, with that text, and stays open when the visitor cancels. A tab:unmodified event clears the mark. Both events must bubble, as in the example.

blade
<div
    id="uiModifiedTabs"
    class="control-tabs secondary-tabs"
    data-control="tab"
    data-closable
    data-close-confirmation="This file has unsaved changes. Close it anyway?">
    <ul class="nav nav-tabs">
        <li class="active">
            <a href="javascript:;">
                notes.md
            </a>
        </li>
        <li>
            <a href="javascript:;">
                todo.md
            </a>
        </li>
    </ul>
    <div class="tab-content">
        <div class="tab-pane active">
            <textarea class="form-control" rows="3" placeholder="Type here to mark the tab as modified"></textarea>
        </div>
        <div class="tab-pane">
            <textarea class="form-control" rows="3" placeholder="Type here to mark the tab as modified"></textarea>
        </div>
    </div>
</div>

<script>
    document.getElementById('uiModifiedTabs').addEventListener('input', (ev) => {
        ev.target.dispatchEvent(new CustomEvent('tab:modified', { bubbles: true }));
    });
</script>

Data attributes ​

Set these on the control-tabs element.

AttributeDescription
data-control="tab"enables the control.
data-linkablekeeps the open tab in the address hash, and opens the tab matching the hash on page load.
data-closableadds a close button to each tab.
data-close-confirmationthe question to ask before a modified tab closes.
data-pane-classesclasses added to the panes that addTab() creates.
data-title-as-file-namesshows only the file name part of the titles that addTab() and updateTab() set.
data-max-title-symbolsshortens those titles to this many characters, keeping the end.

On a tab, data-tab-id gives it an identifier for goTo(), and data-tab-url on its anchor sets the address shown when it opens.

When an AJAX request fails validation, the control opens the tab that holds the first invalid field and focuses the field.

JavaScript API ​

Fetch the control with jax.fetchControl(element, 'tab'), then call:

MethodDescription
addTab(title, content, identifier, tabClass)adds a tab and opens it. content is HTML or an element, and the optional identifier sets its data-tab-id.
updateTab(tab, title, content)replaces a tab's title and content.
closeTab(tab, force)closes a tab. force skips the close confirmation.
modifyTab(tab)marks a tab as modified.
unmodifyTab(tab)clears the modified mark.
goTo(identifier)opens the tab with this data-tab-id.
goToIndex(index)opens the tab at this position, from 0.
goToPane(pane)opens the tab of this pane.
goToElement(element)opens the tab that contains the element.
prev() / next()opens the tab before or after the open one.

A tab argument is a tab's li, its pane, or a selector of either inside the control.

Events ​

EventDescription
tab:showndispatched on a tab's anchor after its tab opens.
tab:shown-linkabledispatched on the anchor of the tab opened from the address hash.
tab:init-tabdispatched on the control after a tab is set up, with detail.tab and detail.pane.
tab:before-closedispatched on the control before a tab closes, with the pane as detail.relatedTarget. preventDefault() keeps it open.
tab:closeddispatched on the control after a tab closes, with detail.tab and detail.pane.
tab:after-all-closeddispatched on the control when its last tab closes.

Dispatch these, with bubbles: true, on a tab, a pane or any element inside them:

EventDescription
tab:closecloses the tab. detail: { force: true } skips the close confirmation.
tab:modifiedmarks the tab as modified.
tab:unmodifiedclears the modified mark.