LastBoyScout: onclick vs. onchange

Hallo zusammen,

Ich hab mal wieder ein kleines Problem mit JavaScript... möchte gerne in einem Formular mit zwei Auswahlfeldern die Anzahl der Optionen des zweiten Feld von der Auswahl im ersten abhängig machen.

Meine erste Lösung dazu ist, die Funktion mittels onclick() innerhalb der Optionen des erste Formularfeldes aufrufen zu lassen. Dies klappt eigentlich auch recht gut... leider aber eben nur bei Bedienung mit der Maus und nicht mit der Tastatur:

<html>
<head>
  <script type="text/javascript">
    function Neuefelder(Anzahl) {
      document.Test.Felder.length = null;
      for (var i = 0; i < Anzahl; i++) document.Test.Felder.options[document.Test.Felder.length] = new Option(i);
    }
  </script>
</head>
<body>
<form name="Test">
  <select name="Auswahl">
    <option onclick="Neuefelder(2)">A</option>
    <option onclick="Neuefelder(3)">B</option>
    <option onclick="Neuefelder(4)">C</option>
  </select>
  <select name="Felder">
    <option>1</option>
  </select>
</form>
</body>
</html>

Ideal wäre onselect(), aber das hat ja leider eine völlig andere Bedeutung... und mit onchange() weiß ich einfach nicht, wie ich die Anzahl der benötigten Felder übergeben soll!?

