Alois: Slider to JS variable

Hi all, Mein Problem: Ich versuche einen Slider-Wert in ein JS-Variable (strOut[0]) zu bringen, um den Wert dort weiter zu verarbeiten .... aber das geling nicht. Ich bitte um Hilfe!

<!DOCTYPE html>
<html>
<body>

<h2>Slider to JS-Var</h2>

<label id=Xout></label><label id=Xout2></label>

<br><br><br>
    <strong>
    <lable>Fußpunkt:</lable>
    <lable><input type="range" id="fpIn" min="-20" max="0"  value="-12"><span>0</span></lable>
  	<label> °C</lable>

<script>
const  strOut=[];

  var sliderFp = document.querySelector("input[type='range'][id='fpIn']");
  sliderFp.addEventListener("change", function(){
    document.querySelector("span").innerHTML = this.value;
    strOut[0]=this.value;
    document.getElementById("test1").innerHTML=strOut[0];
    });
    
  document.getElementById("Xout").innerHTML=">>> ";
  document.getElementById("Xout2").innerHTML=strOut[0];
</script>

</body>
</html>
  1. Hallo Alois,

    Die Zuweisung an strOut[0] funktioniert prima.

    Was nicht funktioniert - was Du erkennen würdest, wenn Du mal die Entwicklerwerkzeuge des Browsers aufmachst, einen Breakpoint bei der Zuweisung setzt und dann schrittweise weiterläufst - ist die Zuweisung an das innerHTML eines Elements mit der ID test2. Denn dieses Element gibt's nicht.

    Damit möchtest Du an die Eigenschaft innerHTML von null etwas zuweisen, und das bricht dein Script ab.

    Wenn es Dir darum geht, dass beim ersten Aufruf der Seite die Zuweisung von strOut[0] an das Xout2 Element keinen Effekt hat - das liegt daran, dass zu diesem Zeitpunkt noch nichts an strOut[0] zugewiesen ist. Das passiert erst, wenn das erste change-Element ausgelöst wird. D.h. du musst beim Seitenstart - außerhalb des Eventhandlers - den Sliderwert einmalig ins Ausgabeelement übertragen.

    Weitere Hinweise:

    • Das lable Element gibt's nicht. Ein label ist ein label, egal ob amerikanisches oder britisches Englisch
    • document.querySelector("input[type='range'][id='fpIn']") - kann man machen, ist aber unnötig kompliziert. Eine ID muss im HTML Dokument eindeutig sein, und deshalb kannst Du hier auch document.querySelector("#fpIn") oder document.getElementById("fpIn") verwenden. Achte auf die Verwendung des # Zeichens. In querySelector muss es hinein, weil dort ein CSS Selektor verwendet wird und darin werden IDs mit einem # markiert.
    • Nicht jedes Textelement ist ein Label. Manchmal ist es auch einfach ein <span> oder <div> - oder in deinem Fall ein <output> Element, wenn Du dorthin Werte ausgibst.
    • Ein Label beschriftet ein Eingabeelement. Damit das funktioniert, benötigt das Eingabeelement eine ID und diese ID muss im for-Attribut des Labels angegeben werden. Beispiel folgt.
    • Die Verwendung von strong oder b nur für den Zweck, Fettschrift zu erzeugen, ist falsch. Diese beiden Elemente tragen eine inhaltliche Aussage ("Semantik") und bei Dir liegt weder die Semantik für <b> (bring attention to element) noch für <strong> (strong importance) vor. Ja, ich weiß, <b> wurde früher als "bold" verstanden. Aber das ist aus der Zeit von HTML 3.2. Wenn es Dir nur um Fettschrift geht, ist CSS angesagt.
    • Die Eigenschaft innerHTML dient dazu, einem Containerelement eine neue, innere HTML Struktur zuzuweisen, also Text mit HTML Markup darin. Um einfache Textausgaben zu machen, ist es ungeeignet. Mit etwas Pech enthält der normale Text Zeichen, die wie HTML Markup aussehen, und es zerreißt einem das Layout. Für normale Textausgaben verwendet man heute die textContent Eigenschaft.
    • Die Seitenüberschrift sollte h1 sein, nicht h2. Wenn Dir die Schrift dafür zu groß ist, stelle mit CSS eine andere ein.
    • Die Angabe °C gehört nicht in ein Label. Es ist zwar eine Form von Beschriftung, aber keine, die eine Aussage über die Bedeutung des beschrifteten Elements macht. Ich überlege sogar, ob die Zahlenwertdarstellung des Sliders vor Assistenztechniken (die auf ein Label angewiesen sind) verborgen werden müsste (mit dem Attribut aria-hidden="true"), denn ein Screenreader dürfte den Wert des Sliders ohnehin ansagen. Und dafür ist dann die Beschriftung "Fußpunkt" auch nicht hinreichend, da muss mehr Erklärung hinzu. Da Du hier aber, wie es aussieht, eine Beispielseite baust, gibt es zu wenig Kontext, um die richtige Einbindung von Assistenztechniken diskutieren zu können. Das ist leider die Crux im Web - du baust deine Seiten nicht nur für einen sehenden User mit zwei funktionierenden Händen, der vor einem Desktop-Monitor sitzt. Eine zugängliche Seite berücksichtigt auch Menschen, die blind sind, und beachtet auch Touchscreen, Tastatur und Spracheingabehelfer als Eingabe. Die Browser nehmen Dir dafür schon viel ab. Aber ein paar Dinge gilt es trotzdem zu beachten. Das macht die Sache leider komplexer und die meisten Beispiele im Web ignorieren das.

    Als Minimalbeispiel würde ich das so bauen:

    <!DOCTYPE html>
    <html>
    <head>
    <style>
    label[for=fpIn] {
       font-weight:bold;
    }
    </style>
    </head>
    <body>
    
    <h1>Slider to JS-Var</h1>
    
    <div>
        <label for="fpIn">Fußpunkt:</label>
        <input type="range" id="fpIn" min="-20" max="0" value="-12">
        <output aria-hidden="true">0</output> °C
    </div>
    
    <script>
    const sliderFp = document.querySelector("#fpIn");
    const sliderOut = document.querySelector("#fpIn + output"); 
    
    sliderFp.addEventListener("input", function() {
       sliderOut.textContent = sliderFp.value;
    });
        
    sliderOut.textContent = sliderFp.value;
    </script>
    
    </body>
    </html>
    
    • Die Debug-Zeile mit >>> und Temperatur habe ich weggenommen. Wenn Du die haben willst, kannst Du das ">>>" auch im HTML notieren.
    • Das °C braucht kein Element, worin es steht
    • Den Fettsatz für Fußpunkt erfolgt über eine CSS Regel, im style-Element. Ich identifiziere das richtige Label über sein for-Attribut.
    • Für das Ausgabeelement für den Temperaturwert habe ich das output Element verwendet. Um es im DOM zu finden, verwende ich den Nachbar-Kombinator +. Der Selektor #fpIn + output findet ein output-Element, das direkt auf ein Element mit ID fpIn folgt.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf, vielen Dank! Da kann ein Anfänger richtig was lernen .... toll! Leider löst es mein zentrales Problem nicht. Ich habe deinen eleganten code mal genommen und ein paar Zeilen eingefügt, da müsste man das eigentlich gut sehen können: Innerhalb der Funktion hält strOut[0] den Sliderwert, außerhalb geht die Variable wieder auf "undefined". Ich brauche aber den Wert außerhalb um mit ihm einen String zu konstruieren, den ich an meinen kleinen Web-Server schicken will. Anmerkung: Die Anzeige in dieser HTML-Seite ist NUR zur Demo hier und nicht mein Ziel. Ich brauche den Wert des Sliders auch nur nachdem er bewegt wurde.

      <!DOCTYPE html>
      <html>
      <head>
      <style>
      label[for=fpIn] {
         font-weight:bold;
      }
      </style>
      </head>
      <body>
      
      <h1>Slider to JS-Var</h1>
      
      <div>
          <label for="fpIn">Fußpunkt:</label>
          <input type="range" id="fpIn" min="-20" max="0" value="-12">
          <output aria-hidden="true">0</output> °C
          <p id="test1"></p>
          <p id="test2"></p>
      </div>
      
      <script>
      const strOut=[];
      const sliderFp = document.querySelector("#fpIn");
      const sliderOut = document.querySelector("#fpIn + output"); 
      
      sliderFp.addEventListener("input", function() {
         sliderOut.textContent = sliderFp.value;
         strOut[0]=sliderFp.value;
         document.getElementById("test1").innerHTML=strOut[0];
      });
          
      sliderOut.textContent = sliderFp.value;
      document.getElementById("test2").innerHTML=strOut[0];
      </script>
      
      </body>
      </html>
      
      1. Hallo Alois,

        du verstehst scheinbar das Konzept der Events noch nicht.

        Das Script, so wie Du es jetzt hast, tut dies:

        Zeilen 1 - 3: Deklaration von drei "Konstanten" - also letztlich Variablen, deren Wert Du nachher nicht mehr ändern kannst. Dieses "const" bezieht sich aber nur auf den Wert der Variablen selbst. Die "Werte" in diesen Variablen sind Objekte (strOut ist ein Array und die slider...-Variablen sind Verweise auf DOM Elemente), und diese Objekte werden durch das "const" nicht vor Veränderungen geschützt.

        strOut ist jetzt ein leeres Array, d.h. strOut[0] ergibt den Wert undefined.

        Zeilen 5-9: Registrieren einer Funktion als EventListener für das input Event auf dem Slider. Hier passiert nichts außer dem Hinterlegen dieser Funktion als EventListener. Sie wird noch nicht ausgeführt.

        Zeile 11: Auslesen von sliderFp.value, Speichern im textContent von sliderOut

        strOut ist immer noch ein leeres Array

        Zeile 12: Speichern von strOut[0] im innerHTML von #test2.

        Was schrieb ich vorhin? innerHTML ist hier falsch. Es muss textContent sein. Du speicherst Text. Kein HTML.

        NUN endet dein Script erstmal, und JETZT erst fängt der Browser an, die Seite darzustellen.

        Sobald Du mit der Maus am Slider herumspielst, fängt er an, Events zu feuern. Jetzt wird die hinterlegte Funktion aufgerufen und speichert einen Wert in strOut[0]. Das hat aber für die Zeile 12 keine Bedeutung. Diese wird nicht mehr ausgeführt.

        Ich brauche aber den Wert außerhalb um mit ihm einen String zu konstruieren, den ich an meinen kleinen Web-Server schicken will.

        Die entscheidende Frage ist nun: Was soll diesen Abschickvorgang auslösen? Durch einen Timer? Einen Button? Soll der Wert automatisch nach dem Verändern des Sliders hochgeschickt werden?

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Ich will die Werte mehrerer Slider in einem String zusammen fassen und an den Server schicken. Es geht um das "Zusammenfassen". Es soll eine Strings entstehen, den ich dann an den Server schicken will. Dieses geht aber nur, wenn ich die Werte verfügbar habe. Das Abschicken an den Server ist implementiert und funktioniert prima.

          1. Das Hochschicken wird mit einem manuel ausgelösten Button "Submit" getriggert. Wie gesagt, wenn ich den String haben funktioniert das bereits.

    2. Hallo Rolf, das ist ja ganz vertrackt: Innerhalb eines "addEventListener" behält eine Vaiable ihren zugewiesenen Wert, außerhalb ist er weg. Das gilt auch für globale Variable. In deinem, von mir modifizierten Code, sieht man das ganz deutlich. Irgend eine Idee, den Slider-Wert da rauß zu bekommen?

      <head>
      <meta charset="UTF-8">
      <style>
      label[for=fpIn] {
         font-weight:bold;
      }
      </style>
      </head>
      <body>
      
      <h1>Slider to JS-Var</h1>
      
      <div>
          <label for="fpIn">Fußpunkt:</label>
          <input type="range" id="fpIn" min="-20" max="0" value="-12">
          <output aria-hidden="true">0</output> °C      
      </div>
      	<br><br>
         	<p id="test1">test1:</p>
          <p id="test2">test2:</p>
          <p id="test3">test3:</p>
      
      <script>
      const strOut=[];
      window.addEventListener("change", function(){
      	const sliderFp = document.querySelector("#fpIn");
      	const sliderOut = document.querySelector("#fpIn + output"); 
      
      	sliderFp.addEventListener("input", function() {
         		sliderOut.textContent = sliderFp.value;
         		strOut[0]=sliderFp.value;
        		document.getElementById("test1").innerHTML=strOut[0];});
             
      	sliderOut.textContent = sliderFp.value;
      	document.getElementById("test2").innerHTML=strOut[0]; 
          strOut[1]="yxz"
      });
      document.getElementById("test3").innerHTML=strOut[1];
      
      </script>
      </body>
      </html>
      
      1. Hallo,

        könntest du bitte Code-Snippets auch als Code markieren, damit es besser lesbar ist? Also markieren, dann den Button </> über dem Eingabefeld drücken, und im besten Fall nach dem einleitenden ~~~ noch die Sprache notieren (z.B. html)?

        Ich habe das mal für dich nachgeholt.

        Einen schönen Tag noch
         Martin

        --
        Kaffee ist nur schädlich, wenn Ihnen ein ganzer Sack aus dem 5. Stock auf den Kopf fällt.
      2. Hallo Alois,

        wir erreichen nun das Thema "Scope von Variablen". Der Scope ist der Code-Bereich, wo eine Variable sichtbar ist.

        Du hast strOut auf globaler Ebene definiert. Damit ist sie überall sichtbar und Werte, die Du im change- oder input-Handler des Sliders zuweist, sind auch in einem submit-Eventhandler sichtbar.

        Aber: Was dein Beispiel zeigt, ist das, was ich schon vorher einmal erklärt habe: Die Eventhandler-Funktion wird von addEventListener lediglich registriert, aber nicht ausgeführt. Dein Code läuft nicht linear von oben nach unten. Die EventListener-Funktion wird erst später aufgerufen, wenn sich der Wert des Sliders ändert. Und dann läuft nur die Funktion und sonst nichts. Deine Ausgabe von strOut[1] am Ende findet statt, bevor die Funktion das erste Mal läuft.

        Und Du machst andere Dinge falsch.

        Schau erstmal hier: Grundlagen der Ereignisverarbeitung, vor allem das Thema "Bubbling".

        1. Du registrierst einen change-Handler auf dem window Objekt. Das hast Du bisher nicht getan, warum jetzt? Das change-Event gelangt natürlich irgendwann per Bubbling dorthin, aber es ist nicht so einfach, ein change- oder input-Event zu verarbeiten, das hochgebubbelt ist. Gerade dann nicht, wenn Du, wie hier, den Wert eines Sliders als Klartext anzeigen willst und Du mehrere Slider hast. Jeder Slider kann change auslösen, und diese change Events würden alle im gleichen Eventhandler landen. Es ist möglich, so vorzugehen, aber nicht so einfach.

        2. Du registrierst innerhalb dieses change-Handlers einen input-Handler. Das ist ganz falsch, weil Du dann bei jedem change-Event einen neuen input-Handler hinzufügst.

        3. Wenn Du die Slider zusammen mit dem Submit-Button in ein <form> Element packst, dann kannst Du über einen submit-Handler den Submit auf einen XMLHttpRequest umlenken (oder machst es mit jQuery Ajax oder dem fetch-API) und den Form-Inhalt über ein FormData Objekt an den Server posten. Es ist dann überhaupt nicht nötig, sich die Slider-Werte im change- oder input-Event zu merken. Guck Dir hier mal Beispiel 2 an, um zu sehen, wie man aus dem Form-Element das FormData Objekt erzeugt und via XMLHttpRequest verschickt.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf, zu 1. und 2. : Ich hatte das einfach aus einem Beispiel übernommen. War eher ein verzweifelter Versuch. Zu 3. : Das schau ich mir jetzt mal an.

          Auf alle Fälle erstmal vielen Dank für deine Hinweise (...und deine Geduld)!

          Grüße Alois

  2. Moin Alois,

    <!DOCTYPE html>
    <html>
    

    Das Gradzeichen ° ist außerhalb des ASCII-Zeichensatzes, weshalb es sinnvoll sein kann, im head die Codierung deines Dokuments anzugeben. Default ist in HTML ISO-8859-1.

    <body>
    
    <h2>Slider to JS-Var</h2>
    
    <label id=Xout></label><label id=Xout2></label>
    

    Die Hauptüberschrift eines Dokuments sollte immer h1 sein und label ohne zugeordnete Formularelemente wirken auf mich falsch (siehe auch https://wiki.selfhtml.org/wiki/HTML/Elemente/label).

    <br><br><br>
    

    Zeilenumbrüche sind keine Spacing-Elemente.

        <strong>
    

    Das strong hat kein End-Tag und umschließt auch das Input.

        <lable>Fußpunkt:</lable>
        <lable><input type="range" id="fpIn" min="-20" max="0"  value="-12"><span>0</span></lable>
      	<label> °C</lable>
    

    Das Element heißt label und nicht lable und beschreibt das Formularelement – so wie Rolf vorgeschlagen hat – oder in der Form

    <label>Fußpunkt:
        <input type="range" id="fpIn" min="-20" max="0" value="-12">
        <output aria-hidden="true"></output>&#x202f;°C
    </label>
    

    Das Zeichen mit dem Unicode-Point 202f ist ein schmales geschütztes Leerzeichen, wie es zwischen Zahlen und Einheiten verwendet wird.

    const  strOut=[];
    
      var sliderFp = document.querySelector("input[type='range'][id='fpIn']");
    

    Das Objekt kann doch auch const sein – und es wird ziemlich kompliziert ermittelt: "#fpIn" sollte vollkommen ausreichen, da die ID eindeutig sein muss, es darf also keine weiteren Elemente mit dieser ID geben.

      sliderFp.addEventListener("change", function(){
        document.querySelector("span").innerHTML = this.value;
        strOut[0]=this.value;
        document.getElementById("test1").innerHTML=strOut[0];
        });
    

    Neben dem change-Event träfe auch das input-Event zu. Aber das eigentliche Problem an der Stelle ist, dass es kein Element mit der ID test1 gibt. (Und getElementById kannst du natürlich auch anstelle von querySelector("#…") verwenden.)

      document.getElementById("Xout").innerHTML=">>> ";
    
      document.getElementById("Xout2").innerHTML=strOut[0];
    

    Das steht außerhalb des Event-Handlers, wird also nur einmal beim Abarbeiten des JavaScript-Codes ausgeführt. Und nach meinem Verständnis sollte man innerHTML gültigen HTML-Code zuweisen, dafür müsstest du doch die spitzen Klammern kontextgerecht behandeln. Aber in deinem Fall reichte auch die textContent-Eigenschaft vollkommen aus.

    Viele Grüße
    Robert

    1. Hallo Robert,

      Das Gradzeichen ° ist außerhalb des ASCII-Zeichensatzes, weshalb es sinnvoll sein kann, im head die Codierung deines Dokuments anzugeben. Default ist in HTML ISO-8859-1.

      da wäre das Grad-Zeichen enthalten. Trotzdem ist es eine gute Idee, die verwendete Codierung explizit anzugeben. Bevorzugt im HTTP-Header, aber eine Angabe per meta-Element im Dokument ist als Fallback auch kein Fehler.

      Das Zeichen mit dem Unicode-Point 202f ist ein schmales geschütztes Leerzeichen, wie es zwischen Zahlen und Einheiten verwendet wird.

      Außer bei Einheiten, die nicht mit Buchstaben notiert werden wie Grad oder Prozent - die werden ohne Abstand direkt an den Zahlenwert gehängt.
      Zumindest habe ich das so gelernt.

      Einen schönen Tag noch
       Martin

      --
      Kaffee ist nur schädlich, wenn Ihnen ein ganzer Sack aus dem 5. Stock auf den Kopf fällt.
      1. Trotzdem ist es eine gute Idee, die verwendete Codierung explizit anzugeben. Bevorzugt im HTTP-Header, aber eine Angabe per meta-Element im Dokument ist als Fallback auch kein Fehler.

        Mehr noch als "kein Fehler". Es ist eine gute Idee, die Codierung im Header und im Markup anzugeben. Die Angabe im Markup wirkt auf den ersten Blick vielleicht obsolet, wenn sie doch schon im Header vorgenommen hat.

        Was aber, wenn es gar keinen Header gibt, weil ich beispielsweise eine Seite lokal gespeichert habe? Dann ist die Meta-Angabe sehr nützlich.

    2. Hallo Robert B.,

      weshalb es sinnvoll sein kann, im head die Codierung deines Dokuments anzugeben. Default ist in HTML ISO-8859-1.

      Und das ist total bekloppt, denn: https://html.spec.whatwg.org/#charset

      Danach kennt HTML 5 nur noch UTF-8, dieses Encoding ist für HTML-5 Dokumente verpflichtend.

      Mein Chrome hier führt beim Laden von file:/// ein character sniffing durch und unterscheidet automatisch zwischen windows-1252 (was nicht ganz iso-8859-1 ist) und utf-8. Ich habe gerade keinen Webserver unter den Fingern, keine Ahnung ob er sich da anders verhält.

      Also grundsätzlich: Ja, man sollte <meta charset="utf-8"> hinschreiben und seine HTML Dateien auch so speichern. Das spart eine Menge &Auml;rger.

      Rolf

      --
      sumpsi - posui - obstruxi
  3. Lieber Alois,

    Mein Problem: Ich versuche einen Slider-Wert in ein JS-Variable (strOut[0]) zu bringen, um den Wert dort weiter zu verarbeiten ...

    nein, das ist nicht Dein Problem. Diese Deine Worte sind die sprichwörtliche „Story vom Pferd“, denn was Du wirklich zu lösen versuchst, ist in der Tat etwas ganz anderes:

    Ich will die Werte mehrerer Slider in einem String zusammen fassen und an den Server schicken.

    Also! Dann lass uns doch genau darüber reden, und nicht über Deine konfuse Beschreibung eines Detailproblems, das auf einer falschen Grundannahme beruht.

    Du willst einen Button manuell auslösen, um den aktuellen Zustand von allen Deinen Slidern in Form eines Strings zusammenzufassen. Dazu registrierst Du einen EventListener auf genau diesem Button:

    <p><button id="update-server">Sliderzustände an Server melden</button></p>
    <script>...</script>
    

    In das Script kommt nun folgendes:

    document
    .getElementById("update-server")
    .addEventListener(
      "click",
      event => {
        let myString = [];
        // alle Slider finden
        document
        .querySelectorAll('input[type="range"]')
        .forEach(slider => {
          // String mit Slider-Wert erweitern
          myString.push(slider.name + "=" + slider.value);
        });
        // POSTe alle Teilstrings in `myString` an den Server
      }
    );
    

    Die EventListener für Änderungen an den Slidern haben mit Deinem an-den-Server-senden nichts zu tun. Sie zeigen nur den jeweiligen Slider-Wert in einem passenden HTML-Element an, damit man das genauer sehen kann. Konzentrieren wir uns also besser überhaupt nicht darauf, sondern nur auf das Auslesen von allen Slidern auf einen Knopfdruck hin.

    Zum besseren Verständnis des Codes:

    • variable => { ... } ist eine modernere Schreibweise für function (variable) { ... } (siehe auch Pfeilfunktion)
    • Um ein HTML-Element anhand einer bestimmten Eigenschaft (wie z.B. einem Attributwert wie hier das type-Attribut) auszuwählen, verwendet man die document-Methode querySelector. Will man alle Elemente mit dieser Eigenschaft erhalten, verwendet man document.querySelectorAll. Als Parameter übergibt man einen Selektor, wie er auch bei CSS verwendet wird.
    • Die forEach-Methode ist nicht nur bei Array-Objekten verfügbar, sondern auch bei dem Objekt, welches von document.querySelectorAll zurückgegeben wird. Damit kann man die gefundenen HTML-Elemente der Reihe nach ansprechen. Dazu will forEach aber eine Funktion als Parameter haben, in der geregelt wird, wie mit dem jeweiligen Element (hier in der Variable slider übergeben) verfahren werden soll.

    Liebe Grüße

    Felix Riesterer

    1. Hallo Felix,

      neue JS Features wie Pfeilfunktionen sollten wir außen vor lassen, es ist für Alois schon genug an neuem Wissen, denke ich.

      Ist es nicht einfacher, die Slider - und was sonst zum Versand ansteht - in ein Form zu stecken und aus dem Form ein FormData zu initialisieren? Form und PHP sollten so gebaut sein, dass ein POST des Forms ebenfalls funktioniert, als Fallback falls das JS nicht ausgeführt wird. Mit JS verwendet man einen submit Listener.

      Gerade entdeckt: Ein FormData Objekt kann verwendet werden, um ein URLSearchParams-Objekt zu initialisieren. Auf dem ruft man toString() auf und verwendet es als Body für den POST Request.

      <form name="temperaturen" method="POST" action="setTemp.php">
      <label for...>...</label><input type="range" name="temp1" min... max...>
      <label for...>...</label><input type="range" name="temp2" min... max...>
      <label for...>...</label><input type="range" name="temp3" min... max...>
      <button name="save" value="form">Senden</button>
      </form>
      

      Die Labels sind nur angedeutet und müssen natürlich ausformuliert werden.

      document.forms.temperaturen.addEventListener("submit", function(submitEvent) {
         const params = new URLSearchParams(new FormData(submitEvent.target));
         params.set("save", "ajax");
      
         // hier Ajax-Code, versende damit params.toString() an setTemp.php
         // zum Beispiel:
         fetch("setTemp.php", { 
            method: "POST", 
            body: params.toString(), 
            headers: { "Content-Type", "application/x-www-form-urlencoded" });
      
         // Submit durch den Browser verhindern
         submitEvent.preventDefault();
      });   
      

      submitEvent.target ist das Form-Element, daraus wird ein FormData und daraus ein URLSearchParams Objekt. Der save-Button ist darin nicht enthalten, das passiert nur beim Browser-Versand. Deshalb wird der save-Eintrag gefaked, mit einem vom Form abweichenden Value, so dass man ggf. am Server zwischen einem POST durch den Browser und einen POST durch den EventListener unterscheiden kann.

      Dann folgt Standard-AJAX-Code, XMLHttpRequest oder fetch, zum Hochschicken. Darin muss man den Content-Type Header auf application/x-www-form-urlencoded setzen.

      Statt den Header zu setzen kann man auch auf die Einkapselung in URLSearchParams verzichten und direkt das FormData Objekt schicken, das geschieht dann aber automatisch als multipart/form-data. Das ist PHP egal, sind aber mehr Bytes auf der Leitung.

      Fertig. Ein Merken der Sliderwerte im change-Handler ist in keinem Fall nötig.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Lieber Rolf,

        neue JS Features wie Pfeilfunktionen sollten wir außen vor lassen, es ist für Alois schon genug an neuem Wissen, denke ich.

        wenn Du meinst, dass die Schreibweise ein Problem sein sollte... das kann nur Alois beantworten.

        Ist es nicht einfacher, die Slider - und was sonst zum Versand ansteht - in ein Form zu stecken und aus dem Form ein FormData zu initialisieren?

        Für Alois war genau dieses Problem laut seinen Worten schon gelöst. Er bastelt einen wie auch immer gearteten String und schickt ihn irgendwie an den Server. Ob das aber besser mit FormData und Ajax passieren sollte, und ob das Formular unbedingt auch über einen regulären POST-Request an den Server übertragen werden können muss - weil das als Default/Fallback das technische Minimum bedeutet und JS lediglich als progressive enhancement fungieren darf - erscheint mir das Alois' Formulierungen nach entweder für ihn fremd, oder für seinen (uns unbekannten) Anwendungsfall bewusst gewählt.

        Liebe Grüße

        Felix Riesterer

        1. Hallo Felix Riesterer,

          wenn Du meinst, dass die Schreibweise ein Problem sein sollte

          Pfeilfunktionen unterscheiden sich auch bezüglich this. Für jemanden, der offenbar noch mit Eventverarbeitung und Scoping kämpft, ein Zukunftsthema. Find ich.

          Mein Plädoyer für ein Form und Formdata bezieht sich hauptsächlich auf das Einsparen von selbst zu schreibendem (und zu testendem) Code und eine sinnvolle Seitengestaltung.

          • Man muss die Datenelemente nicht zusammensuchen, sondern gliedert sie passend im HTML. Was auch der Semantik der Seite hilft.
          • Das Versenden per Submit-Button in einem Form ist ebenfalls logischer als ein generischer Button
          • Man muss keinen eigenen String zusammenbauen - möglicherweise auch noch inkompatibel zu Standards wie x-www-form-urlencoded
          • Man muss keine Sonderzeichen escapen, es ist alles fertig und als Ein- bis Dreizeiler erledigt.

          Rolf

          --
          sumpsi - posui - obstruxi
        2. Hallo Felix ... genauso ist das! Ich bin ziemlicher Leie was HTML/JS/PHP .... betrifft und will auch kein Könner werden. Was ich mache: Ich baue mir mit einem ESP32 eine Heizungssteuerung (Wärmepumpe) und das wird auch funktionieren. Statt einer Eingabe über Druckknöpfe und eine LCD-Ausgabe will das gerne über mein Handy machen und das läuft auch fast schon alles (wahrscheinlich ziemlich unelegant) bis auf die Geschichte mit den Slidern. Leider ist es immer noch so, dass ich die Werte nicht verfügbar bekommen. Für einen 3/4/5-Zeiler, der das macht wäre ich schon sehr dankbar!

          Beste Grüße Alois

          1. Lieber Alois,

            Was ich mache: Ich baue mir mit einem ESP32 eine Heizungssteuerung (Wärmepumpe) und das wird auch funktionieren. Statt einer Eingabe über Druckknöpfe und eine LCD-Ausgabe will das gerne über mein Handy machen

            jetzt haben wir endlich die ganze Geschichte!

            und das läuft auch fast schon alles

            Anscheinend nicht.

            Leider ist es immer noch so, dass ich die Werte nicht verfügbar bekommen.

            Eben.

            Also hat Rolf schon Recht, wenn er vorschlägt, dass Du Dein Formular mit den Slidern ganz regulär abschickst, damit ihre Werte auf die übliche Weise beim Server ankommen. Damit ersparst Du Dir den Hickhack mit JavaScript.

            Zeig mal, wie Dein Dokument wirklich aussieht (egal, ob auf Codepen, Dabblet oder JSFiddle, nur nicht hier in einer Code-Wurst). Dann kann man Dir auch helfen. Wenn Du es mit PHP zusammenbaust, dann zeige bitte nicht den PHP-Quelltext, sondern das Ergebnis in HTML. Wenn Du kein PHP verwendest, dann erzähle uns bitte auch, wie das HTML erzeugt wird.

            Liebe Grüße

            Felix Riesterer

            1. Hallo Felix Riesterer,

              ganz regulär abschickst

              Als Fallback. Ein submit-Handler als progressive enhancement ist ja nicht verboten.

              Aber ein Form ist auf jeden Fall eine hilfreiche Maßnahme.

              Und selbst wenn Alois das nicht will - das Speichern in JavaScript-Variablen ist das falsche Konzept. Man lese beim Senden einfach die Slider neu aus.

              Die bisher von Alois beschriebenen Probleme zeigen jedenfalls ein solides Unverständnis von Eventhandling, denn die Werte landen ja in den Variablen. Er gibt sie nur zu früh aus und wundert sich dann.

              Rolf

              --
              sumpsi - posui - obstruxi
  4. So klappt das jetzt! Zur Demo sende ich Slider-Werte nicht an den Server sondern gebe sie auf der HTML-Seite aus. Den "Durchbruch" hat das Script-Teil //Slider to String gebracht!

    Vielen Dank nochmal an alle Helfer und vorallem die GEDULD die ihr aufgebracht habt!

    Grüße Alois

    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <style>
    label {font-weight:bold;}
    output {font-weight:bold;text-align: center;}
    input {text-align: center;}
     .slidecontainer {
        width: 300px;
        margin-left: auto !important;
        margin-right: auto !important;}
    </style>
    </head>
    <body>
    
    <div class=slidecontainer>  
        <p><strong>Fußpunkt:<strong></p>
        <input type="range" id="fpIn" min="-20" max="0" value="-15">
        <output></output> °C  <hr><br>
        
        <p><strong>Steigung:<strong></p>
        <input type="range" id="stIn" min="-.8" max="-.2" value="-0.5" step="0.01">
        <output aria-hidden="true"></output> R/A<hr><br>
        
        <p><strong>von Wind:<strong></p>
        <input type="range" id="wiIn" min="0" max="6" value="2" step="0.5">
        <output aria-hidden="true"></output> %<hr><br>
        
        <p><strong>von Licht:<strong></p>
        <input type="range" id="liIn" min="0" max="6" value="3" step="0.5">
        <output aria-hidden="true"></output> %<hr><br>
        
        <button id="update-server">Werte senden</button>
        <output id=Out></output>
    </div>
    <script>
    // Slider ---------------------------------------------------------
    const sliderFp = document.querySelector("#fpIn");
    const sliderFpOut = document.querySelector("#fpIn + output"); 
    sliderFp.addEventListener("input", function() {
       sliderFpOut.textContent = sliderFp.value; });       
    sliderFpOut.textContent = sliderFp.value;
    
    const sliderSt = document.querySelector("#stIn");
    const sliderStOut = document.querySelector("#stIn + output"); 
    sliderSt.addEventListener("input", function() {
       	sliderStOut.textContent = sliderSt.value; });       
    sliderStOut.textContent = sliderSt.value; 
    
    const sliderWi = document.querySelector("#wiIn");
    const sliderWiOut = document.querySelector("#wiIn + output"); 
    sliderWi.addEventListener("input", function() {
       	sliderWiOut.textContent = sliderWi.value; });       
    sliderWiOut.textContent = sliderWi.value; 
    
    const sliderLi = document.querySelector("#liIn");
    const sliderLiOut = document.querySelector("#liIn + output"); 
    sliderLi.addEventListener("input", function() {
       	sliderLiOut.textContent = sliderLi.value; });       
    sliderLiOut.textContent = sliderLi.value; 
    
    //Slider to String ---------------------------------------------
    document
    .getElementById("update-server")
    .addEventListener(
      "click",
      event => {
        let myString = [];
        // alle Slider finden
        document
        .querySelectorAll('input[type="range"]')
        .forEach(slider => {
          // String mit Slider-Wert erweitern
          myString.push(slider.name + "&" + slider.value);   });
        // POSTe alle Teilstrings in `myString` an den Server
        strOut = myString+"#";
        document.getElementById("Out").innerHTML = strOut; });
      
    </script>
    </body>
    </html> 
    
    1. Hallo Alois,

         .forEach(slider => {
            // String mit Slider-Wert erweitern
            myString.push(slider.name + "&" + slider.value);   });
         strOut = myString+"#";
      

      kann man machen. Aber das ist alles andere als Standard.

      • myString nennt sich String, ist aber ein Array. Diese Variable sollte "myParams" oder so heißen, um keine falsche Suggestion zu tragen
      • Ein Array und einen String mit "+" zu verketten geht, weil JavaScript da gnädiger ist als der liebe Gott. Es konvertiert das Array dadurch zu einem String, und tut das so, dass es die Array-Einträge kommagetrennt aneinanderpappt.
      • Du verwendest das Komma zum Trennen der Parameter und das & als Trennzeichen für Name und Wert - bei einer Übergabe an den Server ist das & aber eigentlich als Trennzeichen für die Parameter gängig und ein = als Trenner zwischen Name und Wert.

      Dein 12 Monate älteres Ich, das diesen Code liest und seitdem mehr gelernt hat, wird Dich dafür HASSEN.

      Vor allem, weil Du auf dem Server nun die Parameter in Eigenregie wieder auseinandernehmen musst.

      Vorschlag:

         const url = new URL("http://mein-esp32/set_temp.php");
         document
          .querySelectorAll('input[type="range"]')
          .forEach(slider => {
            // URL mit Slider-Wert erweitern
            url.searchParams.append(slider.name, slider.value);
          });
         document.getElementById("Out").textContent = url.toString(); });
      

      Damit bekommst Du eine fertige URL, wo die Parameter als URL Parameter drinstehen. "mein-esp32" musst Du natürlich geeignet ändern.

      Damit kannst Du einen POST-Request an deinen ESP32 machen (ja, POST, weil Du was änderst - ein GET Request ist hier falsch). Dass die Parameter in der URL statt im Body übergeben werden, ist erlaubt.

      Also so:

      const xhr = new XMLHttpRequest();
      xhr.open("POST", url.toString());
      xhr.send();
      

      Oder als Einzeiler so:

      fetch(url.toString(), { method: 'POST') });
      

      Das .toString() kannst Du vermutlich sogar weglassen, ich kann es gerade nur nicht ausprobieren.

      Im PHP findest Du dann in $_SERVER['REQUEST_METHOD'] den Wert 'POST' vor, um den Requesttyp zu erkennen, und im $_GET Array die übergebenen Daten. Wenn Du denn PHP auf dem ESR verwendest und nicht irgendwas anderes.

      Rolf

      --
      sumpsi - posui - obstruxi
    2. Moin Alois,

      wenn du Quellcode im Forum passend auszeichnest, hilft das ungemein beim Verstehen … Und ein paar Dinge sind weiterhin, obwohl du darauf hingewiesen worden bist.

      label {font-weight:bold;}
      

      Dein folgender HTML-Code enthält gar keine label.

      input {text-align: center;}
       .slidecontainer {
          width: 300px;
          margin-left: auto !important;
          margin-right: auto !important;}
      

      Eine fixe Breite in Pixel kann beim Vergrößern/Verkleinern (Zoomen) der Seite problematisch angezeigt werden, nimm lieber relative Dimensionen.

      <div class=slidecontainer>
      

      … oder auch form oder fieldset.

      <p><strong>Fußpunkt:<strong></p>
      <input type="range" id="fpIn" min="-20" max="0" value="-15">
      <output></output> °C  <hr><br>
      

      Die label hätte ich jetzt hier erwartet. Was die hr-Trenner und vor allem der Zeilenumbruch vor dem nächsten Absatz sollen, ist mir unklar. Das br brauchst du gar nicht, das hr erscheint auch deplatziert.

      Das betrifft natürlich die Konstrukte aller deiner Inputs.

      <button id="update-server">Werte senden</button>
      

      Ein button ohne type ist ein Submit-Button. Daran musst du bei der Verwendung von forms denken.

      Viele Grüße
      Robert