classList API cơ bản
element.classList trả về DOMTokenList - tập hợp các class với các method:
add(...classes)- Thêm một hoặc nhiều classremove(...classes)- Xóa một hoặc nhiều classtoggle(class, force)- Bật/tắt classcontains(class)- Kiểm tra có class khôngreplace(oldClass, newClass)- Thay thế classitem(index)- Lấy class tại index
Thêm/xóa nhiều class một lần (Modern API)
Từ Chrome 61+, Firefox 55+, Safari 10.1+, Edge 16+ hỗ trợ multiple arguments:
const el = document.getElementById('myElement');
// Thêm nhiều class
el.classList.add('class1', 'class2', 'class3');
// Xóa nhiều class
el.classList.remove('class1', 'class2', 'class3');
// Toggle nhiều class (mỗi class独立 toggle)
el.classList.toggle('active');
el.classList.toggle('visible', true); // force add
el.classList.toggle('hidden', false); // force remove
Polyfill cho trình duyệt cũ (IE11, Safari cũ)
// Polyfill add/remove multiple cho DOMTokenList
if (!DOMTokenList.prototype.addMany) {
DOMTokenList.prototype.addMany = function(classes) {
const classArray = typeof classes === 'string' ? classes.split(/\s+/) : classes;
classArray.forEach(cls => {
if (cls) this.add(cls);
});
};
}
if (!DOMTokenList.prototype.removeMany) {
DOMTokenList.prototype.removeMany = function(classes) {
const classArray = typeof classes === 'string' ? classes.split(/\s+/) : classes;
classArray.forEach(cls => {
if (cls) this.remove(cls);
});
};
}
if (!DOMTokenList.prototype.toggleMany) {
DOMTokenList.prototype.toggleMany = function(classes, force) {
const classArray = typeof classes === 'string' ? classes.split(/\s+/) : classes;
classArray.forEach(cls => {
if (cls) this.toggle(cls, force);
});
};
}
// Sử dụng
element.classList.addMany('foo bar baz');
element.classList.removeMany(['foo', 'bar']);
element.classList.toggleMany('active visible', true);
Utility Functions hiện đại (Không cần polyfill prototype)
// Helper functions - an toàn, không modify prototype
const classUtil = {
// Thêm nhiều class từ string hoặc array
add(el, ...classes) {
const flat = classes.flat().filter(Boolean);
el.classList.add(...flat);
return el;
},
// Xóa nhiều class
remove(el, ...classes) {
const flat = classes.flat().filter(Boolean);
el.classList.remove(...flat);
return el;
},
// Toggle nhiều class
toggle(el, classes, force) {
const classArray = typeof classes === 'string' ? classes.split(/\s+/) : classes;
classArray.forEach(cls => el.classList.toggle(cls, force));
return el;
},
// Thay thế: xóa old, thêm new
replace(el, oldClasses, newClasses) {
const oldArr = typeof oldClasses === 'string' ? oldClasses.split(/\s+/) : oldClasses;
const newArr = typeof newClasses === 'string' ? newClasses.split(/\s+/) : newClasses;
oldArr.forEach(cls => el.classList.remove(cls));
newArr.forEach(cls => el.classList.add(cls));
return el;
},
// Set classes chính xác (xóa tất cả, thêm mới)
set(el, ...classes) {
const flat = classes.flat().filter(Boolean);
el.className = ''; // Clear all
if (flat.length) el.classList.add(...flat);
return el;
},
// Kiểm tra có bất kỳ class nào trong danh sách
hasAny(el, ...classes) {
const flat = classes.flat().filter(Boolean);
return flat.some(cls => el.classList.contains(cls));
},
// Kiểm tra có TẤT CẢ class trong danh sách
hasAll(el, ...classes) {
const flat = classes.flat().filter(Boolean);
return flat.every(cls => el.classList.contains(cls));
}
};
// Sử dụng
classUtil.add(element, 'foo', 'bar', 'baz');
classUtil.add(element, ['foo', 'bar'], 'baz'); // Array cũng được
classUtil.remove(element, 'foo bar'); // String space-separated
classUtil.toggle(element, 'active visible', true);
classUtil.replace(element, 'old-class', 'new-class');
classUtil.set(element, 'only', 'these', 'classes');
Ví dụ thực tế: State-based Classes
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<title>ClassList Multi Demo</title>
<style>
.card {
width: 300px; padding: 1.5rem; border-radius: 12px;
transition: all 0.3s ease; margin: 1rem auto;
border: 2px solid transparent;
}
.card.loading { opacity: 0.6; pointer-events: none; }
.card.error { border-color: #e74c3c; background: #fdf2f2; }
.card.success { border-color: #27ae60; background: #f0fff4; }
.card.editing { border-color: #3498db; box-shadow: 0 0 0 3px rgba(52,152,219,0.2); }
.card.compact { padding: 0.75rem; }
.card.hidden { display: none; }
.btn-group { display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; margin: 1rem; }
button { padding: 0.5rem 1rem; border: none; border-radius: 4px; cursor: pointer; background: #3498db; color: white; }
button.active { background: #27ae60; }
button.danger { background: #e74c3c; }
</style>
</head>
<body>
<div id="card" class="card">
<h3>Trạng thái Card</h3>
<p id="status">Normal</p>
<div class="class-list" style="font-family: monospace; font-size: 0.8rem; color: #666;"></div>
</div>
<div class="btn-group">
<button onclick="setState('normal')">Normal</button>
<button onclick="setState('loading')">Loading</button>
<button onclick="setState('error')">Error</button>
<button onclick="setState('success')">Success</button>
<button onclick="setState('editing')">Editing</button>
<button onclick="setState('compact')">Compact</button>
<button class="danger" onclick="setState('hidden')">Hidden</button>
<button onclick="setState('visible')">Show</button>
</div>
<script>
const card = document.getElementById('card');
const statusEl = document.getElementById('status');
const classListEl = document.querySelector('.class-list');
const states = {
normal: [],
loading: ['loading'],
error: ['error'],
success: ['success'],
editing: ['editing'],
compact: ['compact'],
hidden: ['hidden'],
visible: [] // Will remove hidden
};
function setState(stateName) {
// Remove all known state classes
const allStateClasses = Object.values(states).flat();
card.classList.remove(...allStateClasses);
// Add new state classes
const newClasses = states[stateName] || [];
if (newClasses.length) {
card.classList.add(...newClasses);
}
// Special: visible removes hidden
if (stateName === 'visible') {
card.classList.remove('hidden');
}
// Update UI
statusEl.textContent = stateName.charAt(0).toUpperCase() + stateName.slice(1);
classListEl.textContent = 'Classes: ' + (card.className || '(none)');
}
// Demo add/remove multiple
function demoAddRemove() {
const el = document.createElement('div');
el.className = 'base-class';
// Add multiple
el.classList.add('a', 'b', 'c');
console.log('After add:', el.className); // "base-class a b c"
// Remove multiple
el.classList.remove('a', 'c');
console.log('After remove:', el.className); // "base-class b"
// Toggle multiple
el.classList.toggle('x');
el.classList.toggle('y', true); // force add
el.classList.toggle('z', false); // force remove
console.log('After toggle:', el.className); // "base-class b x y"
}
demoAddRemove();
</script>
</body>
</html>
Performance Tips
classList.add(...classes)một lần tốt hơn gọi nhiều lầnadd()- Tránh đọc
classNamerồi set lại (gây reflow) - dùngclassList - Batch DOM reads/writes: đọc hết xong mới write
- Dùng
requestAnimationFramecho animation class changes
Browser Support
| Method | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
add/remove/toggle (single) | 8+ | 3.6+ | 5.1+ | 12+ |
add/remove (multiple args) | 61+ | 55+ | 10.1+ | 16+ |
replace() | 61+ | 49+ | 10.1+ | 17+ |