Darßfreund: DropDown Menüs und Datenbank - Aufruf-Problem

Hallo,

ich lasse Dropdown-Menüs für jeden Eintrag einer Datenbankabfrage per php erzeugen (Anfrage funktioniert). Bei der resultuierenden Webseite lässt sich jeder Menübutton anklicken - es öffnet sich jedoch immer das Dropdown-Menü des ersten Eintrages. Im resultierenden html-Code erscheinen die richtigen IDs in den Links des Menüs. Geöffnet wird aber immer nur das Dropdown-Menü des ersten Eintrages, auch wenn ich auf den Dropdown-Button den zweiten, dritten Eintrag klicke.

Kann jemamd helfen? Ich finde den Fehler nicht ...

Vielen Dank! Darßfreund

Hier die Quelltext-Auszüge:

php

$kla=0;
$klb=0;
$klc=0;
$kld=0;
foreach ($daten as $inhalt) {
  if ($klausw == "klst"{
  if ($i1 == 0) {
    echo "<div class=\"container\">";
    $i1++;
	}
  if ($inhalt->klasse == "$stuf"."a") {
	  if ($kla == 0) {
      echo "<div class=\"box\"><h2><a href=\"austragungsportal.php?klausw="; 
      echo $stuf;
      echo "a#1\">Klasse ";
      echo $stuf;
      echo "a</a></h2>";
      $kla++;
    }
    if ($inhalt->ausgetragen == "kran") {
      echo "<p class=\"ausgetragen\">";
    }
    else {
      if ($inhalt->ausgetragen == "ausg") {
        echo "<p class=\"ausgetragen\">";
      }
      else {
			  echo "<p class=\"anwesend\">";
      }
    }

    echo "<a href=\"?aktion=anfragen&ID=";
    echo $inhalt->ID;
    echo "&klausw=";
    echo $klausw;
    echo "#1";
    echo "\"> <button class=\"angefragt\">Ange</button></a>";

    echo "<a href=\"?aktion=finden&ID=";
    echo $inhalt->ID;
    echo "&klausw=";
    echo $klausw;
    echo "#1";
    echo "\"><button class=\"gefunden\">Gefu</button></a>";

    echo "<a href=\"?aktion=austragen&ID=";
    echo $inhalt->ID;
    echo "&klausw=";
    echo $klausw;
    echo "#1";
    echo "\"><button class=\"ausgetragen\">Aust</button></a>";

    // Dropdown-Button
    echo "<span class=\"dropdown\">";
    echo "<button onclick=\"myFunction()\" class=\"bibogta\">Dropdown-Button</button>";
    echo "<span id=\"myDropdown\" class=\"dropdown-content\">";
    echo "<a href=\"?aktion=gtaort&ID="; echo $inhalt->ID; echo "&klausw="; echo $klausw; echo "#1"; echo "\">Eintrag 1</a>";
    echo "<a href=\"?aktion=gtaort&ID="; echo $inhalt->ID; echo "&klausw="; echo $klausw; echo "#1"; echo "\">Eintrag 2</a>";
    echo "<a href=\"?aktion=gtaort&ID="; echo $inhalt->ID; echo "&klausw="; echo $klausw; echo "#1"; echo "\">Eintrag 3</a>";
    echo "<a href=\"?aktion=gtaort&ID="; echo $inhalt->ID; echo "&klausw="; echo $klausw; echo "#1"; echo "\">Eintrag 4</a>";
    echo "<a href=\"?aktion=gtaort&ID="; echo $inhalt->ID; echo "&klausw="; echo $klausw; echo "#1"; echo "\">Eintrag 5</a>";
    echo "</span>";
    echo "</span>";
    echo "</p>";
  }

js

 <script>
/* When the user clicks on the button, 
toggle between hiding and showing the dropdown content */
function myFunction() {
  document.getElementById("myDropdown").classList.toggle("show");
}

// Close the dropdown if the user clicks outside of it
window.onclick = function(event) {
  if (!event.target.matches('.bibogta')) {
    var dropdowns = document.getElementsByClassName("dropdown-content");
    var i;
    for (i = 0; i < dropdowns.length; i++) {
      var openDropdown = dropdowns[i];
      if (openDropdown.classList.contains('show')) {
        openDropdown.classList.remove('show');
      }
    }
  }
}
</script>

css

/* Dropdown-Menue */

.dropbtn {
  background-color: #3498DB;
  color: white;
  padding: 16px;
  font-size: 16px;
  border: none;
  cursor: pointer;
}

.dropbtn:hover, .dropbtn:focus {
  background-color: #2980B9;
}

.dropdown {
  position: relative;
  display: inline-block;
}

.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f1f1f1;
  min-width: 160px;
  overflow: auto;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  z-index: 1;
}

.dropdown-content a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
}

.dropdown a:hover {background-color: #ddd;}

.show {display: block;}

.bibogta {background: #ce5cee; color: #ffffff;}
  1. Moin Darßfreund,

    ich habe für Dich mal den Code entsprechend hervorgehoben – das kannst Du beim nächsten Mal auch 😉

    Viele Grüße
    Robert

  2. Moin Darßfreund,

    $kla=0; $klb=0; $klc=0; $kld=0;
    foreach ($daten as $inhalt) {
     if ($klausw == "klst"{
     if ($i1 == 0){
    	echo "<div class=\"container\">";
    	$i1++;
    			}
     if ($inhalt->klasse == "$stuf"."a"){
    

    Was ist as denn für ein Konstrukt? Wenn du sichergehen möchtest, dass $inhalt->klasse ein String ist, solltest Du mit === vergleichen. Und die merkwürdige Stringoperation zum Vergleichen geht auch eleganter, also:

    if ($inhalt->klasse === "${stuf}a") {
    
    	if ($kla == 0){
    		echo "<div class=\"box\"><h2><a href=\"austragungsportal.php?klausw="; echo $stuf; echo "a#1\">Klasse "; echo $stuf; echo "a</a></h2>";
    		$kla++;
    				}
    

    Wenn Du schon nicht die Kurzform zur Ausgabe nutzen möchtest,

    if ($kla === 0) {
        ?><div class="box">
        <h2><a href="austragungsportal.php?klausw=<?= $stuf ?>a#1">Klasse <?= $stuf ?>a</a></h2>
        <?php
    }
    

    könntest Du zumindest die Einrückungen verbessern und echo effizienter einsetzen:

    if ($kla == 0) {
        echo '<div class="box"><h2><a href="austragungsportal.php?klausw=',
            $stuf, "a#1\">Klasse ${stuf}a</a></h2>";
        $kla++;
    }
    

    Das Herumhantieren mit den Strings kannst Du überall machen, wo bisher das Ende des Variablennamens direkt auf einen angrenzenden String triff.

    „Spannender“ wird es aber hier:

    // Dropdown-Button
    echo "<span class=\"dropdown\">";
      echo "<button onclick=\"myFunction()\" class=\"bibogta\">Dropdown-Button</button>";
      echo "<span id=\"myDropdown\" class=\"dropdown-content\">";
        echo "<a href=\"?aktion=gtaort&ID="; echo $inhalt->ID; echo "&klausw="; echo $klausw; echo "#1"; echo "\">Eintrag 1</a>";
        echo "<a href=\"?aktion=gtaort&ID="; echo $inhalt->ID; echo "&klausw="; echo $klausw; echo "#1"; echo "\">Eintrag 2</a>";
        echo "<a href=\"?aktion=gtaort&ID="; echo $inhalt->ID; echo "&klausw="; echo $klausw; echo "#1"; echo "\">Eintrag 3</a>";
        echo "<a href=\"?aktion=gtaort&ID="; echo $inhalt->ID; echo "&klausw="; echo $klausw; echo "#1"; echo "\">Eintrag 4</a>";
        echo "<a href=\"?aktion=gtaort&ID="; echo $inhalt->ID; echo "&klausw="; echo $klausw; echo "#1"; echo "\">Eintrag 5</a>";
      echo "</span>";
    echo "</span>";
    echo "</p>";
    			}
    

    Gibt es einen Grund, warum

    • das kein select ist
    • und alle „Links“ das gleiche Linkziel haben?
    /* When the user clicks on the button, 
    toggle between hiding and showing the dropdown content */
    function myFunction() {
      document.getElementById("myDropdown").classList.toggle("show");
    }
    
    // Close the dropdown if the user clicks outside of it
    window.onclick = function(event) {
      if (!event.target.matches('.bibogta')) {
        var dropdowns = document.getElementsByClassName("dropdown-content");
        var i;
        for (i = 0; i < dropdowns.length; i++) {
          var openDropdown = dropdowns[i];
          if (openDropdown.classList.contains('show')) {
            openDropdown.classList.remove('show');
          }
        }
      }
    }
    

    Du verwendest oben classList.toggle und baust dessen Funktionalität im Eventhandler wieder nach – warum?

    Viele Grüße
    Robert

    1. Danke für die Tipps, wie ich es eleganter machen kann. Sehr praktisch, danke! Werde ich beherzigen!

      Welchen Vorteil würde denn "select" bringen?

      Dass es immer derselbe Link ist, liegt daran, dass ich dort noch Platzhalter drin hab. Das wird später noch geändert. Ich schaue im Moment nur darauf, welche ID bei den Links im html erscheint. Ud dort ist es im resutlierenden quellcode richtig (verschiedene IDs). Beim Anklicken der Dropdown-Menüs erscheint aber immer nur das des ersten Eintrages. Ich finde einfach nicht den Grund. Vermutlich ist es etwas simples ...

      Besten dank für die Mühe! Darßfreund

    2. Hallo Robert,

      "${stuf}a"
      

      Das funktioniert, ist aber ein PHP Quirk und seit PHP 8.2 missbilligt.
      Korrekt ist schon seit PHP 4

      "{$stuf}a"
      

      Du verwendest oben classList.toggle und baust dessen Funktionalität im Eventhandler wieder nach – warum?

      Weil er keinen toggle will, sondern ein remove. Die Abfrage ist aber unnötig, man kann problemlos remove auf eine nichtexistente Klasse machen.

      Und er kennt vielleicht den zweiten Parameter von toggle nicht: .toggle("show", false) wirkt wie remove. Anzuwenden, wenn man statt false einen booleschen Wert hat, der angibt, ob die Klasse rein soll oder nicht.

      Aber wenn er meiner Idee mit details folgt, ändert sich dieser Code eh. Ein select ist aus meiner Sicht suboptimal, weil er dann mit JavaScript auf die Änderung der Auswahl reagieren muss.

      Dass alle Links gleich sind, ist vermutlich ein Artefakt des Kopierens ins Forum…

      Rolf

      --
      sumpsi - posui - obstruxi
  3. Hallo Darßfreund,

    ich habe ein weiteres Mal editiert.

    • Einrückungen an die logische Struktur angepasst
    • Zeilen mit mehreren Statements auf mehrere Zeilen verteilt

    Zeilenumbrüche kosten nichts. Du musst nicht damit sparen.

    Was hingegen kostet, sind dein umständliches Zusammenbauen von HTML mittels echo. Dazu gleich mehr.

    Angesichts des Seiteninhalts sieht es so aus, als wäre dieser Seite nicht öffentlich zugänglich. Deshalb frage ich nicht nach einem Link darauf. Normalerweise fordern wir sowas ein.

    Es sieht aber so aus, als würdest Du in einer Schleife (für die die schließende Klammer fehlt) mehrere Abschnitte generieren. Jeder Abschnitt enthält einen Dropdown-Button und eine Liste von Links, die sich damit öffnen lässt.

    Ursache ist, dass Du diese Liste über ihre ID suchst. Abgesehen davon, dass IDs in einem Dokument eindeutig sein müssen, findest Du auf diese Weise immer nur die erste dieser IDs.

    Ich könnte Dir jetzt Script geben, mit dem Du das mit etwas Navigieren im DOM lösen kannst, aber es geht viel einfacher: das "Disclosure-Widget" <details>, das von allen aktuellen Browsern unterstützt wird. Nicht vom IE, aber den braucht heute keiner mehr.

    <details>
       <summary>Dropdown-Button</summary>
       <a href="...">Eintrag 1</a>
       <a href="...">Eintrag 2</a>
       <a href="...">Eintrag 3</a>
    </details>
    

    Das Summary-Element kannst Du mit CSS nach deinem Wunsch gestalten. Klickst Du darauf, öffnet sich die Liste. Klickst Du nochmal drauf, schließt sie sich.

    Die Links sind eine Liste. Es könnte richtig sein, sie auch als Liste ins HTML zu stellen.

    Wenn Du weiterhin das Feature möchtest, dass sich bei einem Klick ins Nirvana alle Dropdowns schließen, dann ändere den diesbezüglichen Scriptteil so, dass Du allen details-Elementen das open-Attribut wegnimmst (mit removeAttribute).

    Derzeit ist es aber auch so, dass ein Klick auf einen dieser Links alle Dropdowns schließt. Ist das ok für Dich?

    Achso - dieses Feature ist unvollständig. Du musst auch einen keydown-Handler registrieren und bspw. die Esc-Taste abfragen, damit man die Dropdowns auch per Tastatur schließen kann.

    Eine Frage zu $inhalt->ausgetragen="kran" - das klingt nach "krank". Ist es richtig, dass Du dem p Element dann die Klasse "ausgetragen" gibst?

    Deine Echo-Flut kannst Du auf zwei Wegen bekämpfen.

    (1) Stringinterpolation (in PHP Parsing genannt) (2) PHP-Kontext verlassen.

    Stringinterpolation:

    echo "<div class=\"box\"><h2><a href=\"austragungsportal.php?klausw="; 
    echo $stuf;
    echo "a#1\">Klasse ";
    echo $stuf;
    echo "a</a></h2>";
    

    Einfacher:

    echo "<div class='box'><h2><a href='austragungsportal.php?klausw={$stuf}a#1'>Klasse {$stuf}a</a></h2>
    

    Ein String in doppelten Anführungszeichen wird automatisch nach Variablen durchsucht. echo "Klasse $stuf" würde bereits funktionieren, aber Du willst ja noch ein a dahinter setzen. Deswegen muss das $stuf in geschweifte Klammern, damit PHP weiß, wo der Variablenname aufhört (siehe PHP Handbuch, Parsing von Variablen, „komplexe“ Syntax).

    Das geht auch bei der Abfrage auf die Stufe: Hast Du da bereits dein Glück mit Stringparsing versucht, aber wusstet nicht, wie man $stuf von a trennt? Aber weil Du ja ständig $stuf mit a verbindest, könnte man auch erstmal eine Variable $aStufe oder so erzeugen und darin $stuf."a" ablegen.

    Das Escapen von Anführungszeichen ersparst Du Dir, indem Du für's HTML die einfachen Anführungszeichen verwendest.

    Alternative 2: PHP Kontext verlassen

    Das ist für mehrzeiliges HTML interessant. Hinter dem Erzeugen des p Elements erzeugst Du 3 Links und dann das "Dropdown". Man kann einfach mit ?> den PHP Kontext verlassen, HTML raushauen und danach mit <?php wieder in PHP einsteigen. Aus Sicht von PHP ist das wie ein fetter ECHO. Heißt: Man kann das auch innerhalb von PHP Blöcken tun (also Zeugs, das mit { und } eingeschlossen ist).

    PHP-Werte, die in einen solchen Bereich eingetragen werden sollen, fügt man mit dem Expression-Tag <?= ... ?> ein. Für ... setzt Du den gewünschten Wert ein, den Du bisher mit echo ausgegeben hast. Leerzeichen innerhalb des Expression-Tags werden wie in normalem PHP Code ignoriert.

    if ($inhalt->klasse == "$stuf"."a") {
       ...
       ...
    ?>
      <a href="?aktion=anfragen&ID=<?= $inhalt->ID ?>&klausw=<?= $klausw ?>#1">
        <button class="angefragt">Ange</button>
      </a>
      <a href="?aktion=finden&ID=<?= $inhalt->ID ?>&klausw=<?= $klausw ?>#1">
        <button class="gefunden">Gefu</button>
      </a>
      <a href="?aktion=austragen&ID=<?= $inhalt->ID ?>&klausw=<?= $klausw ?>#1">
        <button class="ausgetragen">Aust</button>
      </a>
    
      <details>
         <summary>Einträge</summary>
         ...
      </details>
    <?php
    }  /* ende von $if ($inhalt->klasse ...) */
    ...
    

    Weil geschweifte Klammern bei solchen Konstrukten gerne mal übersehen werden, bietet PHP hier die alternative Syntax für Blockstrukturen an.

    Statt

    if (...bedingung...) {
       ...code...
    }
    

    kann man

    if (...bedingung...):
       ...code...
    endif;
    

    schreiben.

    Eine Frage noch: dass dieses #1, das Du an die diversen Links anhängst, nicht an den Server geht, sondern vom Browser verwendet wird, um in der verlinkten Seite auf das Element mit id="1" zu positionieren, weißt Du?

    Rolf

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

      Es sieht aber so aus, als würdest Du in einer Schleife (für die die schließende Klammer fehlt) mehrere Abschnitte generieren. Jeder Abschnitt enthält einen Dropdown-Button und eine Liste von Links, die sich damit öffnen lässt.

      da habe ich mich durch die Einrückungen und die fehlende schließende Klammer in die Irre führen lassen und die „mehrfach vorkommende ID“ übersehen. Ich hatte die gleichen Links in dem „Drop-Down“ gesehen und daraus gefolgert, dass @Darßfreund immer zur gleichen URL navigiert, aber nicht, weil die ID mehrfach vergeben ist.

      Viele Grüße
      Robert

    2. @@Rolf B

      if ($inhalt->klasse == "$stuf"."a") {
         ...
         ...
      ?>
        <a href="?aktion=anfragen&ID=<?= $inhalt->ID ?>&klausw=<?= $klausw ?>#1">
      

      Ich bin geneigt, dir hierfür einen verdienten Minuspunkt zu geben. Kämpfe aber gerade mit dem Würgreiz, sodass ich an nichts anderes denken kann.

      Statt

      if (...bedingung...) {
         ...code...
      }
      

      kann man

      if (...bedingung...):
         ...code...
      endif;
      

      schreiben.

      Nicht „kann“. „Sollte“. (Ich hab das mal bunt gemacht.)

      Kwakoni Yiquan

      --
      Ad astra per aspera
    3. Lieber Rolf,

      danke für Deine vielen wertvollen Hinweise und die Mühe, die Du Dir damit gemacht hast. Wirklich super! Ich werde alles Stück für Stück einarbeiten und verbessern.

      Zur Lösung meines Hauptproblems hast Du mir das "Disclosure-Widget" <details> empfohlen. Dieses erzeugt leider eine Art neuen Absatz, so dass es insgesamt dann zu eng auf der Seite wird. Das sorgt dafür, dass nicht mehr alles auf eine Seite passt - das ist aus praktischen Gründen aber zwingend für meine Anwendung notwendig. Ich brauche etwas, was sich - ähnlich wie "span" - als Inline-Element verhält.

      Ja, ich generiere in einer Schleife viele Abschnitte. Und jeder Abschnitt enthält einen Dropdown-Button samt einer Liste von Links, die sich damit öffnen lässt.

      Das Merkwürdige ist nun, dass im resultierenden html-Code bereits die richtigen IDs erscheinen (in den Links des Menüs). Beim Klick auf den Dropdown-Button wird bei allen Abschnitten (Schleifendurchgängen) immer nur das Dropdown-Menü des ersten Eintrages gezeigt, was man an der ID erkennt.

      Ich sehe leider nicht die Ursache dafür. Wahrscheinlich muss ich die ID noch irgendwie im JavaScript-Teil verankern, damit es dann unterscheiden kann, welcher Button in der Liste geklickt wird. Könnte das die Lösung sein?

      1. Hallo Darßfreund,

        Inline-Element

        du kannst das details-Element im CSS mit display:inline-block versehen. Dann kommt es nicht auf eine neue Zeile.

        Vorschlag: gib ihm auch noch position:relative, und dem ul Element mit der Linkliste ein position:absolute. Dann öffnet sie sich über dem Folgetext und schiebt ihn nicht weg. Ich schrieb ja schon:

        Die Links sind eine Liste. Es könnte richtig sein, sie auch als Liste ins HTML zu stellen.

        Rolf

        --
        sumpsi - posui - obstruxi