Marco: Checkboxen sollen sich wechselseitig ausschließen

Hallo zusammen,

ich bin leider blutiger JS-Anfänger, brauche jedoch ein Javascript für folgendes Problem. Zum Hintergrund: bei dem HTML-Code handelt es sich um automatisch generierten Code für Online-Befragungen. Leider ist es nicht möglich, auf die Struktur Einfluss zu nehmen. Die einzige Möglichkeit ist eben, die einzelnen Elemente mit Javascript anzufassen und zu verändern. Dass die Struktur extrem kompliziert/verschachtelt und absolut nicht barrierefrei ist, ist offensichtlich, aber eben leider nicht zu ändern...

Mein Problem ist nun folgendes: ich habe zwei Zeilen à 5 Checkboxen. Es soll möglich sein, eine oder mehrere der ersten 4 Checkboxen pro Zeile anzuwählen. Beim Klick auf die fünfte Checkbox jedoch sollen alle eventuell markierten 4 Checkboxen dieser Zeile deselektiert werden. Umgekehrt soll ein Klick in eine der ersten 4 Checkboxen die eventuell markierte fünfte Checkbox deselektieren. Am Ende darf man also nicht Checkbox 1, 2, 3 oder 4 und Zelle 5 anwählen können, sondern nur eine oder mehrere Zellen aus den Checkboxen 1-4 oder ausschließlich Checkbox 5. Außerdem soll jede Zeile für sich betrachtet werden...

Wie gehe ich nun am besten vor? Ich müsste doch die ersten vier Checkboxen in ein Array packen und bei Klick auf Checkbox 5 müssten alle Elemente des Arrays deselektiert werden? Oder bedarf es da einer Schleife? Ich bin leider wirklich ratlos und hoffe auf den entscheidenden Hinweis...

  
    <tr>  
      <td class="tabstop-0"></td>  
      <td class="tabstop-1"><span class="fontnumber-3">1</span></td>  
      <td class="tabstop-2"><span class="fontnumber-3">2</span></td>  
      <td class="tabstop-3"><span class="fontnumber-3">3</span></td>  
      <td class="tabstop-4"><span class="fontnumber-3">4</span></td>  
      <td class="tabstop-5"><span class="fontnumber-3">wei&#223; nicht/keine Angabe</span></td>  
    </tr>  
    <tr>  
      <td><span class="fontnumber-0"> Online-Werbung</span></td>  
      <td class="tabstop-1 tabstop-nonzero formelementholder formelementholder-1 formelementholder-odd formelementplacement-1 formelementplacement-odd formelementplacement-first formelement-above-placement-first" colspan="5">  
        <table cellpadding="0" cellspacing="0" class="scale" width="100.0%">  
          <tbody>  
            <tr>  
              <td class="scaleholder">  
                <table width="100.0%" cellpadding="0" cellspacing="0" class="scaleitem">  
                  <tbody>  
                    <tr>  
                      <td class="scalecell scalecell-odd scalecell-min scalecell-less scalecell1 scale-normal">  
                        <input type="checkbox" name="answer0-1" value="1">  
                      </td>  
                      <td class="scalecell scalecell-even scalecell-less scalecell2 scale-normal">  
                        <input type="checkbox" name="answer0-2" value="2">  
                      </td>  
                      <td class="scalecell scalecell-odd scalecell-middle scalecell3 scale-normal">  
                        <input type="checkbox" name="answer0-3" value="3">  
                      </td>  
                      <td class="scalecell scalecell-even scalecell-more scalecell4 scale-normal">  
                        <input type="checkbox" name="answer0-4" value="4">  
                      </td>  
                      <td class="scalecell scalecell-odd scalecell-max scalecell-more scalecell5 scale-normal">  
                        <input type="checkbox" name="answer0-5" value="5">  
                      </td>  
                    </tr>  
                  </tbody>  
                </table>  
              </td>  
            </tr>  
          </tbody>  
        </table>  
      </td>  
    </tr>  
    <tr>  
      <td class="tabstop-0 formelementbefore formelementbefore-2 formelementbefore-even formelementbefore-placement-2 formelementbefore-placement-even"><span class="fontnumber-0"> Anzeigen in Tageszeitungen</span></td>  
      <td class="tabstop-1 tabstop-nonzero formelementholder formelementholder-2 formelementholder-even formelementplacement-2 formelementplacement-even" colspan="5">  
        <table cellpadding="0" cellspacing="0" class="scale" width="100.0%">  
          <tbody>  
            <tr>  
              <td class="scaleholder">  
                <table width="100.0%" cellpadding="0" cellspacing="0" class="scaleitem">  
                  <tbody>  
                    <tr>  
                      <td class="scalecell scalecell-odd scalecell-min scalecell-less scalecell1 scale-normal">  
                        <input type="checkbox" name="answer1-1" value="1">  
                      </td>  
                      <td class="scalecell scalecell-even scalecell-less scalecell2 scale-normal">  
                        <input type="checkbox" name="answer1-2" value="2">  
                      </td>  
                      <td class="scalecell scalecell-odd scalecell-middle scalecell3 scale-normal">  
                        <input type="checkbox" name="answer1-3" value="3">  
                      </td>  
                      <td class="scalecell scalecell-even scalecell-more scalecell4 scale-normal">  
                        <input type="checkbox" name="answer1-4" value="4">  
                      </td>  
                      <td class="scalecell scalecell-odd scalecell-max scalecell-more scalecell5 scale-normal">  
                        <input type="checkbox" name="answer1-5" value="5">  
                      </td>  
                    </tr>  
                  </tbody>  
                </table>  
              </td>  
            </tr>  
          </tbody>  
        </table>  
      </td>  
    </tr>  

