Now.js Framework Documentation

Now.js Framework Documentation

data-script

TH 15 Dec 2025 08:53

data-script

ภาพรวม

data-script ระบุ global function ที่จะเรียกเมื่อ template โหลดและพร้อมใช้งาน ทำให้สามารถ initialize JavaScript สำหรับ template components ได้

ใช้เมื่อ:

  • Initialize JavaScript เมื่อ template โหลด
  • ตั้งค่า event listeners หรือ third-party libraries
  • สร้าง cleanup functions สำหรับ navigation

ทำไมต้องใช้:

  • ✅ แยก HTML และ JS ออกจากกันอย่างชัดเจน
  • ✅ รับข้อมูลจาก API อัตโนมัติ
  • ✅ รองรับ cleanup functions
  • ✅ ทำงานร่วมกับ RouterManager

การใช้งานพื้นฐาน

Template กับ Script

profile.html:

<main data-script="initProfile">
  <h1 data-text="user.name"></h1>
  <form data-form="editProfile">
    <input type="text" name="bio" data-attr="value:user.bio">
    <button type="submit">บันทึก</button>
  </form>
</main>

main.js:

function initProfile(element, data) {
  console.log('โหลด Profile แล้ว!', element);
  console.log('ข้อมูลจาก API:', data);

  // Initialize components
  const form = element.querySelector('form');

  // Return cleanup function (ไม่บังคับ)
  return () => {
    console.log('Cleanup Profile');
  };
}

Syntax

<element data-script="functionName">
ส่วน คำอธิบาย
functionName ชื่อ global function ที่จะเรียก

Function Signature

function functionName(element, data) {
  // element: DOM element ที่มี data-script attribute
  // data: ข้อมูลจาก API (ถ้า route มี data-load-api)

  // โค้ด initialization

  // Return cleanup function (ไม่บังคับ)
  return () => {
    // โค้ด cleanup ทำงานเมื่อ navigate ออก
  };
}

Parameters

Parameter ประเภท คำอธิบาย
element HTMLElement DOM element ที่มี data-script
data Object ข้อมูลจาก API (ถ้าใช้ data-load-api)

Return Value

Return ผลลัพธ์
undefined ไม่มี cleanup
Function ถูกเรียกเมื่อ template ถูกลบ

ฟีเจอร์

1. Basic Initialization

<div class="chart-container" data-script="initChart">
  <canvas id="myChart"></canvas>
</div>
function initChart(element, data) {
  const canvas = element.querySelector('#myChart');
  const chart = new Chart(canvas, {
    type: 'bar',
    data: data.chartData
  });

  return () => {
    chart.destroy();
  };
}

2. กับ API Data

Router config:

routes: {
  '/dashboard': {
    template: 'dashboard.html',
    title: 'Dashboard',
    'data-load-api': 'api/dashboard/stats'
  }
}

dashboard.html:

<main data-script="initDashboard">
  <div id="stats"></div>
</main>
function initDashboard(element, data) {
  // data = { totalUsers: 150, revenue: 50000, ... }
  console.log('Stats:', data);

  renderStats(element.querySelector('#stats'), data);
}

3. หลาย Scripts

<div class="page">
  <header data-script="initHeader">
    <nav>...</nav>
  </header>

  <main data-script="initMainContent">
    <div class="content">...</div>
  </main>

  <aside data-script="initSidebar">
    <div class="widgets">...</div>
  </aside>
</div>

Functions ถูกเรียกตามลำดับ DOM (บนลงล่าง)

4. Cleanup Functions

function initTimer(element, data) {
  const timerEl = element.querySelector('#timer');

  // เริ่ม interval
  const intervalId = setInterval(() => {
    timerEl.textContent = new Date().toLocaleTimeString();
  }, 1000);

  // Return cleanup - ถูกเรียกเมื่อ navigate ออก
  return () => {
    clearInterval(intervalId);
  };
}

ตัวอย่างขั้นสูง

Interactive Map

<div class="map-page" data-script="initMap">
  <div id="map" style="height: 400px;"></div>
  <button id="center-btn">กลับตำแหน่งเดิม</button>
</div>
function initMap(element, data) {
  const mapEl = element.querySelector('#map');
  const centerBtn = element.querySelector('#center-btn');

  // Initialize Leaflet map
  const map = L.map(mapEl).setView([13.7, 100.5], 13);
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);

  // เพิ่ม markers จากข้อมูล
  data.locations.forEach(loc => {
    L.marker([loc.lat, loc.lng])
      .addTo(map)
      .bindPopup(loc.name);
  });

  // Event listeners
  const handleCenter = () => {
    map.setView([13.7, 100.5], 13);
  };
  centerBtn.addEventListener('click', handleCenter);

  // Cleanup
  return () => {
    centerBtn.removeEventListener('click', handleCenter);
    map.remove();
  };
}

ฟอร์มกับ Validation

<form data-form="register" data-script="initRegisterForm">
  <input type="email" name="email" id="email">
  <span class="error" id="email-error"></span>

  <input type="password" name="password" id="password">
  <span class="error" id="password-error"></span>

  <button type="submit">สมัครสมาชิก</button>
