fietur: Beliebige Tabellenzeilen mit <details> schalten?

Hallo,

ich möchte von einer Tabelle zunächst nur die ersten Zeilen anzeigen; erst mit dem Klick auf ein <details> sollen die restlichen Zeilen angezeigt werden.

Meine erste Lösung bestand darin, die Tabelle zu teilen und unter der ersten Tabelle die zweite in <details> zu verbergen. Leider stimmen dann die Spaltenbreiten nicht überein.

Angenommen, ich möchte die mittels tr.switch {display:none} ausgeblendeten Zeilen wieder in die Anzeige holen, wie verknüpfe ich das mit dem Zustand von <details>?

Nicht funktioniert:

<html>
<head>
<style>

  tr.switch { display:none; }
  details[open] ~ table tr.switch { display:block; }

</style>
</head>
<body>
<div class="tabelle">  
  <table>
    <tr><td>immer anzeigen</td></tr>
    <tr class="switch"><td>ein- und ausblenden</td></tr>
  </table>
  
  <details><summary>toggle</summary>(open)</details>
  
</div>
</body>
</html>

Ist das grundsätzlich nicht möglich, oder wo liege ich falsch?

akzeptierte Antworten

  1. Hallo fietur,

    das ist eine Variante des Checkbox-Hacks und "funktioniert"

    entweder
    wenn Du das details-Element vor die Tabelle schaltest
    oder
    wenn Du den zweiten Selektor so schreibst:
    .tabelle:has(details[open]) table tr.switch { display:block; }
    

    Die :has Pseudoklasse prüft, ob der in Klammern stehende Selektor innerhalb des Elements zu finden ist, auf das sich die Pseudoklasse bezieht. Sie ist ziemlich neu in der Wildnis und muss im Firefox noch per Flag aktiviert werden (caniuse.com),

    Es hat aber alle Nachteile des Checkbox-Hacks, was Zugänglichkeit angeht.

    Unsere Wiki-Empfehlung, statt des Checkbox-Hacks ein details-Element zu verwenden, hast Du hier auf kreative Weise wörtlich genommen, aber so war sie nicht gemeint. Dumm ist nur, dass sich für Tabellenzeilen das details-Element so, wie wir es im Wiki gemeint hatten, nicht anwenden lässt. Stellst Du tabellarische Daten dar?!

    Besser ist ein Button mit einem aria-controls Attribut, das auf die geschaltete Tabellenzeile verweist (oder auf die ganze Tabelle, wenn die geschaltete Zeile mal hier und mal da sein kann). Und eine Prise JavaScript, um ihn zum Leben zu erwecken.

    Wenn Du mehr als eine .switch-Zeile hast, musst Du vermutlich die Tabelle per aria-controls referenzieren. Es könnte auch gut sein, der Tabelle ein aria-live Attribut zu geben mit aria-relevant="all". Dazu sollen unsere ARIen-Sänger mehr sagen 😉

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Danke!

      <details> VOR der Tabelle... Darauf wäre ich nicht gekommen. Mal sehen, was ich damit anfangen kann.

      Nach sowas wie has() habe ich gesucht, aber das wird erst in Jahren zum Einsatz kommen können. Meine Besucher kommen mit teils musealen Browsern vorbei. An Buttons hatte ich auch schon gedacht, aber Javascript bleibt außen vor.

      Ich könnte die Einträge - Termine mit Datum, Ort, Veranstaltung und Veranstaltern - zwar auch als Listenelemente gestalten, aber die Tabellendarstellung mit festen Spalten erleichtert das schnelle Auffinden und ist oft Hauptgrund des Besuchs der Seite.

      Die Zugänglichkeit ist derzeit sekundär - ausgeblendete Inhalte sind relativ unwichtig und vor allem vollständig über das klassische Menü erreichbar. Ich setze <details> hauptsächlich deshalb ein, um dem geringen Platzangebot auf Handys entgegenzukommen, also das Scrollen nicht überzustrapazieren.

      Wobei es schon einer gewissen Ironie nicht entbehrt, gerade für das Handy besonders große Bedienelemente zu benötigen.

      Ein schöner Nebeneffekt, bei Artikeln nur Bild und Teaser anzuzeigen ist, dass sich das Benutzen der Zieharmonikas nicht mehr so sehr nach Navigieren anfühlt; die Orientierung bleibt besser erhalten als beim Anklicken von Links. Man bleibt mehr "in" der Seite und muss nicht nach der (Browser-) Zurück-Funktion suchen, weil man sich doch verirrt hat.

      1. Hallo fietur,

        Ich setze <details> hauptsächlich deshalb ein, um dem geringen Platzangebot auf Handys entgegenzukommen, also das Scrollen nicht überzustrapazieren.

        Hä? Das geht auch mit einer Checkbox. Das for-Attribut des label-Elements kennst Du?

        <input id="einblenden" type="checkbox"><label for="einblenden">Einblenden</label>
        

        Jetzt reagiert die Checkbox auf ein antippen des Labels, und das kannst Du mit padding so groß machen, wie Du willst. Wenn Du willst, kannst Du mit einer @media-Abfrage das Padding bei kleinen Viewports sogar noch erhöhen.

        Du kannst die Checkbox auch visuell verstecken, musst dann aber dem Label ein Outline verpassen, wenn die Checkbox den Fokus hat:

        #einblenden {
           width: 0;
           margin: 0;
           padding: 0;
        }
        #einblenden:focus + label[for=einblenden] {
           outline: 2px solid black;
        }
        #einblenden:checked + label[for=einblenden] {
           background-color: green;
        }
        

        oder so ähnlich. Die Checkbox ist jetzt versteckt und das Label ist visuell das Bedienelement.

        Ich hab sowas vorgestern erst noch gebaut, für die Überarbeitung vom chmod-Helferlein im Selfwiki. Da habe ich allerdings die Checkbox im Label und arbeite mit :has() - mit einer @supports-Klausel für Browser, die :has() nicht kennen (wie z.B. den Firefox 😟). Dort ist einfach die Checkbox sichtbar.

        Guck Dir das CSS zum chmod-Helferlein mal in der CSS Datei an, die steht im Selfwiki in Mediawiki:Helferlein.css. Die chmod-Styles sind alle mit der Klasse .helferlein-chmod qualifiziert.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Ja, die Checkboxen kenne ich. Aber zum Ausblenden von umfangreichen Texten sind doch <details> überhaupt ins Leben gerufen worden. Auch <summary> weist auf diese Rolle hin. Checkboxen gibts bei mir hauptsächlich in diversen Formularen, und dort modifiziert.

          Ich verwende oft SVG-Symbole. In der Vergangenheit meist inline (weil die Farben als "currentColor" angegeben einfach mit color im CSS eingestellt werden können), und jetzt immer häufiger im CSS per image-url.

          Ich habe jetzt eine Lösung zum Ein- und Ausblenden von Tabellenzeilen am Ende der angezeigten Tabelle gefunden. Ich berechne einfach die Höhe der Tabelle und setze das <summary> ans untere Ende; wird das angeklickt, verschiebe ich das <summary> entsprechend der hinzugekommenen Zeilen.

          Vermutlich kann man sich auch eine komplette CSS-Lösung basteln, die die Anzahl der Zeilen zählt, aber mir ist es jetzt nicht gelungen.

          Außerdem gibt es irgendwie Murks, wenn eine zweite Spalte hinzugefügt wird - in den ausgeblendeten Zeilen werden die Spalten mit der ersten Spalte zusammengefasst.

          Hier das Beispiel:

          <html>
          <head>
          <style>
          
            .tabelle { position:relative; }
            table { position:absolute; top:0rem; left:0rem; background:lightgray; }
            details { position:absolute; }
            
            details > summary { list-style:none; font-size:2rem; }
            details summary::marker { display:none; }
            details summary { cursor:pointer; margin-left:2.5rem; color:blue; }
            details summary::before { content:"..."; }
           
            /* Zeilenhöhe festlegen und Abstand bestimmen */
            tr { height: 1.8rem; }
            .tabelle {
            	--anzPerm: 2;
            	--anzSwit: 2;    
              --cl: calc(var(--anzPerm) * 1.8rem);
              --op: calc((var(--anzSwit) + var(--anzPerm)) * 1.8rem);
            }   
             
            /* Zeilen ein- und ausblenden */
            tr.switch { display:none; }
            details[open] ~ table tr.switch { display:block; }
              
            /* Platzieren des Schaltelements */
            details[closed] { top: var(--cl); }
            details[open] { top: var(--op); }
          
          </style>
          </head>
          <body>
          Beispiel
          <hr>
          <div class="tabelle">  
            
            <details closed><summary></summary></details>
            
            <table>
              <tr class="permanent"> <td>immer angezeigt</td> <td>schmale Spalte</td></tr>
              <tr class="switch">    <td>Inhalt 1</td>        <td>schmale Spalte</td></tr>
              <tr class="permanent"> <td>(immer)</td>         <td>ganz und gar nicht mehr schmale Spalte</td></tr>
              <tr class="switch">    <td>Inhalt 2</td>        <td>ganz und gar nicht mehr schmale Spalte</td></tr>
            </table>
           
          </div>
          </body>
          </html>
          

          Ich denke aber, dass das in eine Sackgasse führt.

          1. Hallo fietur,

            verwende zum Einblenden eines tr Elements nicht display:block. Das Display-Modell für tabellenzeilen ist display:table-row.

            https://wiki.selfhtml.org/wiki/CSS/Eigenschaften/display

            Rolf

            --
            sumpsi - posui - obstruxi
            1. @@Rolf B

              verwende zum Einblenden eines tr Elements nicht display:block. Das Display-Modell für tabellenzeilen ist display:table-row.

              Ja, aber:

              Verwende zum Ausblenden eines tr-Elements nicht display:none. Das HTML-Attribut zum Ausblenden ist hidden.

              Bei gesetztem hidden-Attribut wirkt der Browserdefault [hidden] {display:none}. Zum Anzeigen einfach hidden entfernen und man muss sich keine Gedanken über den Wert für die display-Eigenschaft machen.

              (Müsste man auch nicht bei display:revert. Aber das hidden-Attribut hat noch den Vorteil, dass es auch im DOM auftaucht, man es also im Entwicklertool sieht.)

              Anstatt nun mehrere tr initial auszublenden kann man diese auch in gruppieren – tbody ist dafür vorgesehen – und hidden auf die ganze Gruppe anwenden: guckst du.

              Accessibility: Das Ausblenden des Buttons hab ich mal auskommentiert. Hier muss noch Fokus-Management betrieben werden. Wenn der Button, auf dem vorher der Fokus lag, verschwindet, landet der Fokus im Nirvana.

              🖖 Живіть довго і процвітайте

              --
              „Ukončete, prosím, výstup a nástup, dveře se zavírají.“
              1. Verwende zum Ausblenden eines tr-Elements nicht display:none. Das HTML-Attribut zum Ausblenden ist hidden.

                Der Hack funktioniert aber nicht mit hidden - damit werden die Zeilen angezeigt und sogar der <details>-Schalter verdeckt. Ein Indiz dafür, wie unsauber diese Lösung mit <details> ist.

                Anstatt nun mehrere tr initial auszublenden kann man diese auch in gruppieren – tbody ist dafür vorgesehen – und hidden auf die ganze Gruppe anwenden: guckst du.

                Ja, wenn man die Einträge gruppieren möchte. Sind sie aber wild verteilt, braucht man eine andere Lösung. Dass man mehrere tbody haben kann, wusste ich noch nicht.

                Accessibility: Das Ausblenden des Buttons hab ich mal auskommentiert. Hier muss noch Fokus-Management betrieben werden. Wenn der Button, auf dem vorher der Fokus lag, verschwindet, landet der Fokus im Nirvana.

                Stimmt, daran muss gedacht werden, wenn man das tatsächlich umsetzt.

            2. Danke, display:table-row behebt das Problem der zusammenfallenden Spalten.

              1. Hallo fietur,

                ja, aber wie Gunnar sagte: es ist das falsche Werkzeug. Das fiel mir leider gestern nicht ein. Das hidden-Attribut oder die visibility:hidden CSS Eigenschaft sind besser. Um hidden umzuschalten brauchst Du JavaScript, visibility geht auch mit CSS.

                Der Nachteil von visibility:hidden ist normalerweise, dass das Element nur ausgeblendet wird, aber weiterhin Platz dafür reserviert wird. Tabellen sind die Ausnahme, dort sorgt visibility:hidden auch dafür, dass die verborgenen Zellen keinen Platz einnehmen. Habe ich zumindest neulich so gelesen…

                Du kannst auch deine Logik so gestalteten, dass Du nur die Negativaussage display:none verwendest und die Positivaussage, sei es block oder table-row, dadurch herbeiführst, dass kein display:none vorhanden ist. Ganz im Geiste von "nicht zusammengeschissen ist genug gelobt".

                Das kriegt man zumeist mit einer :not()-Pseudoklasse im Selektor gelöst.

                Rolf

                --
                sumpsi - posui - obstruxi
                1. Also heißt es dann so (oder in Negation umgekehrt):

                    tr.switch { visibility:collapse; }
                    details[open] ~ table tr.switch { visibility:visible }
                  

                  Mit visibility:hidden; blieben mir die Zeilen (leer) erhalten. Vielleicht müssen derart inklusive Platzbedarf ausblendbare Zellen per tbody organisiert sein.

                  1. Hallo fietur,

                    Vielleicht müssen derart inklusive Platzbedarf ausblendbare Zellen per tbody organisiert sein.

                    Nein, müssen sie nicht. Ich hatte das mit collapse offenbar falsch verstanden. Zumindest bei mir funktioniert es mit collapse in Chrome und FF auch ohne tbody.

                    Was aber auch funktioniert, und was ich mit der "Negativ-Formulierung" meinte, ist:

                    details:not([open]) ~ table tr.switch { display:none; }
                    

                    Oder visibility:collapse, geht auch. Das ist dann nur eine Regel.

                    Rolf

                    --
                    sumpsi - posui - obstruxi
                    1. Vielleicht müssen derart inklusive Platzbedarf ausblendbare Zellen per tbody organisiert sein.

                      Nein, müssen sie nicht. Ich hatte das mit collapse offenbar falsch verstanden. Zumindest bei mir funktioniert es mit collapse in Chrome und FF auch ohne tbody.

                      Da hatte ich mich blöd ausgedrückt. Mit collapse funktioniert es. Die Aussage mit dem tbody hatte nichts damit zu tun, sondern war nur ein vorschneller (und falscher) Erklärungsversuch, dass hidden und collapse im Kontext von Tabellen (und vielleicht nur im Rahmen weiterer Bedingungen - tbody) das gleiche machen könnten. Was sie ja nicht tun.

                      Was aber auch funktioniert, und was ich mit der "Negativ-Formulierung" meinte, ist:

                      details:not([open]) ~ table tr.switch { display:none; }
                      

                      Oder visibility:collapse, geht auch. Das ist dann nur eine Regel.

                      Interessanterweise funktioniert von beiden Varianten

                      details:not([open]) ~ table tr.switch { visibility:collapse; }
                      details[closed] ~ table tr.switch { visibility:collapse; }
                      

                      nur die erste. Ich hätte beides für äquvalent gehalten.

                      Wie kommt das denn zustande?

                      1. @@fietur

                        Interessanterweise funktioniert von beiden Varianten

                        details:not([open]) ~ table tr.switch { visibility:collapse; }
                        details[closed] ~ table tr.switch { visibility:collapse; }
                        

                        nur die erste. Ich hätte beides für äquvalent gehalten.

                        Wie kommt das denn zustande?

                        Das details-Element(objekt) hat entweder das Attribut/die Eigenschaft open gesetzt oder nicht gesetzt; aber nicht die Eigenschaft closed.[1]

                        🖖 Живіть довго і процвітайте

                        --
                        „Ukončete, prosím, výstup a nástup, dveře se zavírají.“

                        1. Klar könnte man eine solche mit JavaScript setzen; man sollte aber nicht erwarten, dass sich daraufhin was tut. 😏 ↩︎

                        1. Das details-Element(objekt) hat entweder das Attribut/die Eigenschaft open gesetzt oder nicht gesetzt; aber nicht die Eigenschaft closed

                          Umpf. Danke, das war mir nicht bewusst.

      2. @@fietur

        An Buttons hatte ich auch schon gedacht, aber Javascript bleibt außen vor.

        Warum das denn?

        Für dynamische Änderungen auf der Seite ist JavaScript das Mittel der Wahl. Es gibt kaum vernünftige Lösungen ohne JavaScript.

        Zum Auslösen von Aktionen sind Buttons das Mittel der Wahl.

        🖖 Живіть довго і процвітайте

        --
        „Ukončete, prosím, výstup a nástup, dveře se zavírají.“
        1. @@fietur

          An Buttons hatte ich auch schon gedacht, aber Javascript bleibt außen vor.

          Warum das denn?

          Weil ich bisher grundsätzlich kein JS eingesetzt habe. Anfangs aus Sicherheitsgründen, auch weil ich JS nicht beherrsch(t)e, und inzwischen auch, weil ich es nie wirklich vermisst habe. Und weil ich nur alle paar Jahre intensiv (wieder-)einsteige. Da reicht es mir schon, "nur" HTML, CSS und PHP quasi neu zu lernen. Andererseits ist mir mittlerweile so viel verschiedenes Zeugs untergekommen, dass mich JS nicht mehr schrecken muss. Ein Tier mehr im Zoo.

          Für dynamische Änderungen auf der Seite ist JavaScript das Mittel der Wahl. Es gibt kaum vernünftige Lösungen ohne JavaScript. Zum Auslösen von Aktionen sind Buttons das Mittel der Wahl.

          Da widerspreche ich nicht. Meine Buttons befanden sich bisher nur in Formularen. Oder waren tatsächlich Links, die die Änderungen serverseitig erledigt haben. Nichts zeitkritisches, und man kommt tatsächlich ziemlich weit damit. Ich erinnere mich an ein Memory, das ich so angeboten hatte. Bis dann die robots-misachtenden Crawler jede Variante zu erfassen versucht haben...

          Aber clientseitige Intelligenz hat eine Menge für sich, gerade auch im Hinblick auf Mobilverbindungen, die jetzt auch beim Verwalten meiner Seite eine größere Rolle spielen. Da sehe ich noch Potenzial zum Einsparen weiterer Serververbindungen.