Now.js Framework Documentation

Now.js Framework Documentation

TabsComponent - Tabs Component for Now.js

EN 22 Nov 2025 09:04

TabsComponent - Tabs Component for Now.js

This document describes TabsComponent, a production-ready tabs component with full ARIA accessibility support and keyboard navigation.

📋 Table of Contents

  1. Overview
  2. Installation and Import
  3. Basic Usage
  4. HTML Attributes
  5. Configuration Options
  6. Keyboard Navigation
  7. Accessibility
  8. Events
  9. JavaScript API
  10. Usage Examples
  11. API Reference
  12. Best Practices
  13. Common Pitfalls

Overview

TabsComponent is a production-ready tabs component with complete ARIA support and keyboard navigation capabilities.

Key Features

  • Auto-initialization: Configure via data-component="tabs" instantly
  • Full ARIA Support: Complete attributes for screen readers
  • Keyboard Navigation: Supports Arrow keys, Home, End
  • Data Attribute Configuration: Set all options via HTML
  • Event Callbacks: Lifecycle event callbacks
  • Animations: Transition animation support
  • Dynamic Tab Management: Add/remove tabs in real-time
  • Horizontal and Vertical: Choose display orientation

When to Use TabsComponent

Use TabsComponent when:

  • You need to organize content into switchable categories
  • You want to save screen space by hiding some content
  • You need users to view data from different perspectives
  • You require fully accessible UI
  • You need tabs that work on both desktop and mobile

Don't use TabsComponent when:

  • You have minimal content that fits on one page
  • You want users to see all content simultaneously (use accordion instead)
  • You have sequential steps (use stepper/wizard instead)

Installation and Import

TabsComponent is loaded with Now.js Framework and ready to use via window object:

// No import needed - ready to use
console.log(window.TabsComponent); // TabsComponent object

Dependencies

  • Now.js Core – Main framework
  • CSS (tabs.css) – Tab styles (imported via main.css)

Basic Usage

<div data-component="tabs">
  <div class="tab-buttons" role="tablist">
    <button class="tab-button" data-tab="overview" role="tab">Overview</button>
    <button class="tab-button" data-tab="features" role="tab">Features</button>
    <button class="tab-button" data-tab="usage" role="tab">Usage</button>
  </div>
  <div class="tab-content">
    <div class="tab-pane" data-tab="overview" role="tabpanel">
      <h3>Overview</h3>
      <p>Overview content...</p>
    </div>
    <div class="tab-pane" data-tab="features" role="tabpanel">
      <h3>Features</h3>
      <p>Feature list...</p>
    </div>
    <div class="tab-pane" data-tab="usage" role="tabpanel">
      <h3>Usage</h3>
      <p>How to use...</p>
    </div>
  </div>
</div>

2. JavaScript Usage

const element = document.querySelector('#my-tabs');

const instance = TabsComponent.create(element, {
  defaultTab: 'features',
  keyboard: true,
  animation: true,
  onTabChange: (tabId, previousTab) => {
    console.log(`Changed from ${previousTab} to ${tabId}`);
  }
});

3. Auto-initialization

TabsComponent automatically creates instances for elements with data-component="tabs":

<!-- Automatically created when page loads -->
<div data-component="tabs">
  <!-- Tab content -->
</div>

HTML Attributes

TabsComponent supports these data attributes for configuration:

Basic Attributes

Attribute Type Default Description
data-default-tab string null ID of the default active tab
data-keyboard boolean true Enable/disable keyboard navigation
data-animation boolean true Enable/disable tab change animations
data-animation-duration number 300 Animation duration (milliseconds)
data-orientation string 'horizontal' Display orientation (horizontal or vertical)
data-aria-label string 'Tabs' ARIA label for tablist
data-lazy boolean false Enable lazy loading of tab content
data-debug boolean false Enable debug mode (show console logs)

Example Attribute Usage

<div data-component="tabs"
     data-default-tab="settings"
     data-keyboard="true"
     data-animation="true"
     data-orientation="horizontal">
  <!-- Tab content -->
</div>

Configuration Options

When creating an instance via JavaScript, all options can be configured:

const instance = TabsComponent.create(element, {
  // Basic settings
  defaultTab: null,           // Default tab (null = first tab)
  keyboard: true,             // Enable keyboard navigation
  animation: true,            // Enable animations
  animationDuration: 300,     // Animation duration (ms)

  // Behavior
  destroyOnHidden: false,     // Destroy instance when element is removed
  lazy: false,                // Lazy load content

  // Accessibility
  ariaLabel: 'Tabs',          // ARIA label for tablist
  orientation: 'horizontal',  // 'horizontal' or 'vertical'

  // Event callbacks
  onInit: (instance) => {
    console.log('Tabs initialized');
  },
  beforeTabChange: (newTabId, oldTabId) => {
    // return false to cancel tab change
    return true;
  },
  onTabChange: (newTabId, oldTabId) => {
    console.log(`Changed from ${oldTabId} to ${newTabId}`);
  },
  onDestroy: (instance) => {
    console.log('Tabs destroyed');
  },

  // Debug
  debug: false                // Enable console logging
});