Vielen Dank schonmal im Voraus!

  1. Moin Moin!

    Zum Hintergrund: bei dem HTML-Code handelt es sich um automatisch generierten Code für Online-Befragungen. Leider ist es nicht möglich, auf die Struktur Einfluss zu nehmen. Die einzige Möglichkeit ist eben, die einzelnen Elemente mit Javascript anzufassen und zu verändern. Dass die Struktur extrem kompliziert/verschachtelt und absolut nicht barrierefrei ist, ist offensichtlich, aber eben leider nicht zu ändern...

    Falsch: Wegschmeißen, besseres Tool benutzen.

    Egal was Du mit Javascript bastelst, es wird das Problem nicht vollständig lösen.

    Erstens ist Javascript nicht in jedem Browser vorhanden *und* für die Seite freigeschaltet. Damit ist das ganze Javascript-Gebastel im für Dich schlechten Fall wirkungslos.

    Zweietens hat die Serverseite keine Ahnung von Deinen veränderten Spielregeln und wird daher die Eingaben nach den originalen Spielregeln prüfen (wenn überhaupt). Damit bekommst Du im o.g. Fall Daten in die Auswertung, die Du dort nicht haben willst.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
    1. Moin Moin!

      Zum Hintergrund: bei dem HTML-Code handelt es sich um automatisch generierten Code für Online-Befragungen. Leider ist es nicht möglich, auf die Struktur Einfluss zu nehmen. Die einzige Möglichkeit ist eben, die einzelnen Elemente mit Javascript anzufassen und zu verändern. Dass die Struktur extrem kompliziert/verschachtelt und absolut nicht barrierefrei ist, ist offensichtlich, aber eben leider nicht zu ändern...

      Falsch: Wegschmeißen, besseres Tool benutzen.

      Egal was Du mit Javascript bastelst, es wird das Problem nicht vollständig lösen.

      Erstens ist Javascript nicht in jedem Browser vorhanden *und* für die Seite freigeschaltet. Damit ist das ganze Javascript-Gebastel im für Dich schlechten Fall wirkungslos.

      Zweietens hat die Serverseite keine Ahnung von Deinen veränderten Spielregeln und wird daher die Eingaben nach den originalen Spielregeln prüfen (wenn überhaupt). Damit bekommst Du im o.g. Fall Daten in die Auswertung, die Du dort nicht haben willst.

      Alexander

      Grundsätzlich hast du nicht ganz Unrecht, aber ohne aktiviertes Javascript kommt man auch gar nicht erst zur entsprechenden Frage im Interview. Da das System auch viel mit eigenen Scripts arbeitet (nur eben für diesen Fall keines vorsieht), ist eine Teilnahme ohne aktiviertes JS schlicht nicht möglich... Übrigens, ein Tool, das barrierefrei ist, gibt es schlichtweg nicht. Davon abgesehen, und das wäre dann b), habe ich da nicht ganz alleinige Entscheidungsgewalt ;-)

  2. Ach ja, folgenden Ansatz habe ich bisher, auch wenn das wohl extrem dürftig ist. Leider finde ich selbst hier den Fehler nicht - was mache ich falsch (davon abgesehen, dass dieser Weg wohl völlig unelegenat ist)?

    function nmul()  
    {  
    if (documents.getElementsbyName("answer0-5").select == "true")  
    {  
    documents.getElementsbyName("answer0-1").select == "false";  
    documents.getElementsbyName("answer0-2").select == "false";  
    documents.getElementsbyName("answer0-3").select == "false";  
    documents.getElementsbyName("answer0-4").select == "false";  
    }  
    }
    

    Aufgerufen wird die Funktion mit:

    <script type="text/javascript">document.getElementsByTagName("input").onclick="nmul();"</script>

    1. Om nah hoo pez nyeetz, Marco!

      Ach ja, folgenden Ansatz habe ich bisher, auch wenn das wohl extrem dürftig ist. Leider finde ich selbst hier den Fehler nicht - was mache ich falsch (davon abgesehen, dass dieser Weg wohl völlig unelegenat ist)?

      Du schmeißt Vergleichs- und Zuweisungsoperator in einen Topf.

      Matthias

      --
      1/z ist kein Blatt Papier.

      1. Om nah hoo pez nyeetz, Marco!

        Ach ja, folgenden Ansatz habe ich bisher, auch wenn das wohl extrem dürftig ist. Leider finde ich selbst hier den Fehler nicht - was mache ich falsch (davon abgesehen, dass dieser Weg wohl völlig unelegenat ist)?

        Du schmeißt Vergleichs- und Zuweisungsoperator in einen Topf.

        Matthias

        Danke, aber auch damit klappt es nicht.

          
        function nmul()  
        {  
        if (documents.getElementsbyName("answer0-5").select == "true")  
        {  
        documents.getElementsbyName("answer0-1").select = "false";  
        documents.getElementsbyName("answer0-2").select = "false";  
        documents.getElementsbyName("answer0-3").select = "false";  
        documents.getElementsbyName("answer0-4").select = "false";  
        }  
        }
        

        Des Rätsels Lösung liegt wohl im Aufruf der Funktion - dieser erfolgt nämlich nicht (hab das mal mit alert getestet). Hab ich da nen Denkfehler, wenn ich ins HTML-Dokument schreibe:
        <script type="text/javascript">document.getElementsByTagName("input").onclick="nmul();"</script>

        1. Om nah hoo pez nyeetz, Marco!

          Des Rätsels Lösung liegt wohl im Aufruf der Funktion - dieser erfolgt nämlich nicht (hab das mal mit alert getestet). Hab ich da nen Denkfehler, wenn ich ins HTML-Dokument schreibe:
          <script type="text/javascript">document.getElementsByTagName("input").onclick="nmul();"</script>

          document.getElementsByTagName("input") sind im Ernstfall immer mehrere. Du musst schon sagen welches du meinst, das Nullte oder das 17.

          document.getElementsByTagName("input")[234]

          Matthias

          --
          1/z ist kein Blatt Papier.

          1. Vielen Dank für die bisherigen Antworten! Ein in Ansätzen funktionierendes Script habe ich nun schon einmal :-)

              
            function nmul()  
            {  
            if (  
            	document.getElementsByName(" answer0-5")[0].checked == true  
               )  
            {  
            nmul2();  
            }  
              
            if (  
            	(document.getElementsByName(" answer0-1")[0].checked == true) ||  
            	(document.getElementsByName(" answer0-2")[0].checked == true) ||  
            	(document.getElementsByName(" answer0-3")[0].checked == true) ||  
            	(document.getElementsByName(" answer0-4")[0].checked == true)  
               )  
            {  
            nmul1();  
            }  
            }  
              
            function nmul1() // deselektiert die letzte Checkbox bei Anwahl einer der ersten  
            {  
            document.getElementsByName(" answer0-5")[0].checked = false;  
            }  
              
            function nmul2() // deselektiert die ersten Checkboxen bei Anwahl der letzten  
            {  
            document.getElementsByName(" answer0-1")[0].checked = false;  
            document.getElementsByName(" answer0-2")[0].checked = false;  
            document.getElementsByName(" answer0-3")[0].checked = false;  
            document.getElementsByName(" answer0-4")[0].checked = false;  
            }  
            
            

            Der Aufruf erfolgt dabei via:
            <script type="text/javascript">var inputs = document.getElementsByTagName("input");for (var i = 0, l = inputs.length; i < l; i++) {inputs[i].onclick = nmul;}</script>

            Nun habe ich noch folgendes Problem: Das Script funktioniert natürlich nur für die Bedingung, die in der ersten if-Anweisung steht, da es jedes Mal komplett durchlaufen wird. Ich brauche nun also eine Art Verzweigung in der Funktion nmul(), die, je nachdem, ob eine der ersten vier Checkboxen oder die letzte Checkbox zuletzt geklickt wurde, auf die jeweilige Funktion nmul1() oder nmul2() verweist. Wie übergebe ich diese Information an das Script?
            Die Möglichkeit, die Verzweigung direkt im Aufruf des Scripts zu implementieren, würde ich gern ausschließen, da es relativ schwierig ist, "fremden" HTML-Code in die Seiten zu bekommen...

            1. Okay, ich hab's jetzt! *freu*

              Der Code ist zwar mehr als unelegant und muss vermutlich für jeden Einsatz neu angepasst werden (da die Checkboxen-Matrix in der Praxis jedes Mal andere Ausmaße haben wird), aber es ist auf jeden Fall ersteinmal eine funktionierende Grundlage. Und die Anpassungen werden dank Copy & Paste und Suchen & Ersetzen recht schnell von der Hand gehen. Und bevor ich jetzt noch mehrere Tage damit zubringe, den Code zu verschlanken, kann ich das irgendwann mal angehen, wenn ich mehr Ahnung von Schleifen, Arrays usw. habe... Vielen Dank noch einmal an alle Tippgeber!

              Hier mal exemplarisch mein Code für eine Checkboxen-Matrix aus 2 Zeilen und 5 Spalten, wobei die letzte Spalte nur exklusiv angewählt werden kann:

                
              var NMUL0 = "answer0-5";  
              var NMUL1 = "answer1-5";  
                
              // Funktion zum Überprüfen der selektierten Zelle  
              function nmul() {  
              var objSrc = (window.event.target)? window.event.target : window.event.srcElement;  
                
              // deselektiere letzte Zelle  
              if ((objSrc.name == "answer0-1") || (objSrc.name == "answer0-2") || (objSrc.name == "answer0-3") || (objSrc.name == "answer0-4"))  
                 {nmul0a();}  
              if ((objSrc.name == "answer1-1") || (objSrc.name == "answer1-2") || (objSrc.name == "answer1-3") || (objSrc.name == "answer1-4"))  
                 {nmul1a();}  
                
              // deselektiere erste Zellen  
              if (objSrc.name == NMUL0)  
                 {nmul0b();}  
              if (objSrc.name == NMUL1)  
                 {nmul1b();}  
              }  
                
                
              // Funktionen zum Deselektieren der jeweiligen NMUL-Zelle  
              function nmul0a() {  
              document.getElementsByName(NMUL0)[0].checked = false;  
              }  
              function nmul1a() {  
              document.getElementsByName(NMUL1)[0].checked = false;  
              }  
                
                
              // Funktionen zum Deselektieren der jeweiligen MULTI-Zellen  
              function nmul0b() {  
              document.getElementsByName("answer0-1")[0].checked = false;  
              document.getElementsByName("answer0-2")[0].checked = false;  
              document.getElementsByName("answer0-3")[0].checked = false;  
              document.getElementsByName("answer0-4")[0].checked = false;  
              }  
              function nmul1b() {  
              document.getElementsByName("answer1-1")[0].checked = false;  
              document.getElementsByName("answer1-2")[0].checked = false;  
              document.getElementsByName("answer1-3")[0].checked = false;  
              document.getElementsByName("answer1-4")[0].checked = false;  
              }  
              
              
              1. Und die Anpassungen werden dank Copy & Paste und Suchen & Ersetzen recht schnell von der Hand gehen.

                o_O

                // Funktion zum Überprüfen der selektierten Zelle
                function nmul() {
                var objSrc = (window.event.target)? window.event.target : window.event.srcElement;

                Nur als Anmerkung: window.event ist nur im IE gesetzt, und im IE hat das Event-Objekt immer eine srcElement-Eigenschaft.
                In anderen Browsern muss man das Event-Objekt als Parameter entgegen nehmen.

                Es sollte daher schon so heißen, damit es browserübergreiend funktioniert:

                function nmul (e) {  
                  e = e || window.event;  
                  var objSrc = e.target || e.srcElement;
                

                Mathias

    2. Hi,

      Ach ja, folgenden Ansatz habe ich bisher, auch wenn das wohl extrem dürftig ist.

      aber immerhin, es ist ein Anfang. Für die Forderung, dass die ersten vier Checkboxen wieder abgewählt werden, sobald die fünfte aktiviert wird, ist das sogar schon ein guter Ansatz.

      if (documents.getElementsbyName("answer0-5").select == "true")

      Fehler: getElementsByName() liefert nicht ein einzelnes Objekt, sondern ein Array, weil es ja mehrere Elemente mit demselben name-Attribut geben kann (bei Radiobuttons sinnvoll). Achte auch auf korrekte Groß- und Kleinschreibung!
      Fehler: Ein checkbox-Objekt hat keine select-Eigenschaft. Meintest du vielleicht checked?
      Fehler: Du vergleichst einen boolschen Wert mit dem String "true". Wenn schon, dann solltest du auf die Konstante true überprüfen.
      Im Interesse der besseren Lesbarkeit sollte man boolsche Werte aber gar nicht mehr explizit mit true oder false vergleichen. Also anstatt

      if (cb.checked==true)

      lieber kürzer und verständlicher

      if (cb.checked)

      formulieren, solange sichergestellt ist, dass cb.checked wirklich Boolean ist.

      documents.getElementsbyName("answer0-1").select == "false";
      documents.getElementsbyName("answer0-2").select == "false";
      documents.getElementsbyName("answer0-3").select == "false";
      documents.getElementsbyName("answer0-4").select == "false";

      Siehe oben zu getElementsByName() und zur Zuweisung eines Strings anstatt eines boolschen Werts.
      Außerdem ist == der Vergleichsoperator, nicht der Zuweisungsoperator.

      <script type="text/javascript">document.getElementsByTagName("input").onclick="nmul();"</script>

      Auch das geht schief, denn getElementsByTagName() gibt analog zu getElementsByName() ein Array von Elementen zurück, die du einzeln ansprechen musst.

      So long,
       Martin

      --
      Männer sind ungerecht: Sie sehen immer nur den Baum, den eine Frau mit dem Auto gerammt hat. Aber die vielen Bäume, die sie nicht einmal gestreift hat, sehen sie nicht.
      Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
      1. Vielen Dank für die ausführlichen Antworten - es ist schonmal viel klarer jetzt :-)

        <script type="text/javascript">document.getElementsByTagName("input").onclick="nmul();"</script>

        Auch das geht schief, denn getElementsByTagName() gibt analog zu getElementsByName() ein Array von Elementen zurück, die du einzeln ansprechen musst.

        Aber das verstehe ich noch nicht ganz: die Funktion soll ja gerade bei jedem Klick auf irgendeine Checkbox auf der Seite aufgerufen werden. Wie kriege ich das hin, wenn nicht über den den input-Tag? Das Problem ist ja generell, dass ich nur name-Attribute und keine IDs zum Ansteuern zur Verfügung habe...

    3. Aufgerufen wird die Funktion mit:

      <script type="text/javascript">document.getElementsByTagName("input").onclick="nmul();"</script>

      Das ist an sich richtig gedacht, aber so geht das nicht. Du kannst diese Liste wie gesagt durchlaufen und an jedem Element einzeln einen Event-Handler registrieren:

      var inputs = document.getElementsByTagName("input");  
      for (var i = 0, l = inputs.length; i < l; i++) {  
        inputs[i].onclick = nmul;  
      }
      

      Es gibt viele Wege, das eleganter zu lösen (change- und input-Events steigen auf, können also zentral überwacht werden), aber dieser ist kompatibel mit IE < 9.

      Wenn man ohnehin click-Ereignisse verwendet, so kann man diese zentral beim Dokument oder beim Formular überwachen, beispielsweise:

      document.forms[0].onclick = resetCheckboxes;

      (Das Formular muss nicht das erste im Dokument sein. Weitere Zugriffsarten siehe http://de.selfhtml.org/javascript/objekte/forms.htm)

      In der Funktion schaut man dann, ob eine Checkbox geklickt wurde und setzt die jeweils anderen zurück:

      function resetCheckboxes (e) {  
        // [link:http://molily.de/js/event-handling-objekt.html#event-objekt@title=Zugriff auf das Event-Objekt]  
        e = e || window.event;  
        
        // [link:http://molily.de/js/event-handling-objekt.html#currenttarget-target@title=Hole das Zielelement]  
        var target = e.target || e.srcElement;  
        
        // Prüfe Element-Typ input und Feld-Typ checkbox  
        if (target.[ref:self812;javascript/objekte/node.htm#node_name@title=nodeName] != 'input' || target.[ref:self812;javascript/objekte/elements.htm#type@title=type] != 'checkbox') return;  
        
        // Wir haben es mit einer Checkbox zu tun  
        
        // Extrahiere mit einem [ref:self812;javascript/objekte/regexp.htm@title=Regulären Ausdruck] den Gruppennamen und Nummer aus dem Namen (angenommen, das Schema ist immer gleich)  
        var matches = target.[ref:self812;javascript/objekte/elements.htm#name@title=name].[ref:self812;javascript/objekte/string.htm#match@title=match](/^(answer\d+-)(\d+)$/)  
        if (!matches) return; // Keine Treffer  
        
        var group = matches[0]; // z.B. 'answer1-'  
        var number = Number(matches[1]); // z.B. 2  
        
        // Prüfe, welche Checkbox aktiviert wurde  
        if (number == 5) {  
           // Wenn es die fünfte ist, setze die ersten vier mit einer Helferfunktion zurück  
           for (var i = 1; i < 5; i++) {  
             resetCheckbox(group + i);  
           }  
        } else {  
           // Andernfalls setze die fünfte zurück  
           resetCheckbox(group + 5);  
        }  
      }  
        
      // Helferfunktion zum Zurücksetzen von Felder anhand ihres Namens  
        
      function resetCheckbox (name) {  
        // Hole alle Elemente mit dem gegebenen Namen  
        var inputs = document.getElementsByName(name);  
        // Durchlaufe die Liste  
        for (var i = 0, l = inputs.length; i < l; i++) {  
          var input = inputs[i];  
          // Prüfe nochmal zur Sicherheit, ob wir es mit Checkboxen zu tun haben  
          if (input.nodeName != 'input' || input.type != 'checkbox') continue;  
          // Setze auf unchecked  
          input.[ref:self812;javascript/objekte/elements.htm#checked@title=checked] = false;  
        }  
      }
      

      Ungetestet, soll nur einen möglichen Aufbau illustrieren, wie man das einmal zentral für alle Checkboxen umsetzen kann.

      Mathias