Now.js Framework Documentation
TabsComponent - คอมโพเนนต์แท็บสำหรับ Now.js
TabsComponent - คอมโพเนนต์แท็บสำหรับ Now.js
เอกสารฉบับนี้อธิบาย TabsComponent ซึ่งเป็นคอมโพเนนต์สำหรับสร้างแท็บ (Tabs) ที่มีความสามารถครบถ้วน รองรับการเข้าถึง (Accessibility) และการนำทางด้วยคีย์บอร์ด
📋 สารบัญ
- ภาพรวม
- การติดตั้งและนำเข้า
- การใช้งานพื้นฐาน
- คุณสมบัติ HTML
- ตัวเลือกการกำหนดค่า
- การนำทางด้วยคีย์บอร์ด
- การเข้าถึง (Accessibility)
- อีเวนต์
- JavaScript API
- ตัวอย่างการใช้งาน
- API Reference
- แนวทางปฏิบัติที่ดี
- ข้อควรระวัง
ภาพรวม
TabsComponent เป็นคอมโพเนนต์สำหรับสร้างแท็บที่มีคุณภาพระดับ Production พร้อมการรองรับ ARIA และการนำทางด้วยคีย์บอร์ดอย่างสมบูรณ์
ฟีเจอร์หลัก
- ✅ เริ่มทำงานอัตโนมัติ: กำหนดค่าผ่าน
data-component="tabs"ได้ทันที - ✅ รองรับ ARIA เต็มรูปแบบ: มี attributes ที่จำเป็นสำหรับ screen readers
- ✅ การนำทางด้วยคีย์บอร์ด: รองรับ Arrow keys, Home, End
- ✅ กำหนดค่าผ่าน Data Attributes: ตั้งค่าทั้งหมดผ่าน HTML ได้
- ✅ Event Callbacks: ฟังก์ชัน callback สำหรับ lifecycle events
- ✅ แอนิเมชัน: รองรับ transition animations
- ✅ จัดการแท็บแบบไดนามิก: เพิ่ม/ลบแท็บได้แบบ real-time
- ✅ รองรับทั้งแนวนอนและแนวตั้ง: เลือกทิศทางการแสดงผลได้
เมื่อไหร่ควรใช้ TabsComponent
✅ ใช้ TabsComponent เมื่อ:
- ต้องการแบ่งเนื้อหาออกเป็นหมวดหมู่ที่สลับกันได้
- ต้องการประหยัดพื้นที่หน้าจอโดยซ่อนเนื้อหาบางส่วน
- ต้องการให้ผู้ใช้เลือกดูข้อมูลในมุมมองต่างๆ
- ต้องการ UI ที่รองรับการเข้าถึงอย่างเต็มรูปแบบ
- ต้องการแท็บที่ทำงานได้ทั้งบนเดสก์ท็อปและมือถือ
❌ ไม่ควรใช้ TabsComponent เมื่อ:
- มีเนื้อหาเพียงเล็กน้อยที่แสดงได้ทั้งหมดในหน้าเดียว
- ต้องการให้ผู้ใช้เห็นเนื้อหาทั้งหมดพร้อมกัน (ใช้ accordion แทน)
- มีขั้นตอนที่ต้องทำตามลำดับ (ใช้ stepper/wizard แทน)
การติดตั้งและนำเข้า
TabsComponent โหลดมาพร้อมกับ Now.js Framework และพร้อมใช้งานทันทีผ่าน window object:
// ไม่ต้อง import - พร้อมใช้งานทันที
console.log(window.TabsComponent); // อ็อบเจ็กต์ TabsComponentสิ่งที่ต้องพึ่งพา
- Now.js Core – Framework หลัก
- CSS (tabs.css) – สไตล์สำหรับแท็บ (นำเข้าผ่าน main.css)
การใช้งานพื้นฐาน
1. การใช้งานผ่าน HTML (แนะนำ)
<div data-component="tabs">
<div class="tab-buttons" role="tablist">
<button class="tab-button" data-tab="overview" role="tab">ภาพรวม</button>
<button class="tab-button" data-tab="features" role="tab">ฟีเจอร์</button>
<button class="tab-button" data-tab="usage" role="tab">การใช้งาน</button>
</div>
<div class="tab-content">
<div class="tab-pane" data-tab="overview" role="tabpanel">
<h3>ภาพรวม</h3>
<p>เนื้อหาภาพรวม...</p>
</div>
<div class="tab-pane" data-tab="features" role="tabpanel">
<h3>ฟีเจอร์</h3>
<p>รายการฟีเจอร์...</p>
</div>
<div class="tab-pane" data-tab="usage" role="tabpanel">
<h3>การใช้งาน</h3>
<p>วิธีการใช้งาน...</p>
</div>
</div>
</div>2. การใช้งานผ่าน JavaScript
const element = document.querySelector('#my-tabs');
const instance = TabsComponent.create(element, {
defaultTab: 'features',
keyboard: true,
animation: true,
onTabChange: (tabId, previousTab) => {
console.log(`เปลี่ยนจาก ${previousTab} ไป ${tabId}`);
}
});3. การเริ่มทำงานอัตโนมัติ
TabsComponent จะสร้าง instance อัตโนมัติสำหรับ elements ที่มี data-component="tabs":
<!-- จะถูกสร้างอัตโนมัติเมื่อหน้าโหลด -->
<div data-component="tabs">
<!-- เนื้อหาแท็บ -->
</div>คุณสมบัติ HTML
TabsComponent รองรับ data attributes เหล่านี้สำหรับการกำหนดค่า:
คุณสมบัติพื้นฐาน
| Attribute | ประเภท | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
data-default-tab |
string | null |
ID ของแท็บที่จะเปิดเป็นค่าเริ่มต้น |
data-keyboard |
boolean | true |
เปิด/ปิดการนำทางด้วยคีย์บอร์ด |
data-animation |
boolean | true |
เปิด/ปิดแอนิเมชันการเปลี่ยนแท็บ |
data-animation-duration |
number | 300 |
ระยะเวลาแอนิเมชัน (มิลลิวินาที) |
data-orientation |
string | 'horizontal' |
ทิศทางการแสดงผล (horizontal หรือ vertical) |
data-aria-label |
string | 'Tabs' |
ARIA label สำหรับ tablist |
data-lazy |
boolean | false |
โหลดเนื้อหาแท็บแบบ lazy loading |
data-debug |
boolean | false |
เปิดโหมด debug (แสดง console logs) |
ตัวอย่างการใช้ Attributes
<div data-component="tabs"
data-default-tab="settings"
data-keyboard="true"
data-animation="true"
data-orientation="horizontal">
<!-- เนื้อหาแท็บ -->
</div>ตัวเลือกการกำหนดค่า
เมื่อสร้าง instance ด้วย JavaScript สามารถกำหนดค่าได้ทั้งหมด:
const instance = TabsComponent.create(element, {
// การตั้งค่าพื้นฐาน
defaultTab: null, // แท็บเริ่มต้น (null = แท็บแรก)
keyboard: true, // เปิดการนำทางด้วยคีย์บอร์ด
animation: true, // เปิดแอนิเมชัน
animationDuration: 300, // ระยะเวลาแอนิเมชัน (ms)
// พฤติกรรม
destroyOnHidden: false, // ทำลาย instance เมื่อ element ถูกลบ
lazy: false, // โหลดเนื้อหาแบบ lazy
// การเข้าถึง
ariaLabel: 'Tabs', // ARIA label สำหรับ tablist
orientation: 'horizontal', // 'horizontal' หรือ 'vertical'
// Event callbacks
onInit: (instance) => {
console.log('Tabs initialized');
},
beforeTabChange: (newTabId, oldTabId) => {
// return false เพื่อยกเลิกการเปลี่ยนแท็บ
return true;
},
onTabChange: (newTabId, oldTabId) => {
console.log(`Changed from ${oldTabId} to ${newTabId}`);
},
onDestroy: (instance) => {
console.log('Tabs destroyed');
},
// Debug
debug: false // เปิด console logging
});การนำทางด้วยคีย์บอร์ด
TabsComponent รองรับการนำทางด้วยคีย์บอร์ดตามมาตรฐาน ARIA:
คีย์ที่รองรับ
| คีย์ | การทำงาน |
|---|---|
→ (Arrow Right) |
ไปแท็บถัดไป (วนกลับไปแท็บแรกเมื่อถึงแท็บสุดท้าย) |
← (Arrow Left) |
ไปแท็บก่อนหน้า (วนไปแท็บสุดท้ายเมื่ออยู่แท็บแรก) |
↓ (Arrow Down) |
ไปแท็บถัดไป (สำหรับแนวตั้ง) |
↑ (Arrow Up) |
ไปแท็บก่อนหน้า (สำหรับแนวตั้ง) |
Home |
ไปแท็บแรก |
End |
ไปแท็บสุดท้าย |
Tab |
ย้าย focus ไปยัง tab panel |
ตัวอย่างการใช้งาน
<!-- คลิกที่แท็บแล้วใช้ Arrow keys เพื่อนำทาง -->
<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>ปิดการนำทางด้วยคีย์บอร์ด
<div data-component="tabs" data-keyboard="false">
<!-- แท็บที่ไม่รองรับคีย์บอร์ด -->
</div>การเข้าถึง (Accessibility)
TabsComponent ปฏิบัติตามมาตรฐาน ARIA อย่างเต็มรูปแบบ
ARIA Attributes ที่ใช้
บน Tablist Container
role="tablist"- ระบุว่าเป็นรายการแท็บaria-orientation="horizontal|vertical"- ทิศทางของแท็บaria-label="..."- ป้ายกำกับสำหรับ screen readers
บน Tab Buttons
role="tab"- ระบุว่าเป็นปุ่มแท็บaria-selected="true|false"- สถานะการเลือกaria-controls="panel-id"- เชื่อมโยงกับ paneltabindex="0|-1"- จัดการ focusid="tab-id"- ID ที่ไม่ซ้ำ
บน Tab Panels
role="tabpanel"- ระบุว่าเป็นพื้นที่เนื้อหาแท็บaria-labelledby="tab-id"- เชื่อมโยงกับปุ่มแท็บtabindex="0"- ทำให้ focus ได้hidden- ซ่อน/แสดง panelid="panel-id"- ID ที่ไม่ซ้ำ
ตัวอย่าง HTML ที่สมบูรณ์
<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">
คำอธิบาย
</button>
<button class="tab-button"
data-tab="specs"
role="tab"
aria-selected="false"
aria-controls="panel-specs"
tabindex="-1"
id="tab-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">
เนื้อหาคำอธิบาย...
</div>
<div class="tab-pane"
data-tab="specs"
role="tabpanel"
aria-labelledby="tab-specs"
tabindex="0"
id="panel-specs"
hidden>
เนื้อหาสเปค...
</div>
</div>
</div>อีเวนต์
TabsComponent ส่งอีเวนต์เมื่อมีการเปลี่ยนแปลง
ประเภทของอีเวนต์
| อีเวนต์ | เมื่อไหร่ | รายละเอียด |
|---|---|---|
tabs:tabchange |
เมื่อเปลี่ยนแท็บสำเร็จ | {instance, tabId, previousTab, button, panel} |
การฟังอีเวนต์
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, {
// เรียกเมื่อ component ถูกสร้าง
onInit: function() {
console.log('Tabs initialized', this);
},
// เรียกก่อนเปลี่ยนแท็บ (สามารถยกเลิกได้)
beforeTabChange: function(newTabId, oldTabId) {
if (newTabId === 'restricted') {
alert('ไม่สามารถเข้าถึงแท็บนี้ได้');
return false; // ยกเลิกการเปลี่ยนแท็บ
}
return true; // อนุญาตให้เปลี่ยนแท็บ
},
// เรียกหลังเปลี่ยนแท็บสำเร็จ
onTabChange: function(newTabId, oldTabId) {
console.log(`Changed from ${oldTabId} to ${newTabId}`);
// บันทึกแท็บปัจจุบันลง localStorage
localStorage.setItem('lastTab', newTabId);
},
// เรียกเมื่อ component ถูกทำลาย
onDestroy: function() {
console.log('Tabs destroyed');
}
});JavaScript API
TabsComponent มี methods สำหรับควบคุมผ่าน JavaScript
สร้าง Instance
const instance = TabsComponent.create(element, options);Parameters:
element(HTMLElement) - Element ที่จะสร้างแท็บoptions(Object) - ตัวเลือกการกำหนดค่า
Returns: Instance object
อ่าน Instance
// จาก element โดยตรง
const instance = TabsComponent.getInstance(element);
// จาก instance property
const instance = element.tabsInstance;เปลี่ยนแท็บ
// ผ่าน instance method
instance.switchTab('tab-id');
// หรือผ่าน static method
TabsComponent.switchTab(instance, 'tab-id');Parameters:
tabId(string) - ID ของแท็บที่ต้องการเปลี่ยนไป
Returns: boolean - true ถ้าเปลี่ยนสำเร็จ, false ถ้าไม่สำเร็จ
อ่านแท็บปัจจุบัน
const activeTab = instance.getActiveTab();
console.log('Current tab:', activeTab); // 'tab-id'Returns: string - ID ของแท็บที่กำลังแสดงอยู่
รีเฟรช Instance
// สแกนหาปุ่มและ panels ใหม่
instance.refresh();ใช้เมื่อมีการเพิ่ม/ลบแท็บแบบไดนามิก
ทำลาย Instance
// ทำลาย instance และเคลียร์ทรัพยากร
instance.destroy();ลบ event listeners และ ARIA attributes ทั้งหมด
ตัวอย่างการใช้งาน
1. แท็บพื้นฐาน
<div data-component="tabs">
<div class="tab-buttons">
<button class="tab-button" data-tab="home">หน้าแรก</button>
<button class="tab-button" data-tab="about">เกี่ยวกับ</button>
<button class="tab-button" data-tab="contact">ติดต่อ</button>
</div>
<div class="tab-content">
<div class="tab-pane" data-tab="home">
<h2>หน้าแรก</h2>
<p>ยินดีต้อนรับ...</p>
</div>
<div class="tab-pane" data-tab="about">
<h2>เกี่ยวกับเรา</h2>
<p>เราคือ...</p>
</div>
<div class="tab-pane" data-tab="contact">
<h2>ติดต่อเรา</h2>
<p>โทร: 02-xxx-xxxx</p>
</div>
</div>
</div>2. กำหนดแท็บเริ่มต้น
<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">โปรไฟล์</button>
<button class="tab-button" data-tab="settings">ตั้งค่า</button>
</div>
<div class="tab-content">
<!-- panels -->
</div>
</div>3. แท็บแนวตั้ง
<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">ทั่วไป</button>
<button class="tab-button" data-tab="security">ความปลอดภัย</button>
<button class="tab-button" data-tab="privacy">ความเป็นส่วนตัว</button>
</div>
<div class="tab-content" style="flex: 1; padding-left: 20px;">
<!-- panels -->
</div>
</div>4. ควบคุมด้วย JavaScript
<div id="my-tabs" data-component="tabs">
<!-- tabs content -->
</div>
<button onclick="switchToTab('settings')">ไปที่ตั้งค่า</button>
<script>
function switchToTab(tabId) {
const instance = TabsComponent.getInstance('#my-tabs');
if (instance) {
instance.switchTab(tabId);
}
}
</script>5. บันทึกแท็บปัจจุบันลง localStorage
const tabs = TabsComponent.create(element, {
onInit: function() {
// โหลดแท็บที่บันทึกไว้
const savedTab = localStorage.getItem('currentTab');
if (savedTab && this.tabIds.includes(savedTab)) {
this.switchTab(savedTab);
}
},
onTabChange: function(newTabId) {
// บันทึกแท็บปัจจุบัน
localStorage.setItem('currentTab', newTabId);
}
});6. ยืนยันก่อนเปลี่ยนแท็บ
const tabs = TabsComponent.create(element, {
beforeTabChange: function(newTabId, oldTabId) {
if (oldTabId === 'form' && formHasUnsavedChanges()) {
return confirm('มีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก ต้องการออกจากหน้านี้หรือไม่?');
}
return true;
}
});7. โหลดเนื้อหาแบบ Lazy Loading
const tabs = TabsComponent.create(element, {
lazy: true,
onTabChange: async function(tabId) {
const panel = this.panels.find(p => p.dataset.tab === tabId);
// ตรวจสอบว่าโหลดแล้วหรือยัง
if (!panel.dataset.loaded) {
panel.innerHTML = '<p>กำลังโหลด...</p>';
// โหลดข้อมูลจาก API
const response = await fetch(`/api/tabs/${tabId}`);
const html = await response.text();
panel.innerHTML = html;
panel.dataset.loaded = 'true';
}
}
});8. แท็บแบบไดนามิก
<div id="dynamic-tabs" data-component="tabs">
<div class="tab-buttons"></div>
<div class="tab-content"></div>
</div>
<button onclick="addTab()">เพิ่มแท็บ</button>
<script>
function addTab() {
const instance = TabsComponent.getInstance('#dynamic-tabs');
const newTabId = `tab-${Date.now()}`;
// เพิ่มปุ่มแท็บ
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);
// เพิ่ม 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);
// รีเฟรช instance
instance.refresh();
// เปลี่ยนไปแท็บใหม่
instance.switchTab(newTabId);
}
</script>API Reference
TabsComponent.create(element, options)
สร้าง tabs instance ใหม่
Parameters:
element(HTMLElement) - Container elementoptions(Object) - Configuration options
Returns: Instance object
TabsComponent.getInstance(element)
อ่าน instance ที่มีอยู่
Parameters:
element(HTMLElement | string) - Element หรือ selector
Returns: Instance object หรือ null
TabsComponent.switchTab(instance, tabId, initial)
เปลี่ยนไปยังแท็บที่ระบุ
Parameters:
instance(Object) - Tabs instancetabId(string) - ID ของแท็บinitial(boolean) - เป็นการเปิดครั้งแรกหรือไม่
Returns: boolean
TabsComponent.refresh(instance)
สแกนหาปุ่มและ panels ใหม่
Parameters:
instance(Object) - Tabs instance
TabsComponent.destroy(instance)
ทำลาย instance
Parameters:
instance(Object) - Tabs instance
Instance Methods
instance.switchTab(tabId)
เปลี่ยนแท็บ
Parameters:
tabId(string) - ID ของแท็บ
Returns: boolean
instance.getActiveTab()
อ่าน ID ของแท็บปัจจุบัน
Returns: string
instance.refresh()
รีเฟรช instance
instance.destroy()
ทำลาย instance
แนวทางปฏิบัติที่ดี
✅ ควรทำ
-
ใช้ ARIA attributes ที่ถูกต้อง
<div class="tab-buttons" role="tablist"> <button class="tab-button" data-tab="tab1" role="tab">Tab 1</button> </div> -
ใช้ ID ที่มีความหมาย
<button data-tab="user-profile">โปรไฟล์</button> <button data-tab="user-settings">ตั้งค่า</button> -
จัดกลุ่มเนื้อหาที่เกี่ยวข้องกัน
- แท็บควรมีเนื้อหาที่เกี่ยวข้องกัน
- ไม่ควรมีแท็บมากเกินไป (แนะนำ 3-7 แท็บ)
-
ใช้ keyboard navigation
<div data-component="tabs" data-keyboard="true"> -
บันทึกสถานะแท็บ
onTabChange: (tabId) => { localStorage.setItem('activeTab', tabId); }
❌ ไม่ควรทำ
-
อย่าซ้อนแท็บหลายชั้น
<!-- ❌ ไม่ดี --> <div data-component="tabs"> <div class="tab-pane"> <div data-component="tabs"> <!-- แท็บซ้อนแท็บ - สับสน --> </div> </div> </div> -
อย่าใช้แท็บสำหรับ navigation หลัก
<!-- ❌ ไม่ดี - ใช้ menu แทน --> <div data-component="tabs"> <button data-tab="home">หน้าแรก</button> <button data-tab="products">สินค้า</button> <button data-tab="contact">ติดต่อ</button> </div> -
อย่าลืม role attributes
<!-- ❌ ไม่ดี - ขาด ARIA --> <div data-component="tabs"> <div class="tab-buttons"> <button data-tab="tab1">Tab 1</button> </div> </div> <!-- ✅ ดี --> <div data-component="tabs"> <div class="tab-buttons" role="tablist"> <button class="tab-button" data-tab="tab1" role="tab">Tab 1</button> </div> </div>
ข้อควรระวัง
⚠️ ข้อผิดพลาดที่พบบ่อย
-
ลืมใส่ data-tab attribute
<!-- ❌ ไม่ทำงาน --> <button class="tab-button">Tab 1</button> <!-- ✅ ถูกต้อง --> <button class="tab-button" data-tab="tab1">Tab 1</button> -
ID ของปุ่มและ panel ไม่ตรงกัน
<!-- ❌ ไม่ทำงาน --> <button data-tab="profile">โปรไฟล์</button> <div data-tab="user-profile">...</div> <!-- ✅ ถูกต้อง --> <button data-tab="profile">โปรไฟล์</button> <div data-tab="profile">...</div> -
ไม่มี .tab-button หรือ .tab-pane class
<!-- ❌ ไม่ทำงาน --> <button data-tab="tab1">Tab 1</button> <div data-tab="tab1">Content</div> <!-- ✅ ถูกต้อง --> <button class="tab-button" data-tab="tab1">Tab 1</button> <div class="tab-pane" data-tab="tab1">Content</div>
💡 เคล็ดลับ
-
ใช้ CSS transitions สำหรับแอนิเมชันที่นุ่มนวล
.tab-pane { opacity: 0; transition: opacity 0.3s ease; } .tab-pane.active { opacity: 1; } -
ใช้ beforeTabChange เพื่อ validate
beforeTabChange: (newTab, oldTab) => { if (oldTab === 'form' && !isFormValid()) { alert('กรุณากรอกข้อมูลให้ครบถ้วน'); return false; } return true; } -
ใช้ refresh() หลังเพิ่ม/ลบแท็บ
// เพิ่มแท็บใหม่ addNewTab(); // รีเฟรช instance instance.refresh();