Element Attributes and Properties
Understanding how to work with element attributes, classes, and styles is crucial for creating dynamic web pages. Let’s explore the powerful methods JavaScript provides.
HTML Attributes vs DOM Properties
Important distinction:
<input type="text" value="default" id="myInput">
const input = document.querySelector('#myInput');
// Attribute (in HTML): Initial value
console.log(input.getAttribute('value')); // "default"
// Property (in DOM): Current value
console.log(input.value); // "default" (initially)
// User types "hello"
console.log(input.getAttribute('value')); // Still "default"
console.log(input.value); // "hello" (current value)
Key difference:
- Attributes: HTML source, static
- Properties: DOM object, dynamic
Working with Attributes
getAttribute() and setAttribute()
const link = document.querySelector('a');
// Get attribute
const href = link.getAttribute('href');
const target = link.getAttribute('target');
// Set attribute
link.setAttribute('href', 'https://example.com');
link.setAttribute('target', '_blank');
link.setAttribute('title', 'Visit Example');
// Check if attribute exists
if (link.hasAttribute('download')) {
console.log('This is a download link');
}
// Remove attribute
link.removeAttribute('target');
Common Attributes
const img = document.querySelector('img');
// Image attributes
img.setAttribute('src', 'photo.jpg');
img.setAttribute('alt', 'Photo description');
img.setAttribute('width', '300');
img.setAttribute('loading', 'lazy');
const form = document.querySelector('form');
// Form attributes
form.setAttribute('action', '/submit');
form.setAttribute('method', 'POST');
const input = document.querySelector('input');
// Input attributes
input.setAttribute('type', 'email');
input.setAttribute('placeholder', 'Enter email');
input.setAttribute('required', '');
input.setAttribute('disabled', '');
Boolean Attributes
Special handling for attributes like disabled, checked, required:
const button = document.querySelector('button');
const checkbox = document.querySelector('input[type="checkbox"]');
// Enable/disable button
button.disabled = true; // Disabled
button.disabled = false; // Enabled
// Or with attributes
button.setAttribute('disabled', ''); // Disabled
button.removeAttribute('disabled'); // Enabled
// Checkbox
checkbox.checked = true; // Checked
checkbox.checked = false; // Unchecked
// Check if checked
if (checkbox.checked) {
console.log('Checkbox is checked');
}
Working with Classes
classList API
Modern way to manage classes:
const element = document.querySelector('#box');
// Add class
element.classList.add('active');
// Add multiple classes
element.classList.add('highlight', 'border');
// Remove class
element.classList.remove('active');
// Toggle class
element.classList.toggle('hidden'); // Add if not present, remove if present
// Toggle with condition
element.classList.toggle('visible', isVisible); // Add if isVisible is true
// Check if has class
if (element.classList.contains('active')) {
console.log('Element is active');
}
// Replace class
element.classList.replace('old-class', 'new-class');
// Get all classes as array
const classes = Array.from(element.classList);
console.log(classes); // ['highlight', 'border', ...]
className (Old Way)
const element = document.querySelector('#box');
// Get all classes as string
console.log(element.className); // "box active highlight"
// Set classes (replaces all)
element.className = 'new-class another-class';
// Add class (not recommended - error-prone)
element.className += ' extra-class';
Recommendation: Use classList instead of className
Practical Class Examples
// Toggle menu
const menuButton = document.querySelector('#menu-button');
const menu = document.querySelector('#menu');
menuButton.addEventListener('click', () => {
menu.classList.toggle('open');
});
// Tab switching
const tabs = document.querySelectorAll('.tab');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
// Remove active from all tabs
tabs.forEach(t => t.classList.remove('active'));
// Add active to clicked tab
tab.classList.add('active');
});
});
// Theme switcher
const themeButton = document.querySelector('#theme-toggle');
themeButton.addEventListener('click', () => {
document.body.classList.toggle('dark-mode');
// Save preference
const isDark = document.body.classList.contains('dark-mode');
localStorage.setItem('theme', isDark ? 'dark' : 'light');
});
Data Attributes
Custom data attributes (data-*) for storing information:
HTML
<button
data-user-id="123"
data-action="delete"
data-confirm-message="Are you sure?">
Delete
</button>
<div
data-product-id="42"
data-product-name="Laptop"
data-product-price="999">
Laptop - $999
</div>
JavaScript Access
const button = document.querySelector('button');
// Get data attributes
const userId = button.dataset.userId; // "123"
const action = button.dataset.action; // "delete"
const message = button.dataset.confirmMessage; // "Are you sure?"
// Note: data-user-id becomes dataset.userId (camelCase)
// data-confirm-message becomes dataset.confirmMessage
// Set data attributes
button.dataset.userId = "456";
button.dataset.newAttribute = "value";
// Remove data attribute
delete button.dataset.action;
// Check if exists
if ('userId' in button.dataset) {
console.log('Has user ID');
}
Practical Data Attribute Examples
// Product cards
const productCards = document.querySelectorAll('.product-card');
productCards.forEach(card => {
card.addEventListener('click', () => {
const productId = card.dataset.productId;
const productName = card.dataset.productName;
const price = parseFloat(card.dataset.productPrice);
addToCart({ id: productId, name: productName, price });
});
});
// Modal dialogs
const openButtons = document.querySelectorAll('[data-modal]');
openButtons.forEach(button => {
button.addEventListener('click', () => {
const modalId = button.dataset.modal;
const modal = document.querySelector(`#${modalId}`);
modal.classList.add('open');
});
});
// Sortable table
const headers = document.querySelectorAll('[data-sortable]');
headers.forEach(header => {
header.addEventListener('click', () => {
const column = header.dataset.sortColumn;
const order = header.dataset.sortOrder === 'asc' ? 'desc' : 'asc';
sortTable(column, order);
header.dataset.sortOrder = order;
});
});
Inline Styles
style Property
Manipulate inline CSS:
const box = document.querySelector('#box');
// Set individual styles
box.style.color = 'blue';
box.style.backgroundColor = 'yellow'; // Note: camelCase
box.style.fontSize = '20px';
box.style.border = '2px solid red';
// Get computed style
const color = box.style.color; // Only gets inline styles
// Remove style
box.style.color = '';
// Or
box.style.removeProperty('color');
CSS Property Names
CSS to JavaScript naming:
// CSS → JavaScript (camelCase)
// background-color → backgroundColor
// font-size → fontSize
// margin-top → marginTop
// border-radius → borderRadius
element.style.backgroundColor = 'red';
element.style.fontSize = '16px';
element.style.marginTop = '10px';
Getting Computed Styles
Get actual rendered styles (including CSS from stylesheets):
const element = document.querySelector('#box');
// Get computed styles
const styles = window.getComputedStyle(element);
console.log(styles.color); // "rgb(0, 0, 255)"
console.log(styles.fontSize); // "16px"
console.log(styles.backgroundColor); // "rgb(255, 255, 0)"
// Get specific property
const width = styles.getPropertyValue('width');
console.log(width); // "200px"
Setting Multiple Styles
const element = document.querySelector('#box');
// Method 1: One by one
element.style.color = 'white';
element.style.backgroundColor = 'black';
element.style.padding = '20px';
// Method 2: cssText (replaces all inline styles)
element.style.cssText = `
color: white;
background-color: black;
padding: 20px;
border-radius: 5px;
`;
// Method 3: setAttribute (replaces all inline styles)
element.setAttribute('style', 'color: white; background-color: black;');
// Method 4: Object.assign (modern)
Object.assign(element.style, {
color: 'white',
backgroundColor: 'black',
padding: '20px'
});
Best Practice: Use Classes, Not Inline Styles
/* CSS file */
.highlighted {
background-color: yellow;
font-weight: bold;
}
.hidden {
display: none;
}
// ✅ GOOD: Use classes
element.classList.add('highlighted');
element.classList.toggle('hidden');
// ❌ AVOID: Inline styles for layout/theme
element.style.backgroundColor = 'yellow';
element.style.fontWeight = 'bold';
element.style.display = 'none';
// ✅ OK: Inline styles for dynamic values
element.style.width = `${percentage}%`;
element.style.transform = `translateX(${position}px)`;
Complete Examples
Image Gallery with Data Attributes
<div class="gallery">
<img data-full-size="images/photo1-full.jpg" src="images/photo1-thumb.jpg" alt="Photo 1">
<img data-full-size="images/photo2-full.jpg" src="images/photo2-thumb.jpg" alt="Photo 2">
<img data-full-size="images/photo3-full.jpg" src="images/photo3-thumb.jpg" alt="Photo 3">
</div>
<div id="lightbox" class="hidden">
<img id="lightbox-image" src="" alt="">
</div>
const thumbnails = document.querySelectorAll('.gallery img');
const lightbox = document.querySelector('#lightbox');
const lightboxImage = document.querySelector('#lightbox-image');
thumbnails.forEach(thumb => {
thumb.addEventListener('click', () => {
const fullSizeUrl = thumb.dataset.fullSize;
const alt = thumb.getAttribute('alt');
lightboxImage.src = fullSizeUrl;
lightboxImage.alt = alt;
lightbox.classList.remove('hidden');
});
});
lightbox.addEventListener('click', () => {
lightbox.classList.add('hidden');
});
Progress Bar
function updateProgress(percentage) {
const bar = document.querySelector('#progress-bar');
const text = document.querySelector('#progress-text');
// Update width
bar.style.width = `${percentage}%`;
// Update text
text.textContent = `${percentage}%`;
// Change color based on progress
if (percentage < 30) {
bar.className = 'progress-low';
} else if (percentage < 70) {
bar.className = 'progress-medium';
} else {
bar.className = 'progress-high';
}
// Store value
bar.dataset.progress = percentage;
}
updateProgress(75);
Accordion Component
const accordionHeaders = document.querySelectorAll('.accordion-header');
accordionHeaders.forEach(header => {
header.addEventListener('click', () => {
const content = header.nextElementSibling;
const isOpen = header.classList.contains('active');
// Close all accordions
accordionHeaders.forEach(h => {
h.classList.remove('active');
h.setAttribute('aria-expanded', 'false');
h.nextElementSibling.style.maxHeight = null;
});
// Open clicked accordion (if it was closed)
if (!isOpen) {
header.classList.add('active');
header.setAttribute('aria-expanded', 'true');
content.style.maxHeight = content.scrollHeight + 'px';
}
});
});
Best Practices
✅ DO:
- Use
classListfor class manipulation - Use data attributes for custom data
- Use CSS classes instead of inline styles when possible
- Use
getComputedStyle()to read actual styles - Use camelCase for style properties
❌ DON’T:
- Manipulate
classNamedirectly (useclassList) - Store complex data in data attributes (use JavaScript objects)
- Set layout styles inline (use CSS classes)
- Forget to remove event listeners on removed elements
Summary
| Task | Method | Example |
|---|---|---|
| Get attribute | getAttribute() |
el.getAttribute('href') |
| Set attribute | setAttribute() |
el.setAttribute('href', 'url') |
| Add class | classList.add() |
el.classList.add('active') |
| Remove class | classList.remove() |
el.classList.remove('hidden') |
| Toggle class | classList.toggle() |
el.classList.toggle('open') |
| Check class | classList.contains() |
el.classList.contains('active') |
| Get data attr | dataset |
el.dataset.userId |
| Set data attr | dataset |
el.dataset.userId = '123' |
| Set style | style |
el.style.color = 'red' |
| Get computed | getComputedStyle() |
getComputedStyle(el).color |
Next Article: DOM Traversal