Học cách tạo form cơ bản và xử lý events với JavaScript
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!
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:
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 | 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" |
| 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', ...) |
// 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
}
});
<!-- 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 | 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" |
| 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() |
input xảy ra realtime, change xảy ra khi blurkeydown có thể preventDefault, keyup khôngfocus không bubble, focusin có bubblemouseover có bubble, mouseenter khônge.clipboardData// 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);
});
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>
<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>
<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>
| 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 |
// 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);
});
});
<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>
<label for="message">Tin nhắn:</label>
<textarea name="message" id="message"
rows="4"
placeholder="Nhập tin nhắn..."></textarea>
| 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õ |
// 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ự`;
});
<!-- 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>
| 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 |
🎯 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!
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ệ.
<input type="text"> - Bắt buộc nhập<input type="email"> - Bắt buộc nhập, đúng format<input type="tel"> - Không bắt buộc<select> với 3 options: Hỗ trợ, Khiếu nại, Góp ý<textarea> - Bắt buộc nhập, min 10 ký tự<input type="checkbox"> - Bắt buộc check┌──────────────────────────────────────────┐ │ 📧 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ợ... │ │ │ └────────────────────────────────────┘ │ └──────────────────────────────────────────┘
addEventListener('submit', ...) cho formaddEventListener('blur', ...) cho email inputaddEventListener('input', ...) cho textarea (đếm ký 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.
<input type="text"> - Bắt buộc<input type="number"> - min=1, max=100, bắt buộc<select> với options: Laptop (15tr), iPhone (25tr), iPad (12tr)<input type="radio"> - COD, Chuyển khoản, Thẻ tín dụng<textarea> - Không bắt buộc┌──────────────────────────────────────────┐
│ 🛒 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 │ │
┌──────────────────────────────────────────────┐ │ 👤 ĐĂ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ệ │ │ │ └──────────────────────────────────────┘ │ └──────────────────────────────────────────────┘
/^[a-zA-Z0-9]{3,20}$/(today - birthDate) / (365.25 * 24 * 60 * 60 * 1000)<div class="error-message"> dưới mỗi inpute.preventDefault()<!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>