Danke schon mal für eure Hilfe.

  1. Servus LastBoyScout

      <select name="Auswahl">
        <option onclick="Neuefelder(2)">A</option>
        <option onclick="Neuefelder(3)">B</option>
        <option onclick="Neuefelder(4)">C</option>
      </select>
    

    Ideal wäre onselect(), aber das hat ja leider eine völlig andere Bedeutung... und mit onchange() weiß ich einfach nicht, wie ich die Anzahl der benötigten Felder übergeben soll!?

    Hast du schon mal versucht, den Wert des Select-Feldes nach dem change-Event zu lesen und idealerweise den options ein value-Attribut zu geben?

    ciao

    --
    "Sir, we are surrounded!" - "Excellent! We can attack in any direction!"
    1. Hi henman,

      Hast du schon mal versucht, den Wert des Select-Feldes nach dem change-Event zu lesen und idealerweise den options ein value-Attribut zu geben?

      Ja, das Problem dabei ist jedoch, dass ich auch den Übergabewert des ersten Auswahlfeldes (A, B, C usw.) weiterverarbeiten muss und deshalb value nicht einfach mit der Anzahl an neuen Feldern belegen kann:

        <select name="Auswahl">
          <option onclick="Neuefelder(2)" value="A">A</option>
          <option onclick="Neuefelder(3)" value="B">B</option>
          <option onclick="Neuefelder(4)" value="C">C</option>
        </select>
      
      1. Servus LastBoyScout

        Ja, das Problem dabei ist jedoch, dass ich auch den Übergabewert des ersten Auswahlfeldes (A, B, C usw.) weiterverarbeiten muss und deshalb value nicht einfach mit der Anzahl an neuen Feldern belegen kann:

        Dann hol dir zusätzlich den angezeigten Text.

        • die Eigenschaft "selectedIndex" des Selects gibt dir den Index des zurzeit gewählten Option-Elementes
        • die Eigenschaft "text" der Option-Elemente gibt dir den angezeigten Text des Elementes. Wenn du die zurzeit gewählte Option ansprichst, bekommst du also den angezeigten Text und die Value.

        Anderer Ansatz:

        Entscheide im Change-Handler je nach Übergabewert, welche Anzahl genutzt werden soll.

        ciao

        --
        "Sir, we are surrounded!" - "Excellent! We can attack in any direction!"
        1. Hi henman,

          Dann hol dir zusätzlich den angezeigten Text.

          • die Eigenschaft "selectedIndex" des Selects gibt dir den Index des zurzeit gewählten Option-Elementes
          • die Eigenschaft "text" der Option-Elemente gibt dir den angezeigten Text des Elementes. Wenn du die zurzeit gewählte Option ansprichst, bekommst du also den angezeigten Text und die Value.

          Aber wie bzw. wo soll ich die Anzahl der benötigten Optionen übergeben? Ich kann diese ja weder im Valuewert noch im Optionstext hinterlegen!

          Anderer Ansatz:

          Entscheide im Change-Handler je nach Übergabewert, welche Anzahl genutzt werden soll.

          Auch hier das Problem, wie ich der Funktion übermitteln soll wie viele Felder sie bei der jeweiligen Auswahl erzeugen soll!?

          P.S. Warum onselect() nicht auch für <option> möglich gemacht wurde ist mir ein Rätsel...

          1. Servus LastBoyScout

            • die Eigenschaft "selectedIndex" des Selects gibt dir den Index des zurzeit gewählten Option-Elementes
            • die Eigenschaft "text" der Option-Elemente gibt dir den angezeigten Text des Elementes. Wenn du die zurzeit gewählte Option ansprichst, bekommst du also den angezeigten Text und die Value. Aber wie bzw. wo soll ich die Anzahl der benötigten Optionen übergeben? Ich kann diese ja weder im Valuewert noch im Optionstext hinterlegen!

            Du hast doch Zugriff auf das DOM-Element.

            <select onchange="onSelectChange()">
                <option value="1">A</option>
                <option value="2">B</option>
            </select>
            
            function onSelectChange(){
               // ich spare mir jetzt mal den Code, der die Elemente selektiert
               var _select; // enthält das Select-Element
               var _optionsList; // enthält ein Array mit allen Option-Elementen des Selects
               
               var _gewaehlterWert = _select.value; // wird 1 oder 2 enthalten
               var _gewaehlterText = _optionsList[_select.selectedIndex].text; // wird A oder B enthalten
               
            }
            

            Anderer Ansatz:

            Entscheide im Change-Handler je nach Übergabewert, welche Anzahl genutzt werden soll. Auch hier das Problem, wie ich der Funktion übermitteln soll wie viele Felder sie bei der jeweiligen Auswahl erzeugen soll!?

            Prüfe, welche Option des Selects gewählt ist. IN der Funktion könntest du dann einen switch einsetzen, der je nach gewählter Option eine Variable _anzahl mit entsprechender Anzahl belegt.

            P.S. Warum onselect() nicht auch für <option> möglich gemacht wurde ist mir ein Rätsel...

            onselect wird ausgelöst, wenn Text markiert wird. Das hat nichts mit der Auswahl in einem Select-Element zu tun.

            ciao

            --
            "Sir, we are surrounded!" - "Excellent! We can attack in any direction!"
            1. Lieber henman,

              function onSelectChange(){
                 // ich spare mir jetzt mal den Code, der die Elemente selektiert
                 var _select; // enthält das Select-Element
                 ...
              }
              

              das hätte mich jetzt aber schon brennend interessiert, wie Du die Variable _select befüllst, bzw. woher sie ihren Inhalt bekommen soll! Deine Funktion onSelectChange nimmt ja keinen Parameter entgegen!

              Liebe Grüße,

              Felix Riesterer.

              1. Servus Felix

                Lieber henman,

                function onSelectChange(){
                   // ich spare mir jetzt mal den Code, der die Elemente selektiert
                   var _select; // enthält das Select-Element
                   ...
                }
                

                das hätte mich jetzt aber schon brennend interessiert, wie Du die Variable _select befüllst, bzw. woher sie ihren Inhalt bekommen soll! Deine Funktion onSelectChange nimmt ja keinen Parameter entgegen!

                Das select-Element lässt sich mit document.getElementById u.a. selektieren. Aber du hast Recht, den Handler mit this als Parameter aufzurufen erleichtert das.

                Sprich:

                <select onchange="onSelectChange(this)">
                
                function onSelectChange(pSelectEl){ // ...
                

                Die Optionen können mit

                _select.getElementsByTagName('option')
                

                geholt werden

                ciao

                --
                "Sir, we are surrounded!" - "Excellent! We can attack in any direction!"
                1. Lieber henman,

                  Die Optionen können mit

                  _select.getElementsByTagName('option')
                  

                  geholt werden

                  wäre da _select.options nicht eleganter?

                  Liebe Grüße,

                  Felix Riesterer.

                  1. Servus Felix

                    wäre da _select.options nicht eleganter?

                    Ohne Frage, das war mir entfallen.

                    ... deswegen hab ich ja geschrieben, dass ich mir den Code für die Selektion der Elemente erspare ;P

                    ciao

                    --
                    "Sir, we are surrounded!" - "Excellent! We can attack in any direction!"
            2. Hi henman,

              ich glaube wir reden aneinander vorbei... wie mehrfach gesagt, sind die Valuewerte des ersten Auswahlfeld bereits belegt und nicht identisch mit der Anzahl der benötigten Felder! Die eigentliche Frage ist daher wo kann ich innerhalb der Optionen einen versteckten Wert definieren, den ich per onchange() im Elternelemt in der Funktion Auswerten kann? Folgender Pseudocode macht das Problem eventuell deutlicher:

                <select name="Auswahl" onchange="NeueFelder(Anzahl)">
                  <option Anzahl="21" value="A">Erste Option</option>
                  <option Anzahl="10" value="B">Zweite option</option>
                  <option Anzahl="7" value="C">Dritte Option</option>
                </select>
              

              P.S. Warum onselect() nicht auch für <option> möglich gemacht wurde ist mir ein Rätsel...

              onselect wird ausgelöst, wenn Text markiert wird. Das hat nichts mit der Auswahl in einem Select-Element zu tun.

              Ist schon klar... warum aber eigentlich nicht? hier werden die optionen doch auch markiert!

              1. Hallo LastBoyScout,

                Die eigentliche Frage ist daher wo kann ich innerhalb der Optionen einen versteckten Wert definieren, den ich per onchange() im Elternelemt in der Funktion Auswerten kann?

                Es gibt custom data attributes. Der Artikel ist noch sehr unvollständig.

                Bis demnächst
                Matthias

                --
                Signaturen sind bloed (Steel) und Markdown ist mächtig.
              2. Hallo LastBoyScout,

                Die eigentliche Frage ist daher wo kann ich innerhalb der Optionen einen versteckten Wert definieren, den ich per onchange() im Elternelemt in der Funktion Auswerten kann? Folgender Pseudocode macht das Problem eventuell deutlicher: […]

                Dafür sind die data-Attribute gedacht:

                <form>
                  <select name="Auswahl">
                    <option data-num="21" value="A">Erste Option</option>
                    <option data-num="10" value="B">Zweite option</option>
                    <option data-num="7" value="C">Dritte Option</option>
                  </select>
                </form>
                

                Im JS dann:

                document.getElementById('auswahl').addEventListener('change', function() {
                  var elem = document.getElementById('auswahl');
                  var opt = elem.options[elem.selectedIndex];
                  var num = opt.getAttribute('data-num');
                  console.log("num:", num);
                });
                

                LG,
                CK

              3. Hallo,

                hier ein Beispiel auf codePen

                gr qx

                1. Hi quincunx,

                  hier ein Beispiel auf codePen

                  Ganz schön viel Code, aber funktioniert super... Besten Dank!

                  Gibt es auch noch eine Möglichkeit beim laden der Seite den Wert der Startoption (Erste bzw. vorselektierte Option) zu verarbeiten?

                  Gruß LSB

                  1. Gibt es auch noch eine Möglichkeit beim laden der Seite den Wert der Startoption (Erste bzw. vorselektierte Option) zu verarbeiten?

                    ja, siehe codePen. Im Beispiel ist option mit value C selected.

  2. Hi!

    Du weisst, welches Feld gewaehlt wurde. Du hast auch noch das komplette Element mit allen Feldern vorliegen und kannst Werte pruefen, Elemente zaehlen... . Es ist also kein Problem rauszufinden, ob du (in deinem Beispiel) 2, 3 oder 4 neue Options brauchst.

    Gruss, Steel

    1. Hi Steel,

      Du weisst, welches Feld gewaehlt wurde. Du hast auch noch das komplette Element mit allen Feldern vorliegen und kannst Werte pruefen, Elemente zaehlen... . Es ist also kein Problem rauszufinden, ob du (in deinem Beispiel) 2, 3 oder 4 neue Options brauchst.

      Wie soll das gehen?

        <select name="Auswahl">
          <option onclick="Neuefelder(21)" value="A">Erste Option</option>
          <option onclick="Neuefelder(10)" value="B">Zweite option</option>
          <option onclick="Neuefelder(7)" value="C">Dritte Option</option>
        </select>
      

      vs.

        <select name="Auswahl" onselect="Neuefelder(?)">
          <option value="A">Erste Option</option>
          <option value="B">Zweite option</option>
          <option value="C">Dritte Option</option>
        </select>
      

      Wo könnte ich nun die Anzahl der Neuen Felder übergeben?

      1. Lieber LastBoyScout,

        <option onclick="Neuefelder(21)" value="A">Erste Option</option>
        

        besser fände ich onclick="Neuefelder(this)", weil dann der Funktion "Neuefelder" das <option>-Element übergeben wird (zumindest eine Referenz darauf), so dass Du viele schöne Sachen machen kannst:

        function Neuefelder(opt) {
            // opt ist eine Referenz auf ein <option>-Element
            var val = opt.value; // Wert der Option
            var sel = opt.parentNode; // <select>-Element
            var len = sel.options.length; // Anzahl <option>-Elemente
        
            ...
        }
        

        Liebe Grüße,

        Felix Riesterer.

        1. Hi Felix,

          <option onclick="Neuefelder(21)" value="A">Erste Option</option>
          

          besser fände ich onclick="Neuefelder(this)", weil dann der Funktion "Neuefelder" das <option>-Element übergeben wird (zumindest eine Referenz darauf), so dass Du viele schöne Sachen machen kannst:

          function Neuefelder(opt) {
              // opt ist eine Referenz auf ein <option>-Element
              var val = opt.value; // Wert der Option
              var sel = opt.parentNode; // <select>-Element
              var len = sel.options.length; // Anzahl <option>-Elemente
              ...
          }
          

          Und wo definiere ich dann den Wert... so das die Funktion auch weiß, dass das zweite Auswahlfeld nun genau 21 Felder benötigt?

          Im übrigen soll onclick() ja ersetzt werden, da es nicht auf Tastatureingaben reagiert.

      2. Hi!

        Ich hab jetzt die anderen Antworten nicht alle gelesen:

        
        <form>
            <select id="Auswahl" onChange="Neuefelder(this)">
                <option value="A">Erste Option</option>
                <option value="B">Zweite option</option>
                <option value="C">Dritte Option</option>
            </select>
        </form>
        
        
        
        function Neuefelder(myelem) {
            alert("Die Liste hat " + myelem.options.length + " Elemente.");
            alert("Es wurde der " + (myelem.options.selectedIndex + 1) + " Eintrag gewählt.");
            alert("Der Wert ist '" + myelem.options[myelem.options.selectedIndex].value + "' und angezeigt wird '" + myelem.options[myelem.options.selectedIndex].text +"'");
            myelem.options[myelem.options.selectedIndex].myfeldanzahl = 15;
            alert("Felder: " + myelem.options[myelem.options.selectedIndex].myfeldanzahl)
        }
        
        

        Den Rest solltes Du doch allein hinbekommen? (ne Initialisierungsfunktion z.b.)

        jsfiddle: http://jsfiddle.net/yykv6kks/2/

  3. Hallo,

    du willst vielleicht sowas wie das hier machen?

    HTML

    <body>
      <form name="Test">
        <select name="Auswahl">
          <option>A</option>
          <option>B</option>
          <option>C</option>
        </select>
        <select name="Felder">
        </select>
      </form>
    </body>
    

    JavaScript

    document.body.addEventListener('change', function (e) {
      var Auswahl = document.forms.Test.elements.Auswahl;
      if (e.target === Auswahl) {
        var i, option, index = Auswahl.selectedIndex + 2;
        var Felder = document.forms.Test.elements.Felder;
        if (Felder.options.length) {
          Felder.options.length = 0;
        }
        for (i = 0; i < index; i += 1) {
          option = document.createElement('option');
          Felder.appendChild(option);
          option.text = i;
        }
      }
    });
    

    Gruß,

    Orlok

    1. Servus Orlok

      document.body.addEventListener('change', function (e) {

      Warum soll der Body auf das change-Event hören?

      document.forms.Test.elements.Auswahl.addEventListener // etc.
      

      Dann braucht man die e.target-Abfrage auch nicht.

      ciao

      --
      "Sir, we are surrounded!" - "Excellent! We can attack in any direction!"
      1. Hallo

        document.body.addEventListener('change', function (e) {

        Warum soll der Body auf das change-Event hören?

        document.forms.Test.elements.Auswahl.addEventListener // etc.
        

        Dann braucht man die e.target-Abfrage auch nicht.

        Ja, ich hätte den Event Handler auch gleich für document.forms.Test.elements.Auswahl registrieren können, aber es könnte ja sein, dass der TO, dessen geposteter Code ja eher Beispielcharakter hatte, bei seinem Projekt diese Funktionalität für mehr als ein Paar Selectfelder beziehungsweise bei mehr als einem Formular umsetzen möchte, und so kann er dies alles in dieser einen Funktion organisieren.

        Gruß,

        Orlok

    2. Hi Orlok,

      du willst vielleicht sowas wie das hier machen?

      Nein, es sollen ja nicht bei jeder erneuten Auswahl weitere Optionsfelder hinzugefügt werden! Ziel ist zu jeder Option im ersten Auswahlfeld eine genau definierte Anzahl an Feldern im zweiten zu erzeugen... Daher ja auch der vorhergehende Reset:

      document.Test.Felder.length = null;
      

      ...und das eben nicht nur bei Auswahl mit der Maus durch onclick()

      1. Hallo

        Nein, es sollen ja nicht bei jeder erneuten Auswahl weitere Optionsfelder hinzugefügt werden!

        Ich hatte mein Codebeispiel in der Zwischenzeit aktualisiert, so dass dieser Einwand berücksichtigt wurde.

        Darüber hinaus ist mir jedoch aufgefallen, dass es sein kann, dass automatisch der erste Eintrag der Auswahl vorselektiert wird, weshalb das 'change'-Event bei erneutem Click auf diesen ersten Eintrag nicht ausgelöst wird.

        Die beste Lösung hierfür scheint mir zu sein, den ersten Eintrag per Default zu selektieren und die zu diesem Eintrag gehörenden Einträge im korrespondierenden select im HTML vorzuhalten.

        Ich habe mein Beispiel daher nochmals aktualisiert, um das zu berücksichtigen.

        Gruß,

        Orlok.

        1. Hi Orlok,

          Ich hatte mein Codebeispiel in der Zwischenzeit aktualisiert, so dass dieser Einwand berücksichtigt wurde.

          Die Anzahl der benötigten Optionsfelder muss aber variabel sein und sich nicht einfach nur erhöhen, z.B.:

            <select name="Auswahl">
              <option onclick="Neuefelder(21)" value="A">Erste Option</option>
              <option onclick="Neuefelder(10)" value="B">Zweite option</option>
              <option onclick="Neuefelder(7)" value="C">Dritte Option</option>
            </select>
          

          Darüber hinaus ist mir jedoch aufgefallen, dass es sein kann, dass automatisch der erste Eintrag der Auswahl vorselektiert wird, weshalb das 'change'-Event bei erneutem Click auf diesen ersten Eintrag nicht ausgelöst wird.

          Die beste Lösung hierfür scheint mir zu sein, den ersten Eintrag per Default zu selektieren und die zu diesem Eintrag gehörenden Einträge im korrespondierenden select im HTML vorzuhalten.

          Das ist in der Tat auch noch so ein Problem... ich dachte daran die Funktion per onload() mit dem vorselektierten Wert aufzurufen.

          1. Hallo

            Die Anzahl der benötigten Optionsfelder muss aber variabel sein und sich nicht einfach nur erhöhen […]

            Dazu wurden dir ja an anderer Stelle hier im Thread bereits Lösungen präsentiert.

            Wobei ich mich allerdings frage, was du eigentlich vorhast:

            Soll in deinem zweiten select wirklich nur eine aufsteigend sortierte Liste aus Ganzzahlen mit variabler Länge angeboten werden, sprich, ist die Anzahl der zu erzeugenden options tatsächlich die einzige Information, die du benötigst?

            Wie dem auch sei hast du hier zwei Möglichkeiten, nämlich entweder du hinterlegst die entsprechenden Werte in data-Attributen im HTML, und liest die Werte dann innerhalb der Handlerfunktion aus, nachdem du das option-Element identifiziert hast, welches der Benutzer ausgewählt hat...

            ...oder du hinterlegst die Werte für die Anzahl der zu erzeugenden options in einem Array, idealerweise so, dass document.forms.Test.elements.Auswahl.selectedIndex mit dem Indexschlüssel für den entsprechenden Eintrag im Array identisch ist:

            document.body.addEventListener('change', function (e) {
              var Auswahl = document.forms.Test.elements.Auswahl;
              if (e.target === Auswahl) {
                var Anzahl = [13, 25, 8, 17, 21];
                var Felder = document.forms.Test.elements.Felder;
                var i, option, index = Auswahl.selectedIndex;
                Felder.options.length = 0;
                for (i = 0; i < Anzahl[index]; i += 1) {
                  option = document.createElement('option');
                  Felder.appendChild(option);
                  option.text = i;
                }
              }
            });
            

            Die Frage, die sich stellt ist also zunächst einmal: Welche Werte brauchst du für die Erstellung der options für das zweite select? Und wenn du diese Frage beantwortet hast, ist die nächste Frage: Wo willst du diese Werte hinterlegen? Im HTML-Dokument oder im Script?

            Ersteres hätte den Vorteil, dass nachträgliche Änderungen leichter von der Hand gehen, da sie nur an einer Stelle vorgenommen werden müssen. Wenn wirklich nur die Anzahl der zu erzeugenden options gefragt ist, dürfte dies das Mittel der Wahl sein.

            Letzters allerdings dürfte umso attraktiver werden, je mehr Werte hinterlegt werden sollen, denn nehmen wir mal an, du wolltest nicht bloß eine Liste von 1 bis 17 erzeugen, sondern richtig beschriftete options generieren, dann würdest du, wenn du die ganzen Werte in einem oder gar mehreren data-Attributen hinterlegen wolltest, deinen HTML-Code ziemlich schnell vollkommen unleserlich machen.

            Das ist übrigens ein Effekt, den du auch dadurch erzielen kannst, indem du auf die Methode addEventListener( ) verzichtest und statt dessen JavaScript mit HTML vermischst, aber das nur nebenbei.

            Jedenfalls kannst du dir eine Menge Schreibarbeit sparen und deinen Code deutlich übersichtlicher gestalten, indem du die Events an einer zentralen Stelle abfängst und dann innerhalb der Handlerfunktion selektierst, statt dies bereits bei der Registrierung der Handler zu tun. Stichwort: Event Delegation.

            Das ist in der Tat auch noch so ein Problem... ich dachte daran die Funktion per onload() mit dem vorselektierten Wert aufzurufen.

            Wenn du die Einträge für das erste Element von Auswahl direkt nach dem Laden der Seite erzeugen willst, wäre wohl 'DOMContentLoaded' das richtige Event für dich:

            document.addEventListener('DOMContentLoaded', function ( ) {
                //...
            });
            

            Gruß,

            Orlok

  4. Lieber LastBoyScout,

    als Ergänzung hier noch den Link zu den verketteten Auswahllisten... Dass ich da nicht schon früher draufgekommen bin?!

    Liebe Grüße,

    Felix Riesterer.

    1. Hallo

      als Ergänzung hier noch den Link zu den verketteten Auswahllisten...

      Wirklich ein sehr schöner Artikel! Sowas ähnliches hatte ich auch im Sinn, als ich danach fragte, ob die Anzahl der zu erzeugenden options wirklich der einzige benötigte Parameter ist.

      In der ersten Version des verlinkten Postings hatte ich sogar eine objektorientierte Lösung angedacht, die ich dann allerdings wieder verworfen hatte, da es mir für den vom TO beschriebenen Zweck etwas überdimensioniert erschien und ich erst einmal abwarten wollte, ob er sein Vorhaben nicht vielleicht noch etwas konkreter beschreibt.

      Gruß,

      Orlok