📋 Bài 4A: HTML Forms Cơ Bản

Học cách tạo form cơ bản và xử lý events với JavaScript

🎥 Video Bài Giảng

Xem video hướng dẫn chi tiết về HTML Forms:

💡 Gợi ý: Xem video trước, sau đó thực hành theo các ví dụ bên dưới để hiểu sâu hơn!

📖 Giới thiệu

HTML Forms là công cụ quan trọng để thu thập dữ liệu từ người dùng. Trong bài học này, bạn sẽ học:

🏗️ 1. Cấu trúc Form Cơ Bản

📋 HTML: Thẻ <form>

Thẻ <form> là container chứa tất cả các input elements.

<form action="/submit" method="POST" id="myForm">
  <!-- Input elements ở đây -->
  <button type="submit">Gửi</button>
</form>

📊 Thuộc tính quan trọng

Thuộc tính Mô tả Giá trị Ví dụ
action URL xử lý dữ liệu form URL hoặc đường dẫn action="/submit"
method Phương thức HTTP gửi dữ liệu GET hoặc POST method="POST"
id ID để JavaScript truy cập Tên duy nhất id="contactForm"
name Tên form (optional) Tên form name="contact"

⚡ Form Events Cơ Bản

Event Khi nào xảy ra Sử dụng cho JavaScript
submit Khi form được gửi Validate, xử lý dữ liệu form.addEventListener('submit', ...)
reset Khi form được reset Xóa dữ liệu, confirm form.addEventListener('reset', ...)

🎯 Demo: Form với Submit và Reset Events

📋 Form cơ bản với JavaScript events:

💻 Code JavaScript giải thích:

// Lấy form element
const form = document.getElementById('demo1Form');

// Submit event - Xử lý khi form được gửi
form.addEventListener('submit', function(e) {
  e.preventDefault(); // Ngăn form gửi đi (reload page)
  
  // Thu thập dữ liệu từ form
  const formData = new FormData(this);
  const data = Object.fromEntries(formData);
  
  // Hiển thị dữ liệu
  console.log('Form data:', data);
});

// Reset event - Xử lý khi form được reset
form.addEventListener('reset', function(e) {
  if (!confirm('Bạn có chắc?')) {
    e.preventDefault(); // Hủy reset nếu cần
  }
});

⌨️ 2. Input Elements Cơ Bản

📋 HTML: Các loại Input

<!-- Text input -->
<input type="text" name="username" placeholder="Tên đăng nhập">

<!-- Password input -->
<input type="password" name="password" placeholder="Mật khẩu">

<!-- Email input -->
<input type="email" name="email" placeholder="email@example.com">

<!-- Number input -->
<input type="number" name="age" min="18" max="100">

<!-- Tel input -->
<input type="tel" name="phone" placeholder="0123456789">

📊 Thuộc tính Input quan trọng

Thuộc tính Mô tả Ví dụ
type Loại input type="text"
name Tên field (quan trọng!) name="username"
placeholder Text gợi ý placeholder="Nhập tên..."
required Bắt buộc nhập required
value Giá trị mặc định value="John"
min/max Giá trị min/max (number) min="18" max="100"

⚡ Input Events - Danh mục đầy đủ

Event Khi nào xảy ra Sử dụng cho Thuộc tính quan trọng
🔤 Nhập liệu (Input Events)
input Realtime khi user gõ/thay đổi giá trị Live validation, search, character count e.target.value
change Khi giá trị thay đổi & mất focus Validation sau khi nhập xong, select/checkbox e.target.value
beforeinput Trước khi input thay đổi (có thể preventDefault) Ngăn chặn input không hợp lệ e.data, e.inputType
⌨️ Bàn phím (Keyboard Events)
keydown Khi phím được nhấn xuống Shortcuts, ngăn chặn phím e.key, e.keyCode, e.ctrlKey
keyup Khi phím được thả ra Xử lý sau khi nhấn phím e.key, e.keyCode
keypress Khi phím ký tự được nhấn (deprecated) Không khuyến khích dùng e.key
🎯 Focus Events
focus Khi input được focus Show help text, select all e.target
blur Khi input mất focus Validate, format data, save e.target.value
focusin Khi focus (có bubble) Bắt focus từ parent element e.target
focusout Khi blur (có bubble) Bắt blur từ parent element e.target
🖱️ Chuột (Mouse Events)
click Khi click vào input Toggle, select, track clicks e.target
dblclick Khi double click Select all text, special actions e.target
mousedown Khi nhấn chuột xuống Drag & drop, custom behaviors e.button
mouseup Khi thả chuột Kết thúc drag, selection e.button
mouseover Khi chuột di chuyển vào input Show tooltip, highlight e.target
mouseout Khi chuột rời khỏi input Hide tooltip, remove highlight e.target
mouseenter Khi chuột vào (không bubble) Hover effects e.target
mouseleave Khi chuột ra (không bubble) Remove hover effects e.target
📋 Clipboard Events
cut Khi user cắt (Ctrl+X) Track cuts, prevent cut e.clipboardData
copy Khi user copy (Ctrl+C) Track copies, modify clipboard e.clipboardData
paste Khi user paste (Ctrl+V) Validate/clean paste data e.clipboardData.getData()
✅ Validation Events
invalid Khi validation thất bại Custom error messages e.target.validationMessage
🔧 Khác (Other Events)
select Khi text được select Track text selection e.target.selectionStart/End
wheel Khi scroll chuột trên input number Prevent scroll change value e.deltaY
contextmenu Khi right-click Custom context menu e.preventDefault()

