DOM Manipulation Grundlagen
DOM Manipulation: Elemente dynamisch steuern
Das Document Object Model (DOM) ist die Schnittstelle zwischen JavaScript und HTML. Lernen Sie Elemente zu selektieren, zu ändern und Events zu handhaben.
Elemente selektieren
// Einzelnes Element
const element = document.getElementById('myId');
const first = document.querySelector('.myClass'); // Erstes Match
const first2 = document.querySelector('div.card > p'); // CSS-Selektor
// Mehrere Elemente
const byClass = document.getElementsByClassName('myClass'); // HTMLCollection (live)
const byTag = document.getElementsByTagName('p'); // HTMLCollection (live)
const all = document.querySelectorAll('.myClass'); // NodeList (statisch)
// Unterschied: Live vs. Statisch
// HTMLCollection aktualisiert sich automatisch bei DOM-Änderungen
// NodeList (von querySelectorAll) ist ein Snapshot
// Innerhalb eines Elements suchen
const container = document.querySelector('.container');
const buttons = container.querySelectorAll('button');
// Verwandte Elemente
const parent = element.parentElement;
const children = element.children; // Nur Elemente
const childNodes = element.childNodes; // Alle Nodes (inkl. Text)
const next = element.nextElementSibling;
const prev = element.previousElementSibling;
const first3 = element.firstElementChild;
const last = element.lastElementChild;
Elemente erstellen und einfügen
// Element erstellen
const div = document.createElement('div');
div.className = 'card';
div.id = 'newCard';
div.textContent = 'Neuer Inhalt';
// Attribute setzen
div.setAttribute('data-id', '123');
div.dataset.category = 'news'; // data-category="news"
// Einfügen
const container = document.querySelector('.container');
container.appendChild(div); // Am Ende
container.prepend(div); // Am Anfang
container.insertBefore(div, refElement); // Vor einem Element
// Moderne Methoden
container.append(div, 'Text', anotherEl); // Mehrere auf einmal
container.before(div); // Vor container
container.after(div); // Nach container
// An bestimmter Position
container.insertAdjacentElement('beforebegin', div); // Vor container
container.insertAdjacentElement('afterbegin', div); // Erstes Kind
container.insertAdjacentElement('beforeend', div); // Letztes Kind
container.insertAdjacentElement('afterend', div); // Nach container
// HTML direkt einfügen
container.insertAdjacentHTML('beforeend', '<p>Neuer Absatz</p>');
Elemente ändern
const element = document.querySelector('.card');
// Text ändern
element.textContent = 'Nur Text (sicher)';
element.innerText = 'Nur Text (respektiert CSS)';
element.innerHTML = '<strong>Mit HTML</strong>'; // ⚠️ XSS-Gefahr!
// Klassen ändern
element.classList.add('active', 'highlighted');
element.classList.remove('inactive');
element.classList.toggle('visible');
element.classList.replace('old', 'new');
const hasClass = element.classList.contains('active');
// Styles ändern
element.style.color = 'red';
element.style.backgroundColor = '#f0f0f0';
element.style.cssText = 'color: red; padding: 10px;';
// Computed Style lesen
const styles = getComputedStyle(element);
console.log(styles.color);
// Attribute
element.setAttribute('title', 'Tooltip');
element.getAttribute('title');
element.removeAttribute('title');
element.hasAttribute('title');
// Data-Attribute
element.dataset.userId = '42'; // data-user-id="42"
console.log(element.dataset.userId);
Elemente entfernen
const element = document.querySelector('.to-remove');
// Modern (direkt)
element.remove();
// Alt (über Parent)
element.parentElement.removeChild(element);
// Alle Kinder entfernen
const container = document.querySelector('.container');
// Methode 1: innerHTML (schnell, aber zerstört Event Listener)
container.innerHTML = '';
// Methode 2: Schleife (sicherer)
while (container.firstChild) {
container.removeChild(container.firstChild);
}
// Methode 3: replaceChildren (modern)
container.replaceChildren();
// Element ersetzen
const newElement = document.createElement('div');
oldElement.replaceWith(newElement);
Event Handling
const button = document.querySelector('button');
// Event Listener hinzufügen
button.addEventListener('click', function(event) {
console.log('Geklickt!');
console.log('Target:', event.target);
console.log('Typ:', event.type);
});
// Mit Arrow Function
button.addEventListener('click', (e) => {
e.preventDefault(); // Standard-Aktion verhindern
e.stopPropagation(); // Bubbling stoppen
});
// Event Listener entfernen (benötigt Referenz!)
function handleClick(e) {
console.log('Klick');
}
button.addEventListener('click', handleClick);
button.removeEventListener('click', handleClick);
// Einmalig ausführen
button.addEventListener('click', () => {
console.log('Nur einmal!');
}, { once: true });
// Event Delegation (für dynamische Elemente)
document.querySelector('.list').addEventListener('click', (e) => {
if (e.target.matches('.list-item')) {
console.log('Item geklickt:', e.target.textContent);
}
});
Wichtige Events
// Maus-Events
element.addEventListener('click', handler);
element.addEventListener('dblclick', handler);
element.addEventListener('mouseenter', handler); // Kein Bubbling
element.addEventListener('mouseleave', handler);
element.addEventListener('mouseover', handler); // Mit Bubbling
element.addEventListener('mouseout', handler);
element.addEventListener('mousemove', handler);
// Tastatur-Events
input.addEventListener('keydown', (e) => {
console.log(e.key); // "Enter", "a", "Escape"
console.log(e.code); // "Enter", "KeyA", "Escape"
console.log(e.ctrlKey); // true/false
});
input.addEventListener('keyup', handler);
// Form-Events
form.addEventListener('submit', (e) => {
e.preventDefault();
// Formular verarbeiten
});
input.addEventListener('input', handler); // Bei jeder Änderung
input.addEventListener('change', handler); // Bei blur nach Änderung
input.addEventListener('focus', handler);
input.addEventListener('blur', handler);
// Dokument-Events
document.addEventListener('DOMContentLoaded', handler); // DOM fertig
window.addEventListener('load', handler); // Alles geladen
window.addEventListener('resize', handler);
window.addEventListener('scroll', handler);
Performance-Tipps
// ❌ Schlecht: DOM in Schleife ändern
for (let i = 0; i < 1000; i++) {
container.innerHTML += `<div>${i}</div>`;
}
// ✅ Besser: DocumentFragment nutzen
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = i;
fragment.appendChild(div);
}
container.appendChild(fragment); // Ein DOM-Update
// ✅ Oder: Array + join + innerHTML
const items = Array.from({ length: 1000 }, (_, i) => `<div>${i}</div>`);
container.innerHTML = items.join('');
// ❌ Schlecht: Layout Thrashing
for (const el of elements) {
const height = el.offsetHeight; // Lesen
el.style.height = height + 10 + 'px'; // Schreiben → Reflow!
}
// ✅ Besser: Batch reads, dann writes
const heights = elements.map(el => el.offsetHeight);
elements.forEach((el, i) => {
el.style.height = heights[i] + 10 + 'px';
});
// Event Delegation statt vieler Listener
// ❌ Schlecht
document.querySelectorAll('.btn').forEach(btn => {
btn.addEventListener('click', handler);
});
// ✅ Besser: Ein Listener am Parent
document.querySelector('.buttons').addEventListener('click', (e) => {
if (e.target.classList.contains('btn')) {
handler(e);
}
});
Nützliche Patterns
// Element-Factory
function createElement(tag, attrs = {}, children = []) {
const el = document.createElement(tag);
Object.entries(attrs).forEach(([key, value]) => {
if (key === 'className') {
el.className = value;
} else if (key === 'style' && typeof value === 'object') {
Object.assign(el.style, value);
} else if (key.startsWith('on')) {
el.addEventListener(key.slice(2).toLowerCase(), value);
} else {
el.setAttribute(key, value);
}
});
children.forEach(child => {
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
el.appendChild(child);
}
});
return el;
}
// Verwendung
const card = createElement('div',
{ className: 'card', onClick: () => console.log('Klick') },
[
createElement('h2', {}, ['Titel']),
createElement('p', {}, ['Inhalt'])
]
);
💡 Tipp:
Nutzen Sie
querySelector für moderne Selektoren und Event Delegation für dynamische Listen. Vermeiden Sie innerHTML mit User-Input (XSS-Gefahr) und batchen Sie DOM-Operationen für bessere Performance.