Eventhandler auf inneinander verschachtelte Elemente?
Jörg
- design/layout
- javascript
- jquery
Hallo,
ich habe ein Div, auf dem ein Eventhandler registriert ist. Innerhalb des Divs möchte ich eine Checkbox setzen, auf das ich einen anderen Eventhandler registrieren möchte, ohne dass der Eventhandler des Divs reagiert, wenn der Checkbox-Eventhandler reagiert.
Geht das? Und wie macht man das dann?
Jörg
Hallo,
ich habe ein Div, auf dem ein Eventhandler registriert ist. Innerhalb des Divs möchte ich eine Checkbox setzen, auf das ich einen anderen Eventhandler registrieren möchte, ohne dass der Eventhandler des Divs reagiert, wenn der Checkbox-Eventhandler reagiert.
Geht das? Und wie macht man das dann?
Jörg
Hier noch ein Link, der zeigt, was ich (nicht) möchte.
Tach!
ich habe ein Div, auf dem ein Eventhandler registriert ist. Innerhalb des Divs möchte ich eine Checkbox setzen, auf das ich einen anderen Eventhandler registrieren möchte, ohne dass der Eventhandler des Divs reagiert, wenn der Checkbox-Eventhandler reagiert.
Wenn du dich über das Konzept "Event Bubbling and Capturing" informierst, wirst du das wissen, denn wie man das Bubbling stoppt, denn das steht in den Tutorials mit drin.
dedlfix.
Hallo,
Wenn du dich über das Konzept "Event Bubbling and Capturing" informierst, wirst du das wissen, denn wie man das Bubbling stoppt, denn das steht in den Tutorials mit drin.
Werde ich machen. Davon ab, ist etwas gegen diese Lösung einzuwenden?
Jörg
Edit: Scheint genau das zu sein, was Du ansprachst 😉
Tach!
Davon ab, ist etwas gegen diese Lösung einzuwenden?
Edit: Scheint genau das zu sein, was Du ansprachst 😉
Nein, eigentlich nicht. Man kann zwar innerhalb des äußeren Eventhandlers das Target prüfen, aber du kannst auch komplett verhindern, dass der außere Eventhandler überhaupt aufgerufen wird, wenn du das Bubbling im inneren Eventhandler stoppst.
dedlfix.
Hi dedlfix,
Nein, eigentlich nicht. Man kann zwar innerhalb des äußeren Eventhandlers das Target prüfen, aber du kannst auch komplett verhindern, dass der außere Eventhandler überhaupt aufgerufen wird, wenn du das Bubbling im inneren Eventhandler stoppst.
Aber irgendwie gelingt mir das Ganze nur bei Click-.Events.
Wenn ich dasselbe bei einem Change-Event versuche, scheitert es.
Warum?
Jörg
Hallo Jörg,
du Spaßvogel, du kannst doch nicht erwarten, ein click-Event durch stopPropagation in einem change-Eventhandler zu stoppen. Das sind separate Events und sie werden separat geroutet. Vor allem wird das change-Event erst verarbeitet, wenn das click-Event schon längst Geschichte ist.
Es ist nicht so, dass die Checkbox das change-Event eigenständig kontrolliert. Du klickst auf die Checkbox. Das click-Event wird nun vom Browser in die Event-Warteschlange gestellt und durchläuft den Event-Dispatch. D.h. es hat die capturing-Phase (vom dokument hinunter zur Checkbox) und die bubbling-Phase (von der Checkbox hoch zum dokument) und auf jeder Stufe können Eventhandler lauern. Solange keiner von denen preventDefault aufruft, findest danach das Defaultverhalten statt (danke, Linuchs, das war mir bis eben nicht richtig klar), und das besteht darin, den checked-Zustand der Checkbox zu ändern und ein change-Event in die Warteschlange zu stellen.
Deswegen bekommst Du auch erst die click-Meldung vom div und danach die change-Meldung.
Rolf
Hi Rolf,
na ok, schade.
Hab nun der Checkbox eine Klasse mitgegeben und laure auf den Click hierauf. Dann gehts.
Jörg
@@Jörg
ich habe ein Div, auf dem ein Eventhandler registriert ist.
Hier steckt schon ein Fehler. div
s kann man nicht clicken. Einige Nutzer (die sowas wie eine Maus verwenden) können es, viele andere nicht (z.B. Tastaturnutzer).
Für clickbare Elemente ist <a href=…>
(wenn es ein Link ist, der woandershin führt) bzw. <button>
(für Aktionen auf einer Seite) zu verwenden.[1]
Da du in deinem Eröffnungsposting nur allgemein von Eventhandlern geschrieben hattest und es erst aus deinem nachgereichten Beispiel ersichtlich wurde, dass es sich dabei um click
-Eventhandler handelt, sei @dedlfix verziehen, dass er das nicht erwähnte. Obwohl man sich das hätte denken können.
Bevor du fragst: Nein, interaktive HTML-Elemente kann man nicht verschachteln. Man kann aber mit CSS odr JavaScript interaktive Elemente so implementieren, dass sie sich wie verschachtelte anfühlen. Die UX-Frage ist, ob man das tun sollte.
😷 LLAP
wenn es sich nicht um andere interaktive Elemente wie <img usemap=…
handelt. ↩︎
Tach!
ich habe ein Div, auf dem ein Eventhandler registriert ist.
Hier steckt schon ein Fehler.
div
s kann man nicht clicken.
Und warum soll man darauf keinen Click-Handler registrieren dürfen? Im Zusammenhang mit Event Bubbling ist das doch legitim, nur einen Handler auf einem Container statt jeweils einzelne auf dessem Inhalt zu verwenden.
dedlfix.
@@dedlfix
ich habe ein Div, auf dem ein Eventhandler registriert ist.
Hier steckt schon ein Fehler.
div
s kann man nicht clicken.Und warum soll man darauf keinen Click-Handler registrieren dürfen? Im Zusammenhang mit Event Bubbling ist das doch legitim, nur einen Handler auf einem Container statt jeweils einzelne auf dessem Inhalt zu verwenden.
Von Verarbeitung des vom Click auf die Checkbox ausgelösten Event am div
war hier keine Rede.
Klar kann man Eventhandler weiter oben registrieren und Event-Bubbling nutzen – wenn die Targets interaktive Elemente sind (oder Kindelemente davon).
😷 LLAP
Hallo Gunnar,
wenn es sich nicht um andere interaktive Elemente wie <img usemap=… handelt.
was nicht responsiv und darum ebenfalls blöd ist.
Ein SVG mit klickbaren Flächen ist auch nicht immer eine Option, gerade, wenn ein Punkt in einer Bitmap auszuwählen ist.
Welche Lösung bleibt dann?
Rolf
Hallo Gunnar,
Hier steckt schon ein Fehler.
div
s kann man nicht clicken. Einige Nutzer (die sowas wie eine Maus verwenden) können es, viele andere nicht (z.B. Tastaturnutzer).
Die Tastaturnutzer will ich ausschließen. Deshalb mach ich das so. 😉
Jörg
@@Jörg
Die Tastaturnutzer will ich ausschließen. Deshalb mach ich das so. 😉
Damit schließt du mich als Antwortenden aus. Keine Lust, hier Trolle zu füttern.
😷 LLAP
Hallo Gunnar,
da war ein 😉, hast Du deinen Humor heute morgen im Bett gelassen?
Rolf
Hallo Gunnar,
da war ein 😉, hast Du deinen Humor heute morgen im Bett gelassen?
Danke @Rolf War wirklich als netter, kleiner Scherz gedacht 😀
Jörg
Hallo Jörg,
da war ein 😉, hast Du deinen Humor heute morgen im Bett gelassen?
War wirklich als netter, kleiner Scherz gedacht 😀
wenn es um Accessibility geht, verstehen hier manche Nutzer aber gar keinen Spaß.
Live long and pros healthy,
Martin
Moin,
ich möchte mich mit einem vergleichbaren Problem anschließen.
Den Ablauf von Audio- und Video-Dateien möchte ich mit der Leertaste anhalten und wieder freigeben. Unabhängig davon, welches Element der Seite den Focus hat:
// Leertaste = toggle
var paused = true;
function startStop( evt ) {
if ( evt.keyCode == 32 ) {
evt.stopPropagation();
if ( paused ) {
for ( let i=0; i < arr_ausgewaehlte_videos.length; i++ ) {
arr_ausgewaehlte_videos[i].play();
}
for ( let i=0; i < arr_ausgewaehlte_audios.length; i++ ) {
arr_ausgewaehlte_audios[i].play();
}
paused = false;
} else {
for ( let i=0; i < arr_ausgewaehlte_videos.length; i++ ) {
arr_ausgewaehlte_videos[i].pause();
}
for ( let i=0; i < arr_ausgewaehlte_audios.length; i++ ) {
arr_ausgewaehlte_audios[i].pause();
}
paused = true;
}
}
}
window.addEventListener('DOMContentLoaded', function () {
// Leertaste bedeutet start/stop
//document.removeEventListener("keydown", tutNichts );
document.addEventListener("keydown", startStop );
});
Wenn ich die Leertaste (32) erkenne, setze ich SOFORT stopPropagation. Nachdem die Audio-Dateien per Javascript mit [alle play] gestartet wurden (wo liegt dann der Focus?) werden sie bei Leertaste kurz angehalten, laufen aber sofort wieder weiter. Warum ist stopPropagation hier wirkungslos?
Wenn der Textteil im Focus liegt (irgendwo in den Textteil geklickt), stoppt die Musik bei Leertaste tatsächlich, aber gleichzeitig springt die Seite ein Bild nach oben wie auf anderen Webseiten auch. Wieso?
Linuchs
Tach!
Wenn ich die Leertaste (32) erkenne, setze ich SOFORT stopPropagation.
Javascript wird singlethreaded ausgeführt. Es ist egal, ob du das am Anfang oder am Ende des Eventhandlers setzt. Während er läuft, passiert mit dem Event nichts weiter.
Nachdem die Audio-Dateien per Javascript mit [alle play] gestartet wurden (wo liegt dann der Focus?) werden sie bei Leertaste kurz angehalten, laufen aber sofort wieder weiter. Warum ist stopPropagation hier wirkungslos?
Ist es das? Wirf deinen Debugger an, setz ein paar Breakpoints oder console.log()-Ausgaben und schau nach, wann was aufgerufen wird.
dedlfix.
Hallo dedlfix,
ist schon alles richtig. Aber trotz stopPropagation braucht es noch ein preventDefault, gerade mal in den DevTools probiert. Warum, weiß ich auch nicht so recht - ohne Propagation bekommt der Brauser ja eigentlich nichts mehr vom Tastendruck mit.
Update: Kollege Mozilla meint dazu:
The stopPropagation() method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases. It does not, however, prevent any default behaviors from occurring; for instance, clicks on links are still processed. If you want to stop those behaviors, see the preventDefault() method.
Die DOM-Spec unterscheidet auch zwischen einem stop-propagation und einem canceled Flag. preventDefault setzt canceled. Und offenbar findet das Default-Verhalten eines Events unabhängig von der Propagation statt. Aber ob die Spec das hergibt, dafür müsste ich mir diesen Haufen "if this then that" durchlesen, aber offenbar findet das Default-Verhalten außerhalb des Bubblings statt.
Update: Soweit ich das begreife, wird diese Hypothese von der Spec bestätigt. Schritt 5.13 und 5.14 beschreiben das Bubbling, und das Default-Verhalten ("activation behavior") folgt in Schritt 11 separat.
Rolf
Javascript wird singlethreaded ausgeführt.
Das bedeutet was? stopPropagation wird bis zum Ende der function ignoriert? Also muss ich das Durchhecheln und den Start/Stop der Audios in eine neue function alleToggle legen? Sowas wie
function startStop( evt ) {
if ( evt.keyCode == 32 ) {
evt.stopPropagation();
alleToggle();
}
}
und schau nach, wann was aufgerufen wird.
Komisch, wenn ich mit alert nach stopPropagation eine Verzögerung einbaue, klappt es mit dem Stop/Start, wenn zuvor mit JavaScript gestartet. Fall B, das Blättern um ein Bild nach oben bleibt aber.
function startStop( evt ) {
if ( evt.keyCode == 32 ) {
// alert( "vor stopPropagation" );
evt.stopPropagation();
alert( "nach stopPropagation" );
...
}
Hallo Linuchs,
überwache Ereignisse nicht mit alert, logge lieber in die Konsole. Gerade bei Tastatur oder Maus schießt der Umstand quer, dass da auf einmal ein Dialog aufgeht. Was Du mit der alert-Positionierung beobachtest, leidet also sozusagen unter Quanteneffekten - der Umstand, dass beobachtet wird, ändert das Experiment.
Den Grund, warum es nicht klappt, habe ich ja schon erklärt.
Rolf
Hallo Rolf,
Den Grund, warum es nicht klappt, habe ich ja schon erklärt.
Hmm, da steige ich nicht ganz durch. Habe verstanden: Events, die auch ohne Javascript passieren (etwa der Aufruf einer neuen Seite bei <a href
müssen NICHT mit stopPropagation, sondern mit preventDefault unterbunden werden.
Das funktioniert tatsächlich bei Leertaste, wenn zuvor in den Textbereich geklickt wurde, das Blättern um ein Bild nach oben unterbleibt:
function startStop( evt ) {
if ( evt.keyCode == 32 ) {
// evt.stopPropagation();
evt.preventDefault();
...
}
Doch wenn die Audios per Javascript [alle play] gestartet wurden, stoppen sie kurz und laufen wie zuvor unverändert weiter.
Die Logik wäre ja, die per Javascript eingerichteten Events mit stopPropagation zu behandeln, also die kommentierte Zeile freizugeben. Funktioniert nicht. Audios stoppen kurz und laufen sofort wieder an.
Linuchs
Hallo Linuchs,
Den Grund, warum es nicht klappt, habe ich ja schon erklärt.
Hmm, da steige ich nicht ganz durch. Habe verstanden: Events, die auch ohne Javascript passieren (etwa der Aufruf einer neuen Seite bei
<a href
müssen NICHT mit stopPropagation, sondern mit preventDefault unterbunden werden.
was für ein Event es ist, spielt keine Rolle (wenn ich das richtig verstanden habe). Sondern wo es wirken darf: preventDefault() unterbindet die Standardaktion des Elements, dem das Event eigentlich gilt, und stopPropagation() verhindert, dass das Event noch von anderen Elementen (DOM-Vorfahren) behandelt werden kann.
Das funktioniert tatsächlich bei Leertaste, wenn zuvor in den Textbereich geklickt wurde, das Blättern um ein Bild nach oben unterbleibt:
Ich bin ja echt irritiert. Mir ist noch nie aufgefallen, dass das Drücken der Leertaste im Browser irgendeine Aktion auslöst (außer wenn ein Link oder ein Formularelement focussiert ist). Was genau die Wirkung der Leertaste ist, verstehe ich auch noch nicht; wenn ich es hier auf der Beitragsseite versuche, springt die Anzeige jedenfalls nach unten, zum Threadbaum.
Live long and pros healthy,
Martin
Hallo Martin,
Mir ist noch nie aufgefallen, dass das Drücken der Leertaste im Browser irgendeine Aktion auslöst (außer wenn ein Link oder ein Formularelement focussiert ist).
Bin soeben auch drauf gekommen: Wenn ich auf den Button [alle play] klicke, laufen die Audio-Dateien an und der Button bleibt fokussiert. Die Leertaste wirkt also auch auf diesen Button und startet wieder.
Entsprechend wenn [alle Pause] fokussiert ist. Audios laufen bei Leertaste los und stoppen sofort wieder. Was mich irritiert: Weder stopPropagation noch preventDefault kann diesen Geist aufhalten.
Muss ich mal tüfteln, ob ich ihm die Aktivität bei Leertaste entziehen kann. Bei Klick soll es wie bisher funktionieren.
Linuchs
@@Linuchs
Muss ich mal tüfteln, ob ich ihm die Aktivität bei Leertaste entziehen kann.
Nein. Dass ein Button per Leertaste bedient werden kann, ist gefordertes und von Nutzern erwartetes Standardverhalten und sollte nicht entfernt werden.
😷 LLAP
Muss ich mal tüfteln, ob ich ihm die Aktivität bei Leertaste entziehen kann. Bei Klick soll es wie bisher funktionieren.
Die Idee: Den jeweils anderen Button focussieren:
<button onclick="aufStart('lied')">alle zum Start</button>
<button id="allePlay" onclick="playAudios('lied'); document.getElementById('allePause').focus;">alle play</button><!-- wenn focus, wirkt die Leertaste als Ausloeser -->
<button id="allePause" onclick="pauseAudios('lied'); document.getElementById('allePlay').focus; ">alle pause</button>
Das Fokussieren wird leider ignoriert.
Edit: Mit focus() funktioniert's. Endlich.
@@Linuchs
Die Idee: Den jeweils anderen Button focussieren
ist vielleicht auch keine so gute Idee. Finden Nutzer es gut, wenn der Fokus unerwartet ganz woanders ist?
Wozu brauchst du überhaupt zwei Buttons? Tut’s nicht einer – ein Toggle-Button? Beim Drücken startet die Musik, beim nächsten Drücken stoppt sie …
“Design isn’t crafting a beautiful, textured button with breathtaking animation. It’s figuring out if there’s a way to get rid of the button altogether.” —Edward Tufte
😷 LLAP
Tach!
Das bedeutet was? stopPropagation wird bis zum Ende der function ignoriert?
Das Event ist kein magisches Ding, das irgendwas undurchschaubares macht. Es ist ein Objekt wie alle anderen auch. stopPropagation() an diesem Objekt aufgerufen wird vermutlich nicht viel mehr tun, als ein Flag zu setzen, also eine Eigenschaft des Objekts. Wenn dein Eventhandler fertig ist, übernimmt die Eventsteuerung wieder und wird die Eigenschaften des Objekts daraufhin untersuchen, ob noch weitere Handler aufzurufen sind oder nicht oder was auch immer zu tun ist.
Und wie Rolf B anmerkte, hilft stopPropagation() nur das Weitergeben an andere Elemente zu verhindern, aber stoppt keine Default-Handler am gleichen Element. Da muss noch preventDefault() dazukommen.
dedlfix.