💡 Lưu ý quan trọng về Events

🎯 Demo: Input Events và Validation

⌨️ Input với realtime validation:

💻 Code JavaScript giải thích:

// INPUT event - Xảy ra realtime khi user gõ
input.addEventListener('input', function(e) {
  const value = e.target.value; // Lấy giá trị hiện tại
  
  // Validation logic
  if (value.length < 3) {
    // Hiển thị lỗi
  } else {
    // Hợp lệ
  }
});

// BLUR event - Xảy ra khi input mất focus
input.addEventListener('blur', function(e) {
  const value = e.target.value;
  
  // Validate sau khi user nhập xong
  if (!validateEmail(value)) {
    // Hiển thị lỗi
  }
});

// CHANGE event - Xảy ra khi giá trị thay đổi
input.addEventListener('change', function(e) {
  const value = e.target.value;
  
  // Xử lý khi giá trị thay đổi
  console.log('Changed to:', value);
});

🏷️ 3. Labels và Accessibility

📋 HTML: Thẻ <label>

Thẻ <label> tạo nhãn cho input và cải thiện trải nghiệm người dùng.

<!-- Cách 1: Sử dụng thuộc tính for -->
<label for="username">Tên đăng nhập:</label>
<input type="text" id="username" name="username">

<!-- Cách 2: Bao bọc input trong label -->
<label>
  Email:
  <input type="email" name="email">
</label>

💡 Lợi ích của Labels

🎯 Demo: Labels với Events

🏷️ Click label để focus input:

☑️ 4. Checkboxes và Radio Buttons

📋 HTML: Checkboxes (chọn nhiều)

<h3>Sở thích:</h3>
<label>
  <input type="checkbox" name="hobbies" value="reading">
  Đọc sách
</label>
<label>
  <input type="checkbox" name="hobbies" value="music">
  Nghe nhạc
</label>

📋 HTML: Radio Buttons (chọn một)

<h3>Giới tính:</h3>
<label>
  <input type="radio" name="gender" value="male">
  Nam
</label>
<label>
  <input type="radio" name="gender" value="female">
  Nữ
</label>

⚡ Checkbox/Radio Events

Event Khi nào xảy ra Thuộc tính quan trọng
change Khi checkbox/radio được click e.target.checked (true/false)
click Khi click vào checkbox/radio e.target.value

🎯 Demo: Checkboxes và Radio với JavaScript

☑️ Checkbox và Radio Events:

Sở thích (Checkboxes - chọn nhiều):

Giới tính (Radio - chọn một):

💻 Code JavaScript giải thích:

// Lắng nghe tất cả checkboxes
document.querySelectorAll('input[name="hobbies"]').forEach(checkbox => {
  checkbox.addEventListener('change', function(e) {
    console.log('Checkbox:', e.target.value);
    console.log('Checked:', e.target.checked); // true/false
    
    // Lấy tất cả checkboxes đã chọn
    const checked = document.querySelectorAll('input[name="hobbies"]:checked');
    const values = Array.from(checked).map(cb => cb.value);
    console.log('All selected:', values);
  });
});

// Lắng nghe radio buttons
document.querySelectorAll('input[name="gender"]').forEach(radio => {
  radio.addEventListener('change', function(e) {
    console.log('Selected gender:', e.target.value);
  });
});

📝 5. Select Dropdown và Textarea

📋 HTML: Select Dropdown

<label for="country">Quốc gia:</label>
<select name="country" id="country">
  <option value="">-- Chọn quốc gia --</option>
  <option value="vn">Việt Nam</option>
  <option value="us">Hoa Kỳ</option>
  <option value="jp">Nhật Bản</option>
