Vanilla – best practice gesucht
Matthias Apsel
- javascript
Hallo alle,
ich möchte mich für ein Projekt von JQuery trennen.
Ich habe ein Formular, für das bei jeder Änderung die Aktion "A" ausgelöst werden soll, bei Betätigung eines Buttons einer bestimmten Klasse (von denen es mehrere geben kann) soll jedoch erst die Aktion "B" ausgelöst werden. Meine JQuery-Lösung funktioniert.
do_B = function() {
// do something;
do_A();
}
do_A = function() {
// do something;
}
$('form').on('click', '.foo', do_B);
// JQuerys 'click' lauscht auch auf Tastatur und Touch
$('form').change(do_A);
Wie würde man dies idealerweise umsetzen? A und B in eine Funktion und beim Aufruf nach event.target schauen? A und B in getrennte Funktionen? Wovon wäre diese Entscheidung abhängig?
Die Seite kann ich leider nicht zeigen.
Bis demnächst
Matthias
@@Matthias Apsel
$('form').on('click', '.foo', do_B); // JQuerys 'click' lauscht auch auf Tastatur und Touch $('form').change(do_A);
Wie würde man dies idealerweise umsetzen? A und B in eine Funktion und beim Aufruf nach event.target schauen?
Das sollte dasselbe tun (mit do_A()
und do_B()
wie gehabt):
document.forms[0].addEventListener('click', event => {
if (event.target.classList.contains('foo') {
do_B();
}
});
document.forms[0].addEventListener('change', do_A);
A und B in getrennte Funktionen? Wovon wäre diese Entscheidung abhängig?
Davon, ob B ohne A Sinn hat. Dann:
do_B = function() {
// do something;
}
do_A = function() {
// do something;
}
document.forms[0].addEventListener('click', event => {
if (event.target.classList.contains('foo') {
do_B();
do_A();
}
});
document.forms[0].addEventListener('change', do_A);
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
Lieber Gunnar,
document.forms[0].addEventListener('change', do_A);
bist Du sicher, dass es nur das erste Element in document.forms
braucht? Ich meine mich zu erinnern, dass $("form")
alle <form>
-Elemente findet. Dazu müsste man daher über alle Elemente in document.forms
iterieren.
Liebe Grüße
Felix Riesterer
Hallo Felix Riesterer,
bist Du sicher, dass es nur das erste Element in
document.forms
braucht?
Hab ich nicht deutlich hinzugeschrieben: Es gibt nur ein Form auf der Seite.
Bis demnächst
Matthias
@@Felix Riesterer
document.forms[0].addEventListener('change', do_A);
bist Du sicher, dass es nur das erste Element in
document.forms
braucht?
Wie Matthias sich ausdrückte, ja. („Ich habe ein Formular, für das …“)
Ich hätte aber korrekter schreiben sollen: Das sollte in deinem Fall dasselbe tun.
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
Hallo Gunnar Bittersmann,
document.forms[0].addEventListener('click', event => { if (event.target.classList.contains('foo') { do_B(); } }); document.forms[0].addEventListener('change', do_A);
IE11 kann leider kein =>
.
Bis demnächst
Matthias
@@Matthias Apsel
IE11 kann leider kein
=>
.
Na dann mach
document.forms[0].addEventListener('click', function (event) {
draus (wenn du keinen Transpiler einsetzt).
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
Hallo Gunnar Bittersmann,
Na dann mach
document.forms[0].addEventListener('click', function (event) {
draus
Hab ich schon 😉
Bis demnächst
Matthias
Lieber Matthias,
inzwischen unterstützen alle aktuellen Browser event.target
, so dass Du nicht mehr für den IE nach event.srcElement
suchen musst.
let do_A = function (event) {
let target = event.target; // event.target || event.srcElement für legacy IE
// ...
}
let do_b = function (event) {
let target = event.target;
// ...
do_A(event);
}
Den Eventhandler kann man in Vanilla wie üblich mit addEventListener
binden:
$('form').on('click', '.foo', do_B); // JQuerys 'click' lauscht auch auf Tastatur und Touch $('form').change(do_A);
Quick & dirty:
document.querySelectorAll("form").forEach(function (f) {
f.querySelectorAll(".foo").forEach(function (n) {
n.addEventListener("click", do_B);
});
f.addEventListener("change", do_A);
});
Je nach Alter des Browsers kann es sein, dass die von querySelectorAll
zurückgegebene Liste kein forEach
unterstützt. Da musst Du das Ergebnis erst in ein echtes Array umwandeln, bevor Du mit forEach
iterierst.
Liebe Grüße
Felix Riesterer
Hallo Felix Riesterer,
erstmal vielen Dank. Weil du das Alter ansprichst: Ich muss IE11 unterstützen.
Würden dynamisch hinzukommende Buttons der Klasse foo auch die Funktion B auslösen?
Bis demnächst
Matthias
Lieber Matthias,
Würden dynamisch hinzukommende Buttons der Klasse foo auch die Funktion B auslösen?
nein. Die Verteilung der Eventlistener geschieht ja nur einmal. Wenn du aber dynamisch erzeugte später hinzugefügte Buttons unterstützen musst, dann musst Du das Event am <body>
verankern:
document.body.addEventListener("click", function (event) {
let target = event.target || event.srcElement;
if (target.className.match(/\.foo\b/)) {
do_B(event);
}
});
document.body.addEventListener("change", function (event) {
let target = event.target || event.srcElement;
if (target.className.match(/\.foo\b/)) {
do_A(event);
}
});
Ich habe keine Ahnung, ob IE11 classList
unterstützt, daher habe ich eine RegExp verwendet.
Liebe Grüße
Felix Riesterer
Hi,
if (target.className.match(/\.foo\b/)) {
Der Punkt im Regex möchte gerne ein b sein (also vor und nach foo eine Wortgrenze). Punkt kommt im Klassennamen nicht vor. Du gehst ja direkt auf's class-Attribut, im Gegensatz zu Matthias, der im Originalposting jQuery benutzt, wo der Punkt dazu da ist, jQuery klarzumachen, daß es ein Klassen-Selektor sein soll.
cu,
Andreas a/k/a MudGuard
@@MudGuard
Der Punkt im Regex möchte gerne ein b sein
Nö, der RegExp möchte gar nicht da sein. Eine String-Funktion würde es auch tun – wenn man mit classList
nicht schon eine Liste hätte.
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
Hi,
Nö, der RegExp möchte gar nicht da sein. Eine String-Funktion würde es auch tun
Nein, includes ist nicht geeignet, im class-Attribut auf das Vorhandensein der Klasse foo zu prüfen. Nur ein Narr 😉 (pun intended) würde das tun:
alert("fool".includes("foo"));
cu,
Andreas a/k/a MudGuard
@@MudGuard
Nein, includes ist nicht geeignet, im class-Attribut auf das Vorhandensein der Klasse foo zu prüfen. Nur ein Narr 😉 (pun intended) würde das tun:
alert("fool".includes("foo"));
Foolish me.
Und der RegExp ist genausowenig geeignet:
alert("foo-bar".match(/\bfoo\b/));
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
Hi,
Und der RegExp ist genausowenig geeignet:
alert("foo-bar".match(/\bfoo\b/));
Da bin ich nicht firm genug in Javascript - da es unterschiedliche Implementierungen gibt, was ein word-character ist …
cu,
Andreas a/k/a MudGuard
@@MudGuard
da es unterschiedliche Implementierungen gibt, was ein word-character ist …
Echt? Auch das noch!
Ich dachte, word-characters wären [A-Za-z]
– was an sich auch schon Unsinn ist; damit ist \b
für menschliche Sprachen ungeeignet, die diakritische Zeichen verwenden (von zusätzlichen Buchstaben wie ß und þ ganz abgesehen) – also für alle(?) Sprachen.[1]
Das ist doch Müll:
console.log('Müll'.match(/\bM\b/)); // Array [ "M" ]
console.log('Mull'.match(/\bM\b/)); // null
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
Gibt es irgendeine natürliche Sprache, die das lateinische Alphabet ohne jegliche diakritische Zeichen verwendet? (Selbst im Latein werden doch lange Vokale mit Makron gekennzeichnet.) ↩︎
Hi,
da es unterschiedliche Implementierungen gibt, was ein word-character ist …
Echt? Auch das noch!
ja. In manchen Sprachen sind word-characters alle Buchstaben, in manchen nur die aus dem ASCII-Bereich, mal gehören die Ziffern (mal nur ASCII, mal alle) dazu, weitere Varianten nicht ausgeschlossen.
Ich dachte, word-characters wären
[A-Za-z]
M.W. gehören die Ziffern 0-9 auch noch mit rein.
Englisch?
(ok, über die Natürlichkeit könnte man streiten 😉)
cu,
Andreas a/k/a MudGuard
@@MudGuard
Ich dachte, word-characters wären
[A-Za-z]
M.W. gehören die Ziffern 0-9 auch noch mit rein.
Klar, wegen Wörtern wie H1N1.
Gibt es irgendeine natürliche Sprache, die das lateinische Alphabet ohne jegliche diakritische Zeichen verwendet? (Selbst im Latein werden doch lange Vokale mit Makron gekennzeichnet.)
Englisch?
Das zu glauben wäre naïve. Zoë nickt zustimmend.
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
Hallo Gunnar,
Selbst im Latein werden doch lange Vokale mit Makron gekennzeichnet.
Aber nur in Latein-Lehrbüchern.
(Ich frag mich ja, ob's dem Monsieur Le President recht ist, ein Flatliner zu sein...)
Rolf
@@Rolf B
Selbst im Latein werden doch lange Vokale mit Makron gekennzeichnet.
Aber nur in Latein-Lehrbüchern.
Wenn man Latein so schreiben wollen würde wie es damals geschrieben wurde, als es noch geschrieben wurde … damals gab’s noch keine Kleinbuchstaben! 😉
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
Hallo Gunnar,
ICHWEISSVNDLEERSTELLENAVCHNICHT
Rolf
@@Rolf B
ICHWEISSVNDLEERSTELLENAVCHNICHT
ICHVVEISSVNDVVVVARNOCHKEINEIGENERBVCHSTABE
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
@@Rolf B
Selbst im Latein werden doch lange Vokale mit Makron gekennzeichnet.
Aber nur in Latein-Lehrbüchern.
Sieht man auch hin und wieder, wenn jemand Russisch zusammen-kopiert-und-kleistert:
Ма́льчики, чита́ть уме́ете?[1]
Äh, nö, Betonungszeichen gibt’s nur in Russisch-Lehrbüchern.
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
Mascha und der Bär, Folge 55 ab 2:44 ↩︎
Hallo Gunnar Bittersmann,
Äh, nö, Betonungszeichen gibt’s nur in Russisch-Lehrbüchern.
die es durchaus auch online geben kann.
Bis demnächst
Matthias
@@Gunnar Bittersmann
Und der RegExp ist genausowenig geeignet:
Wenn man wirklich mit RegExp nach „foo“ in einer whitespace-separierten Liste suchen will (was man in diesem Fall nicht will), müsste man da wohl mit (?:^|\s)foo(?:$|\s)
ran.
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
@@Felix Riesterer
Wenn du aber dynamisch erzeugte später hinzugefügte Buttons unterstützen musst, dann musst Du das Event am
<body>
verankern:
Nö, musst du nicht. Ich würde das zugehörige form
-Element nehmen, nicht wahllos irgendein Element zwischen dem Formular und der Wurzel.
if (target.className.match(/\.foo\b/)) {
Ich habe keine Ahnung, ob IE11
classList
unterstützt, daher habe ich eine RegExp verwendet.
Es ist einfacher, sich die Ahnung zu verschaffen, als einen RegExp zu verwenden. Das macht nur Probleme. Besonders, wenn der Ausdruck – wie bei dir – falsch ist.
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
@@Felix Riesterer
document.querySelectorAll("form").forEach(function (f) {
Nein, so nicht. Das macht keinen Sinn. – Immer noch nicht.
f.querySelectorAll(".foo").forEach(function (n) { n.addEventListener("click", do_B); });
Warum das denn und nicht wie gehabt event delegation?
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
Hallo Matthias Apsel,
ich möchte mich für ein Projekt von JQuery trennen.
Ich glaube, ich möchte mich doch nicht damit umherärgern.
<button type="button" class="del" title="diese Zeile löschen">
<svg class="svg-inline--fa fa-minus-square fa-w-14" ></svg>
<!-- <i class="far fa-minus-square"></i> -->
</button>
Bei Klick auf den Button ist Firefox der Meinung, event.target sei das svg, IE11 meint event.target sei button.
Mit JQuery funktioniert es deutlich intuitiver. -meh
Bis demnächst
Matthias
Hallo,
<button type="button" class="del" title="diese Zeile löschen"> <svg class="svg-inline--fa fa-minus-square fa-w-14" ></svg> <!-- <i class="far fa-minus-square"></i> --> </button>
Bei Klick auf den Button ist Firefox der Meinung, event.target sei das svg
was aber nur dann richtig ist, wenn du direkt auf das Bild klickst. Was ist aber beim Klick neben das Bild, aber immer noch auf den Button? Den Fall müsstest du doch sowieso mit berücksichtigen (es sei denn, das SVG-Bild deckt den Button zu 100% ab).
IE11 meint event.target sei button.
Ja, den hättest du dann quasi nebenbei auch noch bedient.
Mit JQuery funktioniert es deutlich intuitiver. -meh
Hmm. Die einen sagen so, die anderen so.
Live long and pros healthy,
Martin
@@Matthias Apsel
Bei Klick auf den Button ist Firefox der Meinung, event.target sei das svg, IE11 meint event.target sei button.
Du willst also nicht event.target
, sondern event.target.closest('button')
.
☞ MDN – Den Polyfill gibt’s da auch gleich mit.
🖖 Stay hard! Stay hungry! Stay alive! Stay home!
Hallo Gunnar Bittersmann,
☞ MDN – Den Polyfill gibt’s da auch gleich mit.
Den Polyfill brauche ich auch schon für do_B. Im Moment habe ich so ein Mischmasch zwischen JQuery und Vanilla. Das ist nicht schön.
Bis demnächst
Matthias
Hallo Matthias,
Ich glaube, ich möchte mich doch nicht damit umherärgern.
<button type="button" class="del" title="diese Zeile löschen"> <svg class="svg-inline--fa fa-minus-square fa-w-14" ></svg> <!-- <i class="far fa-minus-square"></i> --> </button>
Bei Klick auf den Button ist Firefox der Meinung, event.target sei das svg, IE11 meint event.target sei button.
mit so etwas durfte ich mich auch beschäftigen. Such mal in https://wiki.selfhtml.org/wiki/Benutzer:JürgenB/Drag_and_Drop#Die_drei_Phasen_des_Drag_and_Drop nach der Funktion parent
Gruß
Jürgen
Hallo Matthias,
das ist ein Grundproblem bei den Buttons. Ob Elemente im Inneren eines Buttons als Event Target angesehen werden oder nicht, wird von den Browsern unterschiedlich gehandhabt.
Einige Browser sehen HTML im Inneren eines Buttons nicht als Event Target und zeigen den Button als Target, andere schon. Da sind sich die Hersteller nicht einig.
Daher der Bedarf für das .closest('button').
Hm, Update. Es scheint sich vereinheitlicht zu haben. Ich meine, der Chrome hätte früher immer den Button als event.target gesetzt. Jetzt nicht mehr.
Rolf
Hallo Rolf,
Hm, Update. Es scheint sich vereinheitlicht zu haben. Ich meine, der Chrome hätte früher immer den Button als event.target gesetzt. Jetzt nicht mehr.
Beide. Firefox und Chrome. Hat sich vor gar nicht so langer Zeit geändert; ich weiss noch, dass ich JS-Code deshalb anpassen musste.
Freundliche Grüße,
Christian Kruse
Hallo alle,
danke für die Motivation.
ca. 600 Zeilen schlechtes JQuery entsorgt (u.a. schlecht, weil ich DRY nicht beachtet hatte bzw. nicht einhalten konnte). Jetzt ist es nur noch eine Stelle, an der JQuery verwendet wird.
Bis demnächst
Matthias