Frage zum Wiki-Artikel „JavaScript“
BonBoni
- frage zum wiki
- javascript
Hallo zusammen, ich habe eine Darstellung die wie folgt aussieht:
Minus Buttion - Anzeige des Zählerstands - plus-Button
Man kann hier den Zählerstand verringer bzw. erhöhen durch klick auf den entsprechenden Button.
<script type="text/javascript">
click_1 = 0;
function plus_1() {
click_1 += 1;
document.getElementById("click_1").innerHTML = click_1;
};
function minus_1(){
click_1 -= 1;
document.getElementById("click_1").innerHTML = click_1;
};
function pruefen_1(){
if (click_1<0) {
plus_1();
}
};
</script>
<p>
<button class="btn btn-primary" type="button" onclick="minus_1(); pruefen_1()">-
</button>
<a class="btn btn-primary" id="click_1">0</a>
<button class="btn btn-primary" type="button" onclick="plus_1()">+</buttonQuelltext hier
Ich speichere den Wert der Variablen in einer variablen click_1. Ich will diesen Wert am Ende der Seite ausgeben. Leider komme ich nicht drauf, wie ich die Variable weiter unten in der Website darstellen kann. Könnt ihr mir hier helfen? Ich will den wert von click_1 ausgeben. Das funkt iwie nicht.
Danke schon mal!
Grüße
Hallo BonBoni,
Grundsätzlich ist der Einsatz von globalen Variablen zu vermeiden. Du kannst in plus_1 und minus_1 den Wert des click_1 Elements auslesen, mit parseInt eine Zahl draus machen, verändern und zurückschreiben.
Für die eigentliche Veränderung des click_1 Elements würde ich eine eigene Funktion schreiben. Die kann sich auch darum kümmern, dass keine negativen Werte entstehen und bekommt einfach die Änderungsmenge (+1 oder -1) als Parameter übergeben:
function plus_1() {
updateClickcount(1);
}
function minus_1() {
updateClickcount(-1);
}
function updateClickcount(delta) {
const counterElement = document.getElementById("click_1");
// textContent, nicht innerHTML
const newValue = parseInt(counterElement.textContent) + delta;
if (newValue>= 0)
counterElement.textContent = newValue;
}
Was es mit diesem const
auf sich hat, steht hier. Siehe dort auch die Links auf ausführlichere Artikel zu const und let.
Ich will diesen Wert am Ende der Seite ausgeben. Das funkt iwie nicht.
„Funkt iwie nicht“ funkt iwie nicht als Problembeschreibung.
Könntest Du dein Vorhaben genauer erläutern? Wo genau soll dieser Wert hin und wodurch soll das ausgelöst werden? Ausgeben tust Du ihn ja schon - in diesem click_1 Element (das übrigens ein a ist ohne id oder href - was soll das darstellen?).
Nebenbei: Du könntest Dich auch mit unobtrusive Javascript beschäftigen und click-Handler mit addEventListener registrieren, statt mit einem onclick-Attribut. Das steht im Wiki-Artikel zur Ereignisverarbeitung.
Rolf
@@Rolf B
Grundsätzlich ist der Einsatz von globalen Variablen zu vermeiden.
Ist er?
function updateClickcount(delta) { const counterElement = document.getElementById("click_1");
Ich würde sagen: Grundsätzlich sind unnötig wiederholte Suchen von Elementen im DOM zu vermeiden. Das Element muss nur einmal gesucht werden – also außerhalb der Eventhandlerfunktion:
const counterElement = document.getElementById("click_1");
function updateClickcount(delta) {
Wenn dir die globale Variable nicht schmeckt, kannste das ganze ja noch in eine(n?) IIFE tun.
😷 LLAP
Hi Gunnar
const counterElement = document.getElementById("click_1"); function updateClickcount(delta) {
Wenn dir die globale Variable nicht schmeckt, kannste das ganze ja noch in eine(n?) IIFE tun.
Wenn du eh const
verwendest, brauchst du keinen IIFE. Dieses Konstrukt hat man verwendet um dem Umstand Rechnung zu tragen, dass Variablen die mit var
deklariert werden an den Scope der umgebenden Funktion bzw. des umgebenden Skripts gebunden werden.
Variablen, die mit let
oder const
deklariert werden, haben aber Blockscope, das heißt, es reicht ein einfaches Block Statement um einen neuen Gültigkeitsbereich zu erzeugen.
{
const x = 42
}
console.log(x) // Reference Error
Eine weitere Möglichkeit wäre <script type="module">
. Die im globalen Scope eines Moduls deklarierten Variablen und Funktionen sind auf diesen Scope beschränkt und leaken nicht in den Scope von anderen Modulen oder Skripten, die gegebenenfalls noch eingebunden sind.
Viele Grüße,
Orlok
@@BonBoni
Minus Buttion - Anzeige des Zählerstands - plus-Button
Und wozu brauchst du da JavaScript?
<input type="number"/>
– fertig. [MDN]
Man kann hier den Zählerstand verringer bzw. erhöhen durch klick auf den entsprechenden …
Pfeil. Eben.
😷 LLAP
@@Gunnar Bittersmann
Minus Buttion - Anzeige des Zählerstands - plus-Button
Und wozu brauchst du da JavaScript?
Antwort: Weil die vom Browser angebotenen Pfeile ziemlich mickrig sind. Das kann man mit unobtrusive JavaScript verbessern (progressive enhancement).
<input type="number"/>
ist dafür die Basis. Dieser Elementtyp bietet bereits die Methoden stepDown()
und stepUp()
an – da muss man nichts mehr selber implementieren.
Bei ausgeführtem JavaScript werden per CSS die Pfeile ausgeblendet und Buttons [−] und [+] angezeigt (die per visuell verstecktem span
-Element oder aria-label
eine auch für Nutzer assistiver Technologien wie Screenreadern zugängliche Beschriftung erhalten).
Die Vorbelegung mit value
-Attribut ist nötig, sonst funktioniert’s im Safari nicht.
Per min
-Attribut kann man ganz ohne JavaScript dafür sorgen, dass die Anzahl nicht negativ werden kann. Bei Bedarf auch per max
-Attribut, dass sie einen bestimmten Wert nicht übersteigen kann.
Sieht dann so aus: Codepen.
😷 LLAP
@@Gunnar Bittersmann
Sieht dann so aus: Codepen.
Eine Frage hätte ich dazu noch. Geht:
lightsDecrementButton.addEventListener('click', () => {
lightsInputElement.stepDown();
});
Geht nicht:
lightsDecrementButton.addEventListener('click', lightsInputElement.stepDown);
Warum nicht?
😷 LLAP
Hi Gunnar
Eine Frage hätte ich dazu noch. Geht:
lightsDecrementButton.addEventListener('click', () => { lightsInputElement.stepDown(); });
Geht nicht:
lightsDecrementButton.addEventListener('click', lightsInputElement.stepDown);
Warum nicht?
Weil die Bindung an ein Kontextobjekt (this
) erst beim Aufruf einer Funktion erfolgt. Wenn du die Funktion über ein Objekt referenzierst, ohne sie aufzurufen, dann hast du nur einen Zeiger auf die Funktion, aber die Verknüpfung mit dem Objekt ist verloren gegangen.
Deshalb musst du die Funktion entweder explizit als Methode aufrufen, wie in deinem ersten Beispiel, oder mittels bind
eine Funktion erzeugen, bei der this
an das gewünschte Objekt gebunden ist.
Viele Grüße,
Orlok
@@Orlok
oder mittels
bind
eine Funktion erzeugen, bei derthis
an das gewünschte Objekt gebunden ist.
Wie würde das im konkreten Fall aussehen?
😷 LLAP
Hallo Gunnar,
lightsDecrementButton.addEventListener(
'click',
lightsInputElement.stepDown.bind(lightsInputElement)
);
De facto erzeugt bind
aber nichts anderes, als eine generische Variante deiner Adapterfunktion.
Da dein stepDown kein Argument erwartet, ist das hier alles das selbe:
() => lightsInputElement.stepDown()
event => lightsInputElement.stepDown(event)
event => lightsInputElement.stepDown()
lightsInputElement.stepDown.bind(lightsInputElement)
(Edit: Dedlfix' Hinweis beachtet)
Du hast mit dem Einsatz von bind nichts gespart, außer eigener Tipparbeit. Und hier hast Du selbst das nicht getan, weil lightsInputElement ein so ausführlicher Variablenname ist 😂. Es müsste dann schon auf ungarisch übersetzt werden:
ieLight.stepDown.bind(ieLight)
Rolf
Tach!
Da dein stepDown kein Argument erwartet, ist das hier alles das selbe:
Er übergibt kein Argument, aber stepDown() arbeitet mit einem, wenn eins übergeben wird. Das wäre dann ein Faktor für den Step.
() => lightsInputElement.stepDown()
event => lightsInputElement.stepDown(event)
lightsInputElement.stepDown.bind(lightsInputElement)
Die mittlere Variante ist falsch, in den beiden andere Varianten bekommt stepDown vom event-Objekt nichts mit. Und hier übergibst du es als Argument, wo stattdessen ein optionaler Schrittfaktor erwartet wird.
dedlfix.
Hallo dedlfix,
Ups. Ja, ich hätte mir erstmap stepDown anschauen sollen.
Rolf
Tach!
oder mittels
bind
eine Funktion erzeugen, bei derthis
an das gewünschte Objekt gebunden ist.Wie würde das im konkreten Fall aussehen?
lightsDecrementButton.addEventListener('click',
lightsInputElement.stepDown.bind(lightsInputElement));
oder auch
lightsDecrementButton.addEventListener('click',
HTMLInputElement.prototype.stepDown.bind(lightsInputElement));
Aber bleib lieber bei deiner "Geht"-Variante. Die lässt sich direkt lesen. Bei bind() muss man die Arbeitsweise von bind() kennen, um das Konstrukt zu verstehen. Und es gibt hier keine Notwendigkeit, eine Bindung erst zur Laufzeit zu erstellen.
In der ersten Variante erzeugt der Teil lightsInputElement.stepDown
zunächst eine ungebundene Referenz auf die Funktion, nur damit bind()
die Bindung wiederherstellt. Der Autor gibt dem Leser erstmal eine Knobelaufgabe mit, das Konstrukt und den Sinn dahinter zu verstehen. Der ist hier nicht wirklich vorhanden, weil der Aufruf auch gleich gebunden hätte stattfinden können.
Für die zweite Variante bringt man noch zusätzlich den/einen Prototypen ins Spiel. Das ist eine technische Notwendigkeit, um das bind() nutzen zu können, und wäre dann nötig, wenn lightsInputElement
die Methode stepDown nicht hätte. Fachlich spricht nichts dafür, das für den vorliegenden Anwendungsfall so umständlich zu lösen.
dedlfix.
Hallo dedlfix,
Aber bleib lieber bei deiner "Geht"-Variante
Dem schließe ich mich an...
In der ersten Variante erzeugt der Teil ... zunächst eine ungebundene Referenz
Dem schließe ich mich nicht an. Er erzeugt sie nicht. Er liest sie einfach aus dem Objekt.
Das ist eine technische Notwendigkeit, um das bind() nutzen zu können, und wäre dann nötig, wenn lightsInputElement die Methode stepDown nicht hätte.
Hä? Wenn es die nicht hätte, wäre eine Bindung von lightsInputElement
als this
wenig sinnvoll. lightsInputElement
hat HTMLInputElement
als Prototyp und damit schaut lightsInputElement.stepDown
zuerst, ob das Objekt selbst die Methode enthält, und geht danach die Prototypkette durch.
Direkt auf den Prototypen zuzugreifen kann nur dann nötig werden, wenn Du davon ausgehst, dass irgendwer eine stepDown-Methode direkt ans Objekt geklebt hat und Du definitiv die Prototyp-Methode willst.
Rolf
Tach!
In der ersten Variante erzeugt der Teil ... zunächst eine ungebundene Referenz
Dem schließe ich mich nicht an. Er erzeugt sie nicht. Er liest sie einfach aus dem Objekt.
Das ist für mich dasselbe nur anders formuliert.
Das ist eine technische Notwendigkeit, um das bind() nutzen zu können, und wäre dann nötig, wenn lightsInputElement die Methode stepDown nicht hätte.
Hä? Wenn es die nicht hätte, wäre eine Bindung von
lightsInputElement
alsthis
wenig sinnvoll.lightsInputElement
hatHTMLInputElement
als Prototyp und damit schautlightsInputElement.stepDown
zuerst, ob das Objekt selbst die Methode enthält, und geht danach die Prototypkette durch.
Ja, im vorliegenden Fall hat lightsInputElement über die Prototype-Kette eine stepDown Funktion. Deswegen ist diese Variante ja hier auch nicht notwendig. Ich meinte, dass diese Variante bei Konstrukten verwendet werden müsste, bei dem das nicht der Fall ist, zum Beispiel einer statischen Funktion einer Klasse, die auf ein this zugreift, das erst zur Laufzeit gebunden wird.
Direkt auf den Prototypen zuzugreifen kann nur dann nötig werden, wenn Du davon ausgehst, dass irgendwer eine stepDown-Methode direkt ans Objekt geklebt hat und Du definitiv die Prototyp-Methode willst.
Um für den vorliegenden Fall ein solches Konstrukt zu verwenden, müsste ich über den Prototype gehen, was aber nur unnötig umständlich ist, weil es auch direkt geht.
dedlfix.
Hallo dedlfix,
Das ist für mich dasselbe nur anders formuliert.
Na gut. Für mich wäre „erzeugen“ ein ex- oder implizites new
eines Objekts.
zum Beispiel einer statischen Funktion einer Klasse, die auf ein this zugreift, das erst zur Laufzeit gebunden wird.
Statische Funktionen greifen auf kein this zu. Darum heißen sie statisch.
Aber ich denke, du meinst sowas wie Array.prototype.forEach, das Du auf ein Array-ähnliches Objekt anwenden willst, das kein eigenes forEach hat.
Rolf
Tach!
Statische Funktionen greifen auf kein this zu. Darum heißen sie statisch.
Ehrlich gesagt fällt es mir schwer, ein sinnvolles Anwendungsbeispiel für bind() zu finden, wo es wirklich notwendig ist und nicht durch einfachere Konstrukte abgebildet werden kann. Deswegen hab ich nur solche eigenartigen Beispiele auf Lager.
dedlfix.
Hallo dedlfix,
ich dachte, dass bind() vielleicht eine Altlast sei, aber nein, sie ist in ECMAScript 5 hinzugekommen.
Deshalb habe ich mal das Nashorn gefragt, und da steht, dass bind() mehr kann als ein eigener Wrapper.
Ein eigener bind-Wrapper kann this festlegen und partielle Applikation ausführen (einen Teil der Parameter vorgeben). An der Stelle kann er mehr als bind, weil man so beliebige Parameter vorgeben kann, nicht unbedingt nur die ersten. Die zurückgegebene Funktion hat, da sie im eigenen Wrapper für den konkreten Zweck definiert wird, eine korrekte Stelligkeit (was laut LEO die Übersetzung für arity sein soll).
instanceof
Operator von einem ungebundenen nicht zu unterscheiden.Das sind aber alles Randfälle und vermutlich hauptsächlich für Libraries wichtig.
Rolf
Servus!
@@BonBoni
Minus Buttion - Anzeige des Zählerstands - plus-Button
Und wozu brauchst du da JavaScript?
<input type="number"/>
– fertig. [MDN]Man kann hier den Zählerstand verringer bzw. erhöhen durch klick auf den entsprechenden …
Pfeil. Eben.
Grad auf twitter geufnden! 😀
Herzliche Grüße
Matthias Scharwies