Keyboard Navigation

TabsComponent supports keyboard navigation following ARIA standards:

Supported Keys

Key Action
(Arrow Right) Next tab (wraps to first tab at end)
(Arrow Left) Previous tab (wraps to last tab at start)
(Arrow Down) Next tab (for vertical orientation)
(Arrow Up) Previous tab (for vertical orientation)
Home First tab
End Last tab
Tab Move focus to tab panel

Usage Example

<!-- Click on tab then use Arrow keys to navigate -->
<div data-component="tabs" data-keyboard="true">
  <div class="tab-buttons" role="tablist">
    <button class="tab-button" data-tab="tab1">Tab 1</button>
    <button class="tab-button" data-tab="tab2">Tab 2</button>
    <button class="tab-button" data-tab="tab3">Tab 3</button>
  </div>
  <!-- panels -->
</div>

Disable Keyboard Navigation

<div data-component="tabs" data-keyboard="false">
  <!-- Tabs without keyboard support -->
</div>

Accessibility

TabsComponent follows ARIA standards completely.

ARIA Attributes Used

On Tablist Container

  • role="tablist" - Identifies as tab list
  • aria-orientation="horizontal|vertical" - Tab direction
  • aria-label="..." - Label for screen readers

On Tab Buttons

  • role="tab" - Identifies as tab button
  • aria-selected="true|false" - Selection state
  • aria-controls="panel-id" - Links to panel
  • tabindex="0|-1" - Focus management
  • id="tab-id" - Unique ID

On Tab Panels

  • role="tabpanel" - Identifies as tab content area
  • aria-labelledby="tab-id" - Links to tab button
  • tabindex="0" - Focusable
  • hidden - Hide/show panel
  • id="panel-id" - Unique ID

Complete HTML Example

<div data-component="tabs">
  <!-- Tablist -->
  <div class="tab-buttons" role="tablist" aria-orientation="horizontal" aria-label="Product Information">
    <button class="tab-button active"
            data-tab="description"
            role="tab"
            aria-selected="true"
            aria-controls="panel-description"
            tabindex="0"
            id="tab-description">
      Description
    </button>
    <button class="tab-button"
            data-tab="specs"
            role="tab"
            aria-selected="false"
            aria-controls="panel-specs"
            tabindex="-1"
            id="tab-specs">
      Specs
    </button>
  </div>

  <!-- Tab Panels -->
  <div class="tab-content">
    <div class="tab-pane active"
         data-tab="description"
         role="tabpanel"
         aria-labelledby="tab-description"
         tabindex="0"
         id="panel-description">
      Description content...
    </div>
    <div class="tab-pane"
         data-tab="specs"
         role="tabpanel"
         aria-labelledby="tab-specs"
         tabindex="0"
         id="panel-specs"
         hidden>
      Specs content...
    </div>
  </div>
</div>

Events

TabsComponent dispatches events when changes occur.

Event Types

Event When Details
tabs:tabchange Tab changed successfully {instance, tabId, previousTab, button, panel}

Listening to Events

const element = document.querySelector('[data-component="tabs"]');

element.addEventListener('tabs:tabchange', (e) => {
  console.log('Tab changed to:', e.detail.tabId);
  console.log('Previous tab:', e.detail.previousTab);
  console.log('Tab button:', e.detail.button);
  console.log('Tab panel:', e.detail.panel);
  console.log('Instance:', e.detail.instance);
});

Event Callbacks

const instance = TabsComponent.create(element, {
  // Called when component is created
  onInit: function() {
    console.log('Tabs initialized', this);
  },

  // Called before tab change (can cancel)
  beforeTabChange: function(newTabId, oldTabId) {
    if (newTabId === 'restricted') {
      alert('Cannot access this tab');
      return false; // Cancel tab change
    }
    return true; // Allow tab change
  },

  // Called after successful tab change
  onTabChange: function(newTabId, oldTabId) {
    console.log(`Changed from ${oldTabId} to ${newTabId}`);

    // Save current tab to localStorage
    localStorage.setItem('lastTab', newTabId);
  },

  // Called when component is destroyed
  onDestroy: function() {
    console.log('Tabs destroyed');
  }
});

JavaScript API

TabsComponent provides methods for JavaScript control.

Create Instance

const instance = TabsComponent.create(element, options);

Parameters:

  • element (HTMLElement) - Element to create tabs on
  • options (Object) - Configuration options