</form>
function initRegisterForm(element, data) {
  const emailInput = element.querySelector('#email');
  const passwordInput = element.querySelector('#password');

  let debounceTimer;

  const validateEmail = async () => {
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(async () => {
      const email = emailInput.value;
      // Validation logic
    }, 500);
  };

  emailInput.addEventListener('input', validateEmail);

  return () => {
    clearTimeout(debounceTimer);
    emailInput.removeEventListener('input', validateEmail);
  };
}

Live Chat

function initLiveChat(element, data) {
  const messagesEl = element.querySelector('#messages');
  const inputEl = element.querySelector('#message-input');
  const sendBtn = element.querySelector('#send-btn');

  // Connect WebSocket
  const ws = new WebSocket('wss://chat.example.com');

  ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    appendMessage(messagesEl, msg);
  };

  const sendMessage = () => {
    const text = inputEl.value.trim();
    if (text) {
      ws.send(JSON.stringify({ type: 'message', text }));
      inputEl.value = '';
    }
  };

  sendBtn.addEventListener('click', sendMessage);
  inputEl.addEventListener('keydown', (e) => {
    if (e.key === 'Enter') sendMessage();
  });

  // Cleanup - ปิด WebSocket
  return () => {
    ws.close();
    sendBtn.removeEventListener('click', sendMessage);
  };
}

API อ้างอิง

เมื่อ Scripts ทำงาน

Event พฤติกรรม
Template load Script ถูกเรียกหลัง DOM พร้อม
Route navigation Cleanup ก่อนหน้าถูกเรียก, script ใหม่ถูกเรียก
Manual cleanup เรียก RouterManager.cleanup()

ลำดับการทำงาน

  1. Template HTML ถูก render
  2. data-script functions ถูกเรียก (ตามลำดับ DOM)
  3. Cleanup functions ถูกเก็บไว้
  4. เมื่อ Navigate ออก: cleanup functions ถูกเรียก

ข้อควรระวัง

⚠️ 1. Function ต้องเป็น Global

// ❌ ไม่สามารถเข้าถึง - scoped เข้า DOMContentLoaded
document.addEventListener('DOMContentLoaded', () => {
  function initPage(element, data) { }
});

// ✅ Global function
function initPage(element, data) { }

// ✅ กำหนด global ชัดเจน
window.initPage = function(element, data) { };

⚠️ 2. Memory Leaks

// ❌ ไม่ดี - ไม่มี cleanup สำหรับ timer
function initClock(element, data) {
  setInterval(() => {
    element.querySelector('#time').textContent = new Date().toLocaleTimeString();
  }, 1000);
}

// ✅ ดี - มี cleanup
function initClock(element, data) {
  const timer = setInterval(() => {
    element.querySelector('#time').textContent = new Date().toLocaleTimeString();
  }, 1000);

  return () => clearInterval(timer);
}

⚠️ 3. Async Functions

// ✅ Async ทำงานได้ - cleanup ยังรองรับ
async function initDashboard(element, data) {
  const stats = await fetchStats();
  renderStats(stats);

  return () => {
    // Cleanup
  };
}

⚠️ 4. ตรวจสอบ Element Existence

// ❌ อาจ error ถ้า element ไม่มี
function initForm(element, data) {
  element.querySelector('#submit').addEventListener('click', submit);
}

// ✅ ตรวจสอบก่อน
function initForm(element, data) {
  const submitBtn = element.querySelector('#submit');
  if (!submitBtn) {
    console.warn('ไม่พบปุ่ม Submit');
    return;
  }
  submitBtn.addEventListener('click', submit);
}

⚠️ 5. ชื่อ Function ไม่ซ้ำกัน

// ❌ ชื่อเดียวกัน - อันที่สองเขียนทับอันแรก
function initForm(element, data) { console.log('Form 1'); }
function initForm(element, data) { console.log('Form 2'); }

// ✅ ชื่อไม่ซ้ำกัน
function initLoginForm(element, data) { }
function initRegisterForm(element, data) { }

Best Practices

1. ชื่อที่สื่อความหมาย

// ❌ ไม่ชัดเจน
function init(element, data) { }

// ✅ ชัดเจน
function initProductGallery(element, data) { }
function initCheckoutForm(element, data) { }

2. Error Handling

function initChart(element, data) {
  try {
    const canvas = element.querySelector('#chart');
    if (!canvas) throw new Error('ไม่พบ Canvas');

    // Initialize
  } catch (error) {
    console.error('Chart init ล้มเหลว:', error);
    element.innerHTML = '<p class="error">ไม่สามารถโหลด chart ได้</p>';
  }
}

3. แยกเป็น Helper Functions

function initDashboard(element, data) {
  const cleanup1 = initStats(element, data.stats);
  const cleanup2 = initChart(element, data.chart);
  const cleanup3 = initActivityFeed(element, data.activity);

  return () => {
    cleanup1?.();
    cleanup2?.();
    cleanup3?.();
  };
}

function initStats(element, stats) { /* ... */ }
function initChart(element, chartData) { /* ... */ }

ที่เกี่ยวข้อง