9.1 Event-Delegation
Die Arbeit mit Events in JavaScript kann ein komplexes Management erfordern, um die Leistung zu optimieren und die Architektur der Anwendung zu verbessern.
Die Optimierung der Arbeit mit Events in JavaScript ist von entscheidender Bedeutung für die Erstellung leistungsfähiger Webanwendungen, insbesondere wenn mit einer großen Anzahl von Elementen gearbeitet wird. In diesem Kontext spielen Event-Delegation und die Vermeidung unnötiger Handler eine Schlüsselrolle.
Event-Delegation ist ein Muster, bei dem der Event-Handler auf ein übergeordnetes Element gesetzt wird und nicht auf jedes einzelne untergeordnete Element. Dies ermöglicht ein effizientes Management von Events für eine große Anzahl von untergeordneten Elementen, da der Handler nur einmal für das übergeordnete Element aufgerufen wird, nicht für jedes Element.
Beispiel ohne Event-Delegation:
<!DOCTYPE html>
<html>
<head>
<title>Beispiel für Event-Delegation</title>
</head>
<body>
<ul id="list">
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
</ul>
<script>
const list = document.querySelector('ul');
const items = document.querySelectorAll('#list li');
items.forEach(item => {
item.addEventListener('click', function(event) {
const li = document.createElement('li');
li.innerText = "Neues Element, auf dem das Event nicht ausgelöst wird";
list.appendChild(li);
alert(`Du hast geklickt auf: ${event.target.textContent}`);
});
});
</script>
</body>
</html>
Problem: Wenn die Liste der Elemente dynamisch wächst, müssen Handler zu jedem neuen Element hinzugefügt werden.
Lösung — Beispiel mit Event-Delegation:
<!DOCTYPE html>
<html>
<head>
<title>Beispiel für Event-Delegation</title>
</head>
<body>
<ul id="list">
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
</ul>
<script>
const list = document.getElementById('list');
list.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
const li = document.createElement('li');
li.innerText = "Neues Element, auf dem das Event ausgelöst wird";
list.appendChild(li);
alert(`Du hast geklickt auf: ${event.target.textContent}`);
}
});
</script>
</body>
</html>
Vorteile:
- Der Handler wird nur dem übergeordneten Element hinzugefügt
- Dynamisch hinzugefügte Elemente unterstützen automatisch den Event-Handler
9.2 Vermeidung unnötiger Handler
Um Leistungseinbußen zu vermeiden, ist es wichtig, die Anzahl der Event-Handler zu minimieren, besonders wenn sie an viele Elemente gebunden oder häufig aufgerufen werden. Einige Ansätze zur Optimierung:
1. Reduktion der Anzahl an Handlers: Verwende Event-Delegation, um die Anzahl der Handler zu reduzieren.
2. Verwendung von once in addEventListener: Wenn ein Event-Handler nur einmal ausgelöst werden soll, verwende die Option { once: true }
beim Hinzufügen des Handlers.
Beispiel:
<!DOCTYPE html>
<html>
<head>
<title>Einmalige Event-Beispiel</title>
</head>
<body>
<button id="myButton">Klick mich!</button>
<script>
const button = document.getElementById('myButton');
button.addEventListener('click', function(event) {
alert('Du hast den Knopf geklickt');
}, { once: true });
</script>
</body>
</html>
3. Debouncing und Throttling: Diese Techniken sind nützlich für die Optimierung von Event-Handlern, die häufig ausgelöst werden, wie scroll
oder resize
.
9.3 Debouncing
Debouncing fasst mehrere aufeinanderfolgende Funktionsaufrufe zu einem zusammen, der erst ausgeführt wird, nachdem der Event-Strom beendet ist.
Beispiel:
function debounce(func, wait) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
window.addEventListener('resize', debounce(() => {
console.log('Fenstergröße geändert');
}, 300));
Ändere die Breite des Widgets, um das Ergebnis zu sehen
Debouncing Beispiel
In diesem Beispiel wird die Suchfunktion nur aufgerufen, nachdem der Benutzer 300 Millisekunden lang keine Eingaben mehr macht.
<!DOCTYPE html>
<html>
<head>
<title>Debouncing Beispiel</title>
</head>
<body>
<div style="min-height: 55px">
<input type="text" id="searchInput" placeholder="Beginne deine Anfrage einzugeben...">
<div id="results"></div>
</div>
<script>
const searchInput = document.getElementById('searchInput');
const results = document.getElementById('results');
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
function performSearch(event) {
const query = event.target.value;
results.textContent = 'Suche: ' + query;
// Simuliere eine Anfrage an den Server
setTimeout(() => {
results.textContent = 'Sie haben gesucht: ' + query;
}, 500);
}
const debouncedSearch = debounce(performSearch, 300);
searchInput.addEventListener('input', debouncedSearch);
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Debouncing Beispiel</title>
</head>
<body>
<input type="text" id="searchInput" placeholder="Beginne deine Anfrage einzugeben...">
<div id="results"></div>
<script>
const searchInput = document.getElementById('searchInput');
const results = document.getElementById('results');
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
function performSearch(event) {
const query = event.target.value;
results.textContent = 'Searching for: ' + query;
// Simuliere eine Anfrage an den Server
setTimeout(() => {
results.textContent = 'Sie haben gesucht: ' + query;
}, 500);
}
const debouncedSearch = debounce(performSearch, 300);
searchInput.addEventListener('input', debouncedSearch);
</script>
</body>
</html>
9.4 Throttling
Trolling Throttling stellt sicher, dass eine Funktion nicht öfter als einmal in einem angegebenen Zeitraum aufgerufen wird.
Beispiel:
function throttle(func, limit) {
let inThrottle;
return function(...args) {
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
window.addEventListener('scroll', throttle(() => {
console.log('Fenster scrolled');
}, 300));
Ein weiteres Beispiel für Throttling
In diesem Beispiel wird der Scroll-Handler nicht öfter als alle 200 Millisekunden aufgerufen, was hilft, den Browser bei häufigen Scroll-Events nicht zu überlasten.
<!DOCTYPE html>
<html>
<head>
<title>Throttling Beispiel</title>
</head>
<body>
<div id="scrollContainer" style="height: 200px; overflow-y: scroll;">
<div style="height: 1000px;"></div>
</div>
<div id="status">Beginne zu scrollen, um den Effekt zu sehen</div>
<script>
const scrollContainer = document.getElementById('scrollContainer');
const status = document.getElementById('status');
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function(...args) {
const context = this;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
function handleScroll(event) {
status.textContent = 'Scrolling... ' + new Date().toLocaleTimeString();
}
const throttledScroll = throttle(handleScroll, 200);
scrollContainer.addEventListener('scroll', throttledScroll);
</script>
</body>
</html>
9.5 Optimierung von Events mit passiven Listenern
Passive Event-Listener (passive event listeners) werden verwendet, um die Leistung zu verbessern, insbesondere bei der Verarbeitung von Scroll-Events (scroll
). Wenn ein Event-Handler als passiv festgelegt ist, bedeutet dies, dass er preventDefault()
nicht aufrufen wird, was es dem Browser ermöglicht, die Leistung zu optimieren.
Beispiel:
window.addEventListener('scroll', function(event) {
console.log('Scrolling');
}, { passive: true });
GO TO FULL VERSION