forked from molily/javascript-einfuehrung
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathevent-handling-effizient.html
More file actions
128 lines (116 loc) · 12 KB
/
event-handling-effizient.html
File metadata and controls
128 lines (116 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<!DOCTYPE html>
<html lang="de">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>JavaScript: Effiziente Ereignisverarbeitung: Event-Delegation und Capturing</title>
<link rel="stylesheet" href="js-doku.css">
<script type="text/javascript" src="js-doku.js"></script>
</head>
<body>
<div id="nav">
<p>Hier entsteht eine <strong>JavaScript-Dokumentation</strong> von <a href="http://molily.de/">molily</a>. Derzeit ist sie noch lückenhaft, wächst aber nach und nach. Kommentare und Feedback werden gerne per <a href="mailto:zapperlott@gmail.com">E-Mail</a> entgegen genommen.</p>
<p class="contents-link"><a href="./">Zum Inhaltsverzeichnis</a></p>
</div>
<h1>JavaScript: Effiziente Ereignisverarbeitung</h1>
<div class="section" id="delegation">
<h2>Event-Delegation</h2>
<p>Wenn zahlreiche Elemente im Dokument überwacht werden sollen, ist es sehr aufwändig umzusetzen und langsam in der Ausführung, diese herauszusuchen, zu durchlaufen und bei jedem denselben Event-Handler zu registrieren. Bei solchen Aufgabenstellungen können Sie vom <a href="event-handling-objekt.html#bubbling">Bubbling-Effekt</a> profitieren, das ist das Aufsteigen der Ereignisse im DOM-Baum. Man macht sich die Verschachtelung der Elemente im DOM-Baum zunutze und überwacht die Ereignisse von verschiedenen Elementen bei einem gemeinsamen, höherliegenden Element, zu dem die Ereignisse aufsteigen. Diese Technik nennt sich <dfn>Event-Delegation</dfn> (englisch <em>delegation</em> für Übertragung von Aufgaben). Dabei wird einem zentralen Element die Aufgabe übertragen, die Ereignisse zu verarbeiten, die bei seinen Nachfahrenelementen passieren.</p>
<p>Event-Delegation eignet sich insbesondere dann, wenn viele gleichförmige Elemente in Menüs, Link-Listen, Formularen oder Tabellen JavaScript-Interaktivität benötigen. Ohne Event-Delegation müsste man jedes Element einzeln ansprechen, um dort immer denselben Event-Handler zu registrieren.</p>
<p>Nehmen wir beispielsweise eine Liste mit Links zu Bildern. Wenn JavaScript aktiv ist, soll das Vollbild dokumentintern eingeblendet werden. Ein ähnliches Beispiel hatten wir bereits beim Unterdrücken der Standardaktion – die Umsetzung der Einblendung bleibt weiterhin ausgeklammert.</p>
<p>Wir gehen von folgendem HTML-Gerüst aus:</p>
<pre>
<ul id="bilderliste">
<li><a href="bilder/bild1.jpg"><img src="bilder/thumbnail1.jpg" alt="">
Ebru und Robin auf dem Empire State Building</a></li>
<li><a href="bilder/bild2.jpg"><img src="bilder/thumbnail2.jpg" alt="">
Noël und Francis vor dem Taj Mahal</a></li>
<li><a href="bilder/bild3.jpg"><img src="bilder/thumbnail3.jpg" alt="">
Isaak und Ahmet vor den Pyramiden von Gizeh</a></li>
<!-- ... viele weitere Links mit Thumbnails ... -->
</ul>
</pre>
<p>Beim Klick auf einen der Links soll nun das verlinkte Bild eingeblendet werden. Anstatt jedem <code>a</code>-Element einzeln einen Handler zuzuweisen, registrieren wir ihn beim gemeinsamen Vorfahrenelement <code>ul</code> mit der ID <code>bilderliste</code>:</p>
<pre>document.getElementById("bilderliste").onclick = bilderlistenKlick;</pre>
<p>In der angegebenen Handler-Funktion <code>bilderlistenKlick</code> findet nun die Überprüfung des Zielelementes statt.</p>
<pre>funktion bilderlistenKlick (e) {
// Vereinheitlichung von <a href="event-handling-objekt.html">Event-Objekt</a> und <a href="event-handling-objekt.html#currenttarget-target">Zielelement</a>
var e = e || window.event;
var target = e.target || e.srcElement;
var elementName = target.nodeName,
aElement = false;
// Überprüfe, ob das Zielelement ein Link oder ein Bild im Link ist:
if (elementName == "A") {
// Falls ein Link geklickt wurde, speichere das Zielelement
// in der Variable aElement:
aElement = target;
} else if (elementName == "IMG") {
// Falls das Thumbnail-Bild geklickt wurde,
// suche das zugehörige Link-Element:
aElement = target.parentNode;
}
// Zeige das Vollbild, wenn das Zielelement
// ein Link ist oder in einem Link liegt:
if (aElement) {
zeigeVollbild(aElement);
// Unterdrücke die Standardaktion:
return false;
}
// Andernfalls mache nichts.
}</pre>
<p>In dieser Funktion wird das Zielelement des Ereignisses angesprochen und dessen Elementname überprüft. Wenn ein <code>a</code>-Element geklickt wurde, muss es sich um einen Link auf ein Bild handeln und das Vollbild soll eingeblendet werden.</p>
<p>Das alleine wäre bereits mit der Abfrage <code>if (target.nodeName == "A")</code> zu erledigen. Das Beispiel hat allerdings bewusst eine Schwierigkeit eingebaut, um Ihnen das Event-Bubbling und das Arbeiten mit dem Zielelement näher zu bringen: In den <code>a</code>-Elementen liegen zusätzlich <code>img</code>-Elemente für die Thumbnails. Wenn der Anwender auf diese klickt, soll das Vollbild selbstverständlich ebenfalls eingeblendet werden. In dem Fall ist jedoch nicht der Link das Zielelement, sondern logischerweise das <code>img</code>-Element.</p>
<p>Aus diesem Grund muss die Abfrage erweitert werden: Handelt es sich um ein <code>a</code>-Element <em>oder</em> um ein Element, das direkt in einem <code>a</code>-Element liegt? Falls ein <code>img</code>-Element das Zielelement ist, steigen wir von diesem zu seinem <code>a</code>-Elternelement auf. Schließlich wird die Funktion <code>zeigeVollbild</code> mit dem gefundenen <code>a</code>-Elementobjekt als Parameter aufgerufen. Das Gerüst dieser Funktion sieht so aus:</p>
<pre>function zeigeVollbild (aElement) {
// Empfange das Elementobjekt als ersten Parameter und
// lese dessen href-Attribut mit der Bild-Adresse aus:
var bildAdresse = aElement.href;
// Blende das Bild ein, auf das der Link zeigt.
// (Die genaue Umsetzung ist an dieser Stelle ausgeklammert.)
}</pre>
<p>Dieses Beispiel soll Ihnen die grundlegende Funktionsweise von Event-Delegation veranschaulichen:</p>
<ol>
<li>Es gibt eine Handler-Funktion, die alle Ereignisse eines Types überwacht, welche von seinen Nachfahrenelementen aufsteigen.</li>
<li>Darin wird das Ereignis untersucht und insbesondere das Zielelement überprüft.</li>
<li>Wenn das Zielelement gewissen Kriterien entspricht (z.B. einem bestimmten Elementyp oder einer Klasse angehört), wird auf das Ereignis reagiert. Das kann in dieser Funktion erfolgen oder der Übersicht halber in einer anderen.</li>
</ol>
<p>Wie Sie schon bei diesem einfachen Beispiel sehen, ist eine aufwändige Untersuchung des DOM-Elementenbaumes rund um das Zielelement nötig. Bei Event-Delegation stellt sich oft die Frage, ob das Zielelement in einem anderen Element enthalten ist, auf das gewisse Kriterien zutreffen. Eine allgemeinere und vielseitig einsetzbare Lösung werden Sie später noch kennenlernen.</p>
<div class="subsection" id="delegation-kritik">
<h3>Anwendungsbereiche von Event-Delegation</h3>
<p>... <a href="http://forum.de.selfhtml.org/archiv/2009/6/t187745/#m1251783">Forumsposting</a></p>
</div>
</div>
<div class="section" id="capturing">
<h2>Capturing</h2>
<p>Capturing (englisch für »Einfangen«) ist eine Phase beim <a href="event-handling-objekt.html#bubbling">Event-Fluss</a>, die wir bereits kurz angesprochen haben. Der DOM-Event-Standard definiert <strong>drei Phasen</strong>, in denen ein Ereignis durch den DOM-Elementbaum wandert (Event-Fluss) und Handler auslöst:</p>
<ol>
<li><strong>Capturing-Phase</strong> (Absteigen zum Zielelement): Das Ereignis steigt vom obersten Dokument-Knoten im Elementenbaum hinab bis zum Zielelement des Ereignisses. Auf diesem Weg werden alle Handler ausgeführt, die für den Ereignistyp für die Capturing-Phase registriert wurden.
<li><strong>Target-Phase</strong> (Zielelement-Phase): Das Ereignis erreicht sein Zielelement und löst die betreffenden Handler aus, die dort für die Bubbling-Phase registriert wurden.</li>
<li><strong>Bubbling-Phase</strong> (Aufsteigen vom Zielelement): Das Ereignis steigt ausgehend vom Zielelement wieder in der Element-Hierarchie auf. Es durchläuft alle Vorfahrenelemente und löst dort die relevanten Handler aus.</li>
</ol>
<p>Alle bisher beschriebenen Modelle, ausgehend vom <a href="event-handling-grundlagen.html#traditionelles-event-handling">traditionellen</a> über <a href="event-handling-fortgeschritten.html#dom-events">W3C DOM Events</a> und dem <a href="event-handling-fortgeschritten.html#microsoft">Microsoft-Modell</a>, haben Handler für die Bubbling-Phase registriert. Wie wir uns das Bubbling zunutze machen, haben wir bereits bei der <a href="#delegation">Event-Delegation</a> kennengelernt.</p>
<p>Capturing ist ein weiterer Ansatz, um Ereignisse effizienter zu überwachen: Wir können ein Event-Handler bei einem höherliegenden Element registrieren, um die Ereignisse zu überwachen, die bei vielen Nachfahrenelemente passieren.</p>
<p>Der Unterschied zwischen Bubbling und Capturing folgender: Nicht alle Ereignisse haben eine Bubbling-Phase, das heißt nicht alle Ereignisse steigen auf und lösen die entsprechenden Handler bei ihren Vorfahrenelementen aus. Das hat durchaus seinen Sinn, macht aber die beschriebene Event-Delegation unmöglich. Gäbe es das Event-Capturing nicht, wären Sie gezwungen, alle nicht aufsteigenden Ereignisse direkt bei ihren Zielelementen zu überwachen. Mithilfe des Event-Capturings können Sie auch solche Ereignisse zentral überwachen – denn <em>jedes</em> Ereignis hat eine Capturing-Phase.</p>
<p>Event-Capturing ist nur unter Verwendung der standardisierten Methode <code>addEventListener</code> möglich. Das traditionelle Event-Handling mit seinem Schema <code>element.onevent = handlerfunktion</code> registriert den Handler immer für die Bubbling-Phase. Dasselbe gilt für das Microsoft-Modell mit <code>attachEvent</code>. Für Internet Explorer vor Version 9, welche <code>addEventListener</code> nicht unterstützen, müssen Sie daher gegebenenfalls eine Alternative ohne Event-Capturing bereitstellen.</p>
<p>Um Event-Handler für die Capturing-Phase zu registrieren, nutzen Sie wie gewohnt <code>addEventListener</code>, setzen jedoch den dritten Boolean-Parameter auf <code>true</code>:</p>
<pre>document.addEventListener("focus", captureHandler, true);</pre>
<p>Die Vorteile des Capturings liegen also darin, insbesondere nicht aufsteigende Ereignisse bei einem höherliegenden Element zu verarbeiten.
<p>Folgende Ereignisse beispielsweise steigen nicht auf:</p>
<ul>
<li><code>load</code>, z.B. bei Bildern, Objekten und Iframes</li>
<li><code>focus</code> und <code>blur</code>. (Als alternative aufsteigende Ereignisse gibt es allerdings <code>focusin</code> und <code>focusout</code>.)</li>
<li><code>mouseenter</code> und <code>mouseleave</code>. (Die Ereignisse <code>mouseover</code> und <code>mouseout</code> hingegen steigen auf.)</li>
<li><code>submit</code></li>
</ul>
<p>Da die Capturing-Phase die erst im Event-Fluss ist, ist es möglich, ein Ereignis schon in dieser Phase abzufangen. Ruft man in der Capturing-Phase die <a href="event-handling-objekt.html#bubbling-verhindern"><code>stopPropation</code>-Methode des Event-Objektes</a> auf, so wird der Fluss abgebrochen und die Ziel- und Bubbling-Phase fallen aus. Das Ereignis erreicht somit das Zielelement nicht.</p>
</div>
<div class="sequence-navigation">
<!-- <p class="next"><a href="event-handling-effizient.html" rel="next"></a></p> -->
<p class="prev"><a href="event-handling-onload.html" rel="prev">Onload-Techniken</a></p>
</div>
<div id="footer">
<p><strong>JavaScript-Dokumentation</strong> · <a href="./">Zum Inhaltsverzeichnis</a></p>
<p>Autor: <a href="http://molily.de/">molily</a> · Kontakt: <a href="mailto:zapperlott@gmail.com">zapperlott@gmail.com</a></p>
<p>Lizenz: <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/de/">Creative Commons Namensnennung - Weitergabe unter gleichen Bedingungen</a>.</p>
</div>
</body>
</html>