Rolf B: Javascript - getter Eigenschaft ist undefined

Beitrag lesen

problematische Seite

Hallo ebody,

nein, ein that ist eine Krücke. Eine Krücke mit Tradition, aber trotzdem eine Krücke.

In einem function-Eventhandler ist this das currentTarget des Events. D.h. das Element, auf dem addEventListener aufgerufen wurde. Das kann mit target übereinstimmen, muss aber nicht, wenn bubbling stattfindet.

Es ist demnach schwer verständlich, im change-Eventhandler this zu verwenden und damit das DOM Element zu meinen, in dem der change stattfand. Es ist noch schwerer verständlich, wenn man this und event.target gleichzeitig verwendet. Dein 4 Monate älteres Ich fragt sich dann um Ostern rum: Was hat der verfluchte Kerl da kurz vor Weihnachten nur getan? Warum mal this, warum mal event.target - was war die Notwendigkeit dafür?!?!?! Gibt keine. Verwende immer event.target.

Bei Dir kommt verschärfend hinzu, dass der Eventhandler eine lokale Funktion in einer Methode ist. In einem solchen Umfeld verbinde ich this eigentlich automatisch mit dem Objekt, zu dem die Methode gehört. Darf ich in dem Moment aber nicht. Ich muss ständig nachdenken, wofür jetzt this steht. Und das ist Mist. Jedes Nachdenken über die richtige Bedeutung ist einmal zu viel nachgedacht.

Weiteres Problem: Du registrierst die change-Handler in einer Schleife auf jeder Checkbox einzeln. Lass das. Registriere den Handler nur einmal, auf #chipsbox. Die change Events entstehen auf den Checkboxen und blubber von da aus im DOM nach oben. Wenn sie am chipsbox-div vorbeikommen, kannst Du sie mit einer einzigen Eventhandler Registrierung behandeln und kannst Dir die Registrierschleife sparen.

Um das this-Problem zu lösen, kannst Du - aus Lesbarkeitsgründen - die Handlerfunktion separat aufschreiben und als Eventhandler die an this gebundene Funktion übergeben. Oder du kannst sie gleich als Pfeilfunktion notieren, dann entsteht keine neue this-Bindung und du verwendest automatisch das this des Eltern-Scopes.

saveSelection() {
   let objFilterCollection = {};
   const chipsBox = document.getElementById('chipsBox');
   chipsBox.addEventListener(
               'change', event => {
      // Handle on
   });
}

Nächster Punkt: An Arrays herumzufummeln, um einzeln Werte reinzuschieben oder rauszuholen, ist lästig, mühsam, fehlerträchtig. Kurz: sollte man lassen. Viel einfacher ist es, das Array bei jeder Änderung neu aufzubauen. Dabei kann man sich sogar noch von einem CSS Selektor helfen lassen - die aktiven Checkboxen kriegst Du mit querySelectorAll("input[type=checkbox]:checked").

Wenn Du dann noch alle Checkboxen zu einem Thema in ein Fieldset kapselst, kannst Du den Key ans Fieldset stellen und brauchst ihn nicht an jede Checkbox zu schreiben. Dorthin kommen nur die Values. Und die auch nicht als data, sondern als value-Attribut. Dafür gibt's das schließlich. WENN Du unbedingt den Key an der Checkbox haben willst - nagut, steck ihn in's name-Attribut. Wenn Du das in einem Form hast und an einen PHP Server submitten willst, dann mit name="Genre[]" oder so, damit das Checkboxarray am Server auch als Array ankommt. Aber solange Du das nur in JavaScript machst, ohne Server, brauchst Du Dich um ein name Attribut der Checkboxen nicht zu kümmern.

Meine Idee zum Thema

Der komplexeste Teil in meinem Fiddle sind vermutlich diese beiden Zeilen:

const keySet = container.querySelectorAll(`input[type=checkbox]:checked`);
this.filters[key] = Array.prototype.map.call(keySet, checkbox => checkbox.value);

Aber dafür übernehmen die auch den größten Teil deines Scripts. Zeile 1: container ist eine Variable, die auf das fieldset zeigt. Mit querySelectorAll suche ich im fieldset alle checkboxen, die checked sind. Das Ergebnis ist eine NodeList.

Eine NodeList ist kein Array, deshalb hat sie keine map-Methode. Aber sie ist array-artig genug, um sie der map-Methode von Array.prototype als Array unterzuschieben (dafür braucht's nur eine length-Eigenschaft und Werte, die mit 0,1,2,3... indexiert sind).

Array.prototype.map.call

tut genau das. call ist eine Methode, die jedes Function-Objekt hat. Man kann sie damit aufrufen und einen Wert für this festlegen. Ich verwende damit also die map-Methode der Arrays für die NodeList, die in keySet steht. Die map-Methode bekommt einen Callback, der auf jedes Array-Element angewendet wird, und das Ergebnis ist ein Array mit den Ergebnissen. D.h. ich bekomme ein Array mit den value-Werten aller Checkboxen, die angehakt sind. Und das speichere ich in this.filters[key]. Wenn da vorher schon was stand, egal, weg damit, ist veraltet.

So kurz und knackig kann das gehen 😉

Rolf

--
sumpsi - posui - obstruxi