Returns: Instance object

Get Instance

// From element directly
const instance = TabsComponent.getInstance(element);

// From instance property
const instance = element.tabsInstance;

Switch Tab

// Via instance method
instance.switchTab('tab-id');

// Or via static method
TabsComponent.switchTab(instance, 'tab-id');

Parameters:

  • tabId (string) - ID of tab to switch to

Returns: boolean - true if successful, false if failed

Get Active Tab

const activeTab = instance.getActiveTab();
console.log('Current tab:', activeTab); // 'tab-id'

Returns: string - ID of currently active tab

Refresh Instance

// Re-scan for buttons and panels
instance.refresh();

Use when dynamically adding/removing tabs.

Destroy Instance

// Destroy instance and clean up resources
instance.destroy();

Removes all event listeners and ARIA attributes.

Usage Examples

1. Basic Tabs

<div data-component="tabs">
  <div class="tab-buttons">
    <button class="tab-button" data-tab="home">Home</button>
    <button class="tab-button" data-tab="about">About</button>
    <button class="tab-button" data-tab="contact">Contact</button>
  </div>
  <div class="tab-content">
    <div class="tab-pane" data-tab="home">
      <h2>Home</h2>
      <p>Welcome...</p>
    </div>
    <div class="tab-pane" data-tab="about">
      <h2>About Us</h2>
      <p>We are...</p>
    </div>
    <div class="tab-pane" data-tab="contact">
      <h2>Contact Us</h2>
      <p>Phone: 02-xxx-xxxx</p>
    </div>
  </div>
</div>

2. Set Default Tab

<div data-component="tabs" data-default-tab="profile">
  <div class="tab-buttons">
    <button class="tab-button" data-tab="dashboard">Dashboard</button>
    <button class="tab-button" data-tab="profile">Profile</button>
    <button class="tab-button" data-tab="settings">Settings</button>
  </div>
  <div class="tab-content">
    <!-- panels -->
  </div>
</div>

3. Vertical Tabs

<div data-component="tabs" data-orientation="vertical" style="display: flex;">
  <div class="tab-buttons" style="flex-direction: column; border-right: 1px solid #ddd;">
    <button class="tab-button" data-tab="general">General</button>
    <button class="tab-button" data-tab="security">Security</button>
    <button class="tab-button" data-tab="privacy">Privacy</button>
  </div>
  <div class="tab-content" style="flex: 1; padding-left: 20px;">
    <!-- panels -->
  </div>
</div>

4. JavaScript Control

<div id="my-tabs" data-component="tabs">
  <!-- tabs content -->
</div>

<button onclick="switchToTab('settings')">Go to Settings</button>

<script>
function switchToTab(tabId) {
  const instance = TabsComponent.getInstance('#my-tabs');
  if (instance) {
    instance.switchTab(tabId);
  }
}
</script>

5. Save Current Tab to localStorage

const tabs = TabsComponent.create(element, {
  onInit: function() {
    // Load saved tab
    const savedTab = localStorage.getItem('currentTab');
    if (savedTab && this.tabIds.includes(savedTab)) {
      this.switchTab(savedTab);
    }
  },
  onTabChange: function(newTabId) {
    // Save current tab
    localStorage.setItem('currentTab', newTabId);
  }
});

6. Confirm Before Tab Change

const tabs = TabsComponent.create(element, {
  beforeTabChange: function(newTabId, oldTabId) {
    if (oldTabId === 'form' && formHasUnsavedChanges()) {
      return confirm('You have unsaved changes. Do you want to leave this page?');
    }
    return true;
  }
});

7. Lazy Loading Content

const tabs = TabsComponent.create(element, {
  lazy: true,
  onTabChange: async function(tabId) {
    const panel = this.panels.find(p => p.dataset.tab === tabId);

    // Check if already loaded
    if (!panel.dataset.loaded) {
      panel.innerHTML = '<p>Loading...</p>';

      // Load data from API
      const response = await fetch(`/api/tabs/${tabId}`);
      const html = await response.text();

      panel.innerHTML = html;
      panel.dataset.loaded = 'true';
    }
  }
});

8. Dynamic Tabs

<div id="dynamic-tabs" data-component="tabs">
  <div class="tab-buttons"></div>
  <div class="tab-content"></div>
</div>

<button onclick="addTab()">Add Tab</button>