</select>

📋 HTML: Textarea

<label for="message">Tin nhắn:</label>
<textarea name="message" id="message" 
          rows="4" 
          placeholder="Nhập tin nhắn..."></textarea>

⚡ Select/Textarea Events

Event Áp dụng cho Khi nào xảy ra
change Select, Textarea Khi giá trị thay đổi
input Textarea Realtime khi user gõ

🎯 Demo: Select và Textarea với JavaScript

📝 Select và Textarea Events:
0 / 200 ký tự

💻 Code JavaScript giải thích:

// Select change event
select.addEventListener('change', function(e) {
  const value = e.target.value; // Giá trị được chọn
  const text = e.target.options[e.target.selectedIndex].text; // Text hiển thị
  
  console.log('Selected:', value, text);
});

// Textarea input event (realtime character count)
textarea.addEventListener('input', function(e) {
  const value = e.target.value;
  const length = value.length;
  
  console.log('Character count:', length);
  
  // Hiển thị số ký tự
  document.getElementById('charCount').textContent = `${length} ký tự`;
});

🔘 6. Buttons và Form Submission

📋 HTML: Các loại Buttons

<!-- Submit button - Gửi form -->
<button type="submit">Gửi form</button>
<input type="submit" value="Gửi form">

<!-- Reset button - Reset form -->
<button type="reset">Xóa form</button>
<input type="reset" value="Xóa form">

<!-- Button thông thường - Không làm gì mặc định -->
<button type="button" onclick="doSomething()">Click me</button>

📊 Phân biệt các loại Button

Type Chức năng Khi click
submit Gửi form Trigger submit event
reset Reset form Trigger reset event
button Không làm gì Chỉ có click event

🎯 Demo: Form hoàn chỉnh với tất cả các elements

🔘 Form đăng ký hoàn chỉnh:

🎯 Bài tập thực hành

🎓 Bạn sẽ học được gì?

📝 Bài 1 - Cơ bản:
  • ✓ Form validation
  • ✓ Submit & Reset events
  • ✓ Input validation (email)
  • ✓ Character counter
🛒 Bài 2 - Trung bình:
  • ✓ Realtime calculation
  • ✓ Number validation
  • ✓ Format currency
  • ✓ Enable/disable button
👤 Bài 3 - Nâng cao:
  • ✓ Complex validation
  • ✓ Password strength
  • ✓ Date validation (tuổi)
  • ✓ Multiple checkboxes

🎯 Hãy tự tạo các form sau theo yêu cầu. Mỗi bài tập có demo mẫu ASCII art để bạn dễ hình dung!
💡 Lưu ý: Bài tập sắp xếp từ dễ → khó. Làm tuần tự để tích lũy kinh nghiệm!

📝 Bài tập 1: Form Liên hệ

📋 Mô tả:

Tạo form để khách hàng liên hệ với công ty. Form cần thu thập thông tin cơ bản và nội dung liên hệ.

✅ Yêu cầu HTML:
  1. Họ tên: <input type="text"> - Bắt buộc nhập
  2. Email: <input type="email"> - Bắt buộc nhập, đúng format
  3. Số điện thoại: <input type="tel"> - Không bắt buộc
  4. Chủ đề: <select> với 3 options: Hỗ trợ, Khiếu nại, Góp ý
  5. Tin nhắn: <textarea> - Bắt buộc nhập, min 10 ký tự
  6. Đồng ý gửi: <input type="checkbox"> - Bắt buộc check
  7. Buttons: Submit và Reset
⚡ Yêu cầu JavaScript:
  1. Submit event: Hiển thị tất cả thông tin đã nhập dưới dạng card đẹp
  2. Reset event: Confirm "Bạn có chắc muốn xóa?" trước khi reset
  3. Email validation: Khi blur, kiểm tra email đúng format (có @)
  4. Textarea character count: Hiển thị số ký tự đã nhập (realtime)
  5. Select change: Hiển thị chủ đề đã chọn
