BonBoni: Frage zum Wiki-Artikel „JavaScript“

problematische Seite

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

  1. problematische Seite

    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

    --
    sumpsi - posui - obstruxi
    1. problematische Seite

      @@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

      --
      “When I was 5 years old, my mother always told me that happiness was the key to life. When I went to school, they asked me what I wanted to be when I grew up. I wrote down ‘happy.’ They told me I didn’t understand the assignment, and I told them they didn’t understand life.” —John Lennon
      1. problematische Seite

        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

  2. problematische Seite

    @@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

    --
    “When I was 5 years old, my mother always told me that happiness was the key to life. When I went to school, they asked me what I wanted to be when I grew up. I wrote down ‘happy.’ They told me I didn’t understand the assignment, and I told them they didn’t understand life.” —John Lennon
    1. problematische Seite

      @@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

      --
      “When I was 5 years old, my mother always told me that happiness was the key to life. When I went to school, they asked me what I wanted to be when I grew up. I wrote down ‘happy.’ They told me I didn’t understand the assignment, and I told them they didn’t understand life.” —John Lennon
      1. problematische Seite

        @@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

        --
        “When I was 5 years old, my mother always told me that happiness was the key to life. When I went to school, they asked me what I wanted to be when I grew up. I wrote down ‘happy.’ They told me I didn’t understand the assignment, and I told them they didn’t understand life.” —John Lennon
        1. problematische Seite

          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

          1. problematische Seite

            @@Orlok

            oder mittels bind eine Funktion erzeugen, bei der this an das gewünschte Objekt gebunden ist.

            Wie würde das im konkreten Fall aussehen?

            😷 LLAP

            --
            “When I was 5 years old, my mother always told me that happiness was the key to life. When I went to school, they asked me what I wanted to be when I grew up. I wrote down ‘happy.’ They told me I didn’t understand the assignment, and I told them they didn’t understand life.” —John Lennon
            1. problematische Seite

              Hallo Gunnar,

              lightsDecrementButton.addEventListener(
                 'click', 
                 lightsInputElement.stepDown.bind(lightsInputElement)
              );
              

              Wiki-Referenz

              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

              --
              sumpsi - posui - obstruxi
              1. problematische Seite

                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.

                1. problematische Seite

                  Hallo dedlfix,

                  Ups. Ja, ich hätte mir erstmap stepDown anschauen sollen.

                  Rolf

                  --
                  sumpsi - posui - obstruxi
            2. problematische Seite

              Tach!

              oder mittels bind eine Funktion erzeugen, bei der this 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.

              1. problematische Seite

                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

                --
                sumpsi - posui - obstruxi
                1. problematische Seite

                  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 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.

                  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.

                  1. problematische Seite

                    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

                    --
                    sumpsi - posui - obstruxi
                    1. problematische Seite

                      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.

                      1. problematische Seite

                        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).

                        • ein Polyfill für bind hat mit korrekter Stelligkeit Schwierigkeiten, weil man die Stelligkeit einer selbsterstellten Funktion nicht variabel setzen kann. Man müsste für jede Stelligkeit eine eigene Funktion im Polyfill vorhalten. Das eingebaute bind löst das irgendwie.
                        • Man kann Konstruktorfunktionen bind()-en und sie für den new Operator verwenden. Das erzeugte Objekt hat den Prototypen des gebundenen Konstruktors, und verweist mit dem constructor-Property auch darauf. Das natürlich nur im Zusammenhang mit partieller Applikation (festlegen der ersten Parameter) sinnvoll.
                        • Ein gebundener Konstruktor ist für den instanceof Operator von einem ungebundenen nicht zu unterscheiden.

                        Das sind aber alles Randfälle und vermutlich hauptsächlich für Libraries wichtig.

                        Rolf

                        --
                        sumpsi - posui - obstruxi
    2. problematische Seite

      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

      --
      Einfach mal was von der ToDo-Liste auf die Was-Solls-Liste setzen.“