<script>
function addTab() {
  const instance = TabsComponent.getInstance('#dynamic-tabs');
  const newTabId = `tab-${Date.now()}`;

  // Add tab button
  const button = document.createElement('button');
  button.className = 'tab-button';
  button.dataset.tab = newTabId;
  button.textContent = `Tab ${instance.tabIds.length + 1}`;
  instance.element.querySelector('.tab-buttons').appendChild(button);

  // Add panel
  const panel = document.createElement('div');
  panel.className = 'tab-pane';
  panel.dataset.tab = newTabId;
  panel.textContent = `Content for ${newTabId}`;
  instance.element.querySelector('.tab-content').appendChild(panel);

  // Refresh instance
  instance.refresh();

  // Switch to new tab
  instance.switchTab(newTabId);
}
</script>

API Reference

TabsComponent.create(element, options)

Create a new tabs instance.

Parameters:

  • element (HTMLElement) - Container element
  • options (Object) - Configuration options

Returns: Instance object

TabsComponent.getInstance(element)

Get existing instance.

Parameters:

  • element (HTMLElement | string) - Element or selector

Returns: Instance object or null

TabsComponent.switchTab(instance, tabId, initial)

Switch to specified tab.

Parameters:

  • instance (Object) - Tabs instance
  • tabId (string) - Tab ID
  • initial (boolean) - Is initial activation

Returns: boolean

TabsComponent.refresh(instance)

Re-scan for buttons and panels.

Parameters:

  • instance (Object) - Tabs instance

TabsComponent.destroy(instance)

Destroy instance.

Parameters:

  • instance (Object) - Tabs instance

Instance Methods

instance.switchTab(tabId)

Switch tab.

Parameters:

  • tabId (string) - Tab ID

Returns: boolean

instance.getActiveTab()

Get current tab ID.

Returns: string

instance.refresh()

Refresh instance.

instance.destroy()

Destroy instance.

Best Practices

✅ Do

  1. Use correct ARIA attributes

    <div class="tab-buttons" role="tablist">
     <button class="tab-button" data-tab="tab1" role="tab">Tab 1</button>
    </div>
  2. Use meaningful IDs

    <button data-tab="user-profile">Profile</button>
    <button data-tab="user-settings">Settings</button>
  3. Group related content

    • Tabs should contain related content
    • Don't have too many tabs (recommend 3-7 tabs)
  4. Enable keyboard navigation

    <div data-component="tabs" data-keyboard="true">
  5. Save tab state

    onTabChange: (tabId) => {
     localStorage.setItem('activeTab', tabId);
    }

❌ Don't

  1. Don't nest tabs multiple levels

    <!-- ❌ Bad -->
    <div data-component="tabs">
     <div class="tab-pane">
       <div data-component="tabs">
         <!-- Nested tabs - confusing -->
       </div>
     </div>
    </div>
  2. Don't use tabs for main navigation

    <!-- ❌ Bad - use menu instead -->
    <div data-component="tabs">
     <button data-tab="home">Home</button>
     <button data-tab="products">Products</button>
     <button data-tab="contact">Contact</button>
    </div>
  3. Don't forget role attributes

    <!-- ❌ Bad - missing ARIA -->
    <div data-component="tabs">
     <div class="tab-buttons">
       <button data-tab="tab1">Tab 1</button>
     </div>
    </div>
    
    <!-- ✅ Good -->
    <div data-component="tabs">
     <div class="tab-buttons" role="tablist">
       <button class="tab-button" data-tab="tab1" role="tab">Tab 1</button>
     </div>
    </div>

Common Pitfalls

⚠️ Common Mistakes

  1. Forgetting data-tab attribute

    <!-- ❌ Won't work -->
    <button class="tab-button">Tab 1</button>
    
    <!-- ✅ Correct -->
    <button class="tab-button" data-tab="tab1">Tab 1</button>
  2. Mismatched button and panel IDs

    <!-- ❌ Won't work -->
    <button data-tab="profile">Profile</button>
    <div data-tab="user-profile">...</div>
    
    <!-- ✅ Correct -->
    <button data-tab="profile">Profile</button>
    <div data-tab="profile">...</div>
  3. Missing .tab-button or .tab-pane class

    <!-- ❌ Won't work -->
    <button data-tab="tab1">Tab 1</button>
    <div data-tab="tab1">Content</div>
    
    <!-- ✅ Correct -->
    <button class="tab-button" data-tab="tab1">Tab 1</button>
    <div class="tab-pane" data-tab="tab1">Content</div>

💡 Tips

  1. Use CSS transitions for smooth animations

    .tab-pane {
     opacity: 0;
     transition: opacity 0.3s ease;
    }
    
    .tab-pane.active {
     opacity: 1;
    }
  2. Use beforeTabChange for validation

    beforeTabChange: (newTab, oldTab) => {
     if (oldTab === 'form' && !isFormValid()) {
       alert('Please fill in all required fields');
       return false;
     }
     return true;
    }
  3. Use refresh() after adding/removing tabs

    // Add new tab
    addNewTab();
    
    // Refresh instance
    instance.refresh();

Additional Resources