🎨 Demo mẫu (tham khảo):
┌──────────────────────────────────────────┐
│  📧 FORM LIÊN HỆ                         │
├──────────────────────────────────────────┤
│  Họ tên:  [Nguyễn Văn A_______] ✅       │
│  Email:   [nguyenvana@mail.com] ✅       │
│  SĐT:     [0912-345-678_______]          │
│  Chủ đề:  [▼ Hỗ trợ           ]          │
│                                          │
│  Tin nhắn:                               │
│  ┌────────────────────────────┐          │
│  │ Tôi cần hỗ trợ về sản phẩm │ (45/500) │
│  │ X. Cảm ơn!                 │          │
│  └────────────────────────────┘          │
│                                          │
│  ☑ Tôi đồng ý gửi thông tin              │
│  [  Gửi  ] [  Reset  ]                   │
│                                          │
│  ✅ KẾT QUẢ SAU KHI GỬI:                 │
│  ┌────────────────────────────────────┐  │
│  │ 👤 Họ tên: Nguyễn Văn A           │  │
│  │ 📧 Email: nguyenvana@mail.com     │  │
│  │ 📞 SĐT: 0912-345-678              │  │
│  │ 📋 Chủ đề: Hỗ trợ                 │  │
│  │ 💬 Tin nhắn: Tôi cần hỗ trợ...    │  │
│  └────────────────────────────────────┘  │
└──────────────────────────────────────────┘
💡 Gợi ý làm bài:
  • Bước 1: Tạo HTML form với tất cả các trường theo yêu cầu
  • Bước 2: Thêm addEventListener('submit', ...) cho form
  • Bước 3: Thêm addEventListener('blur', ...) cho email input
  • Bước 4: Thêm addEventListener('input', ...) cho textarea (đếm ký tự)
  • Bước 5: Test và hoàn thiện!

🛒 Bài tập 2: Form Đặt hàng

📋 Mô tả:

Tạo form đặt hàng trực tuyến. Form sẽ tính tổng tiền realtime khi user thay đổi số lượng.

✅ Yêu cầu HTML:
  1. Tên khách hàng: <input type="text"> - Bắt buộc
  2. Số lượng: <input type="number"> - min=1, max=100, bắt buộc
  3. Sản phẩm: <select> với options: Laptop (15tr), iPhone (25tr), iPad (12tr)
  4. Thanh toán: <input type="radio"> - COD, Chuyển khoản, Thẻ tín dụng
  5. Ghi chú: <textarea> - Không bắt buộc
  6. Buttons: Submit (ban đầu disabled)
⚡ Yêu cầu JavaScript:
  1. Change event (số lượng): Tính tổng tiền = số lượng × giá sản phẩm
  2. Change event (sản phẩm): Cập nhật giá và tính lại tổng tiền
  3. Hiển thị tổng tiền: Realtime, format tiền VNĐ (VD: 15.000.000đ)
  4. Enable/Disable submit: Chỉ enable khi form hợp lệ
  5. Submit event: Hiển thị hóa đơn đẹp với tất cả thông tin
🎨 Demo mẫu (tham khảo):
┌──────────────────────────────────────────┐
│  🛒 FORM ĐẶT HÀNG                        │
├──────────────────────────────────────────┤
│  Tên KH:    [Trần Thị B_________] ✅     │
│  Số lượng:  [5] ◀━━━●━━━▶ (min:1, max:100) │
│  Sản phẩm:  [▼ Laptop - 15tr   ]         │
│  ─────────────────────────               │
│  💰 Giá:       15.000.000đ                │
│  💵 Tổng tiền: 75.000.000đ ← realtime!   │
│                                          │
│  Thanh toán:                             │
│  ○ COD  ● Chuyển khoản  ○ Thẻ           │
│                                          │
│  Ghi chú:                                │
│  [Giao trong giờ hành chính_______]      │
│                                          │
│  [ Đặt hàng ] (enabled)                  │
│                                          │
│  📋 HÓA ĐƠN (hiển thị sau khi submit):   │
│  ┌────────────────────────────────────┐  │
│  │ 🛒 ĐƠN HÀNG #12345                │  │
│  │ ──────────────────────────────────│  │
│  │ Khách hàng: Trần Thị B            │  │
│  │ Sản phẩm: Laptop                  │  │
│  │ Số lượng: 5 × 15.000.000đ         │  │
│  │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━│  │
│  │ Tổng: 75.000.000đ                 │  │
│  │ Thanh toán: Chuyển khoản          │  │
          
        
⚡ Yêu cầu JavaScript (quan trọng!):
  1. Input event (username): Realtime validation, hiển thị ✅/❌ ngay
  2. Blur event (email): Validate khi mất focus
  3. Input event (password): Check độ mạnh (yếu/trung bình/mạnh)
  4. Input event (confirm): Check khớp với password
  5. Change event (ngày sinh): Tính tuổi, check >= 18
  6. Change event (checkbox): Đảm bảo ít nhất 1 checkbox được chọn
  7. Submit event: Validate toàn bộ → hiển thị thông tin user
🎨 Demo mẫu (tham khảo):
┌──────────────────────────────────────────────┐
│  👤 ĐĂNG KÝ TÀI KHOẢN                        │
├──────────────────────────────────────────────┤
│  Username:  [john_doe____] ✅ Hợp lệ         │
│  Email:     [john@mail.com] ✅ Đúng format   │
│  Password:  [••••••••] 🟢 Mạnh (8+ ký tự)    │
│  Confirm:   [••••••••] ✅ Khớp               │
│  Ngày sinh: [📅 10/05/2000] ✅ (24 tuổi)     │
│                                              │
│  Giới tính:                                  │
│  ○ Nam  ● Nữ  ○ Khác                         │
│                                              │
│  Sở thích (chọn ít nhất 1):                  │
│  ☑ Đọc sách  ☐ Du lịch  ☑ Thể thao          │
│  ☐ Âm nhạc   ☑ Công nghệ                     │
│                                              │
│  [ Đăng ký ] [ Reset ]                       │
│                                              │
│  ✅ THÔNG TIN USER (sau submit):             │
│  ┌──────────────────────────────────────┐    │
│  │ 👤 Username: john_doe               │    │
│  │ 📧 Email: john@mail.com             │    │
│  │ 🎂 Tuổi: 24                         │    │
│  │ ⚧ Giới tính: Nữ                     │    │
│  │ ❤️ Sở thích: Đọc sách, Thể thao,   │    │
│  │             Công nghệ               │    │
│  └──────────────────────────────────────┘    │
└──────────────────────────────────────────────┘
💡 Gợi ý làm bài:
  • Username validation: Dùng regex /^[a-zA-Z0-9]{3,20}$/
  • Password strength: Check length + có số + có chữ hoa
  • Tính tuổi: (today - birthDate) / (365.25 * 24 * 60 * 60 * 1000)
  • Hiển thị error: Tạo <div class="error-message"> dưới mỗi input
  • Border color: Đỏ = lỗi, Xanh = hợp lệ

✅ Checklist hoàn thành (áp dụng cho cả 3 bài)

  • HTML: Sử dụng đúng input types (text, email, number, date...)
  • HTML: Có label cho TẤT CẢ inputs (accessibility)
  • HTML: Dùng validation attributes (required, min, max, minlength)
  • JS: Submit event với e.preventDefault()
  • JS: Reset event với confirm dialog
  • JS: Dùng đúng events (input/change/blur) cho từng trường hợp
  • UX: Hiển thị error/success messages rõ ràng
  • UX: Border màu đỏ (lỗi) / xanh (hợp lệ)
  • Data: Thu thập và hiển thị form data đẹp mắt
  • Test: Test trên trình duyệt, check tất cả các trường hợp

💡 Gợi ý Code Template:

<!DOCTYPE html>
<html>
<head>
  <title>Form Exercise</title>
  <style>
    .error { border-color: red; }
    .success { border-color: green; }
    .error-message { color: red; font-size: 0.9em; }
    .success-message { color: green; font-size: 0.9em; }
  </style>
</head>
<body>
  <form id="myForm">
    <label for="username">Username:</label>
    <input type="text" id="username" name="username" required>
    <div id="usernameError" class="error-message"></div>
    
    <!-- More fields... -->
    
    <button type="submit">Submit</button>
    <button type="reset">Reset</button>
  </form>
  
  <div id="result"></div>

  <script>
    const form = document.getElementById('myForm');
    
    // Submit event
    form.addEventListener('submit', function(e) {
      e.preventDefault();
      
      // Validate
      if (!validateForm()) {
        return;
      }
      
      // Get data
      const formData = new FormData(this);
      const data = Object.fromEntries(formData);
      
      // Display result
      document.getElementById('result').innerHTML = 
        `Form submitted: ${JSON.stringify(data)}`;
    });
    
    // Reset event
    form.addEventListener('reset', function(e) {
      if (!confirm('Reset form?')) {
        e.preventDefault();
      }
    });
    
    // Input validation
    document.getElementById('username').addEventListener('input', function(e) {
      const value = e.target.value;
      const errorDiv = document.getElementById('usernameError');
      
      if (value.length < 3) {
        this.classList.add('error');
        errorDiv.textContent = 'Username quá ngắn';
      } else {
        this.classList.remove('error');
        this.classList.add('success');
        errorDiv.textContent = '';
      }
    });
    
    function validateForm() {
      // Your validation logic
      return true;
    }
  </script>
</body>
</html>