Voll?: Frage zum Wiki-Artikel „empty“

problematische Seite

Ist es bekannt, daß :empty als Style anders funktioniert als :empty in einem querySelector bzw. querySelectorAll?

Jedenfalls habe ich länger gesucht, wieso es mit einem :empty { display: block; inline-size: 4cm; block-size: 2cm; background-color: red; } im Stylesheet auch nach einem

const empties = document.querySelectorAll(':empty');
for (const empty of empties) {
	console.log(empty);
	empty.remove();
	rumble = true;
}

immer noch so hübsche rote Karten zu sehen gab. Nur ein paar verschwanden damit.

Es genügen, soweit ich das jetzt sehe, irgend welche Attribute, Klassen oder Daten-Elemente, die zugewiesen sind, um diesen Unterschied auszumachen.

  1. problematische Seite

    Servus!

    Ist es bekannt, daß :empty als Style anders funktioniert als :empty in einem querySelector bzw. querySelectorAll?

    Es genügen, soweit ich das jetzt sehe, irgend welche Attribute, Klassen oder Daten-Elemente, die zugewiesen sind, um diesen Unterschied auszumachen.

    Der Grund für dein Verhalten liegt sehr wahrscheinlich nicht an Attributen, Klassen oder data-*-Attributen – die spielen für :empty keine Rolle.

    Ein Element ist :empty, wenn es

    • keine Kind-Elemente und
    • keine Textknoten (auch keine Whitespace-Textknoten!) enthält.

    Wichtig:
    Schon ein einziges Leerzeichen oder ein Zeilenumbruch im HTML erzeugt einen Textknoten – und dann ist das Element nicht mehr empty.

    Herzliche Grüße
    Matthias Scharwies

    1. problematische Seite

      @@Matthias Scharwies

      Wichtig:
      Schon ein einziges Leerzeichen oder ein Zeilenumbruch im HTML erzeugt einen Textknoten – und dann ist das Element nicht mehr empty.

      Gegenwärtig ist das so. In Level 4 wird sich das ändern.

      🖖 Live long and prosper

      --
      In our chants of “ICE out now”
      Our city’s heart and soul persists
      Through broken glass and bloody tears
      On the streets of Minneapolis

      — Bruce Springsteen, Streets of Minneapolis
      1. problematische Seite

        Servus!

        @@Matthias Scharwies

        Wichtig:
        Schon ein einziges Leerzeichen oder ein Zeilenumbruch im HTML erzeugt einen Textknoten – und dann ist das Element nicht mehr empty.

        Gegenwärtig ist das so. In Level 4 wird sich das ändern.

        Danke!

        Herzliche Grüße
        Matthias Scharwies

      2. problematische Seite

        @@Gunnar Bittersmann

        empty.

        In Level 4 wird sich das ändern.

        Firefox hat das Verhalten bereits als :-moz-only-whitespace implementiert.

        ☞ Codepen

        🖖 Live long and prosper

        --
        In our chants of “ICE out now”
        Our city’s heart and soul persists
        Through broken glass and bloody tears
        On the streets of Minneapolis

        — Bruce Springsteen, Streets of Minneapolis
      3. problematische Seite

        Hallo Gunnar,

        einerseits – gut so. Die aktuelle Definition ist nicht zweckmäßig.

        Andererseits – wow! Die Spec-Autoren leisten sich einen Breaking Change?

        Rolf

        --
        sumpsi - posui - obstruxi
        1. problematische Seite

          @@Rolf B

          einerseits – gut so. Die aktuelle Definition ist nicht zweckmäßig.

          Andererseits – wow! Die Spec-Autoren leisten sich einen Breaking Change?

          Das hat mich auch kurz stutzig gemacht.

          Gerade mal geschaut, wer die Autoren sind. fantasai und Tab Atkins, beide hochkompetent.

          Ich nehme an, sie sind nach reiflicher Überlegung zu dem Schluss gekommen, dass der Change nicht breaking ist, also keine bestehenden Webseiten kaputtmacht.

          Was wäre die Alternative? Eine neue Pseudoklasse einführen?

          Der PHP-Weg: unsinnige Implementierungen nicht ersetzen, sondern wegen Abwärtskompatibilität beibehalten und sinnige Implementierungen unter neuem Namen danebenzustellen. Sodass man als unbedarfter Entwickler gar nicht mehr überschauen kann, welche Methoden man denn nehmen sollte und von welchen man tunlichst die Finger lassen sollte. Da fallen mir bspw. die unzähligen sogenannten Stringfunktionen ein, die nicht mit mb_ anfangen, also keine Stringfunktionen sind.

          Nee, das ist schon gut, dass das in CSS besser gehandhabt wird.

          🖖 Live long and prosper

          --
          In our chants of “ICE out now”
          Our city’s heart and soul persists
          Through broken glass and bloody tears
          On the streets of Minneapolis

          — Bruce Springsteen, Streets of Minneapolis
          1. problematische Seite

            Hallo Gunnar Bittersmann,

            Da fallen mir bspw. die unzähligen sogenannten Stringfunktionen ein...

            von denen einige mit str_ beginnen und andere so wie in der C-Library benannt sind. Und einige von denen, die unter String Functions stehen, sind nicht wirklich welche (setlocale, vfprintf).

            Die Ursünde des Einbyte-Zeichensatzes loszuwerden war aber mit dem Standardfunktionssatz nicht möglich. Selbst C++ kämpft noch damit, std::wstring scheint der aktuelle Notnagel zu sein.

            Und mit denormalisiertem Zeugs wie "e\u0301" (e mit combining acute accent) oder diesen Kombinationsmonstern im Emoji-Bereich kommen die meisten Libs vermutlich auch nicht klar.

            bricht vermutlich nichts

            Na, doch schon. Was vorher nicht empty war, wird auf einmal als empty erkannt, das könnte ein paar Seiten beschädigen. Eigentlich ist die CSS Doktrin, an einmal verabschiedete Dinge nicht mehr zu rühren. Aber ich stimme Dir zu, dass es so das kleinere Übel ist. Eine Pseudoklasse :only-whitespace hätte ich nicht haben wollen.

            Rolf

            --
            sumpsi - posui - obstruxi
            1. problematische Seite

              @@Rolf B

              Und mit denormalisiertem Zeugs wie "e\u0301" (e mit combining acute accent)

              Du meinst decomposed Zeugs. (In meiner Übersetzung habe ich den Begriff auf Englisch belassen und mit „getrennt“ umschrieben; gleiches für precomposed/„zusammengesetzt“.)


              Was vorher nicht empty war, wird auf einmal als empty erkannt, das könnte ein paar Seiten beschädigen.

              Mir wollte kein Anwendungsfall einfallen, wo das wirklich eine Gefahr sein könnte.

              Eigentlich ist die CSS Doktrin, an einmal verabschiedete Dinge nicht mehr zu rühren.

              Wo wir gerade über breaking changes sinnieren: Ich bin auf dieses Video von Kevin Powell gestoßen, wo er über breaking changes bei 100vw sinniert.

              🖖 Live long and prosper

              --
              In our chants of “ICE out now”
              Our city’s heart and soul persists
              Through broken glass and bloody tears
              On the streets of Minneapolis

              — Bruce Springsteen, Streets of Minneapolis
    2. problematische Seite

      Wenn ich nicht deshalb und davor extra alle solchen Knoten entfernt hätte. Nicht mal mit normalize(), sondern mit einem NodeIterator „zu Fuß“ (in einem Aufwasch mit anderen ggf. anfallenden Säuberungsaktionen).

      Auch der Inspektor meinte: „da ist nichts (mehr) drin“. Und dann eben die Diskrepanz, daß das Skript ettliche Knoten bei document.querySelectorAll(':empty') (und auch manch anderen Versuche mit entsprechenden Selektoren, die hätten passen können) ignoriert hat, während der Eintrag im Stylesheet hübsche rote Rechtecke …

      Der einzige Unterschied zu denen, die gefunden wurden und den ich entdecken konnte ist eben, daß die ignorierten „irgend was“ als Attribt, Klasse oder Daten-Dinges geführt haben.

      1. problematische Seite

        @@Voll?

        Der einzige Unterschied zu denen, die gefunden wurden und den ich entdecken konnte […]

        Und du bist auch der einzige, der den Unterschied finden kann.

        Wenn du das Feld „problematische Seite“ mit dem Link zur problematischen Seite befüllt hättest anstatt mit einem Link zum SELFHTML-Wiki, hätte man das Problem vielleicht nachvollziehen können. Hast du aber nicht.

        So kann man nur sagen: du machst was falsch. Viel Spaß bei der Fehlersuche!

        🖖 Live long and prosper

        --
        In our chants of “ICE out now”
        Our city’s heart and soul persists
        Through broken glass and bloody tears
        On the streets of Minneapolis

        — Bruce Springsteen, Streets of Minneapolis
        1. problematische Seite

          Mittlerweile bin ich etwas klüger geworden: nichts zu finden gab es bei Operationen innerhalb eines DocumentFragment. Dieses dann aber, eingefügt in document.body, lieferte dem Stylesheet Futter für :empty.

          Bei den Operationen im Fragment habe ich lediglich ungeliebte Dinge entfernt, nichts hinzugefügt. Also kein createElement, kein append, lediglich remove sowie removeAttribute. Sogar replaceChildrengibt’s nur fürs Einhängen des Fragments.

          Auch ein normalize()lief über das Fragment. Nur(?) zwischen diesem und der Stelle, an der querySelectorAll(':empty') blind war, liefen noch `NodeIterator˚-Schleifen (mittendrin versuchsweise auch mal mit TreeWalker) mit entsprechenden Aufräum-Aktionen.

          „Unten“ angelangt hatten die sich verweigernden Nodes dann aber trotzdem textContent: lauter \n. Und die verschwanden dann, offensichtlich („sagt ja schon das CSS danach“), alleine durch das einhängen des Fragments.

          Jetzt habe ich lediglich noch ein normalize() direkt vor dem relevanten Abschnitt eingefügt. Und schon klappt es wie gedacht. Nur: das Wieso, den Unterschied, den verstehe ich „nicht wirklich“. Wobei das Unverständnis auf dem Effekt des „Whitespace raus beim Einhängen“ beruht. (Die „empty-ignorierten“ Nodes befinden sich nicht mal am Anfang oder Ende des Fragments!)

          (Anmerkung: außer den erwähnten Querries war/ist, vermutlich „natürlich“, auch matchesbetroffen.)

        2. problematische Seite

          BTW: wo hätte ich anstelle zu einer „problematischen Seite“ einen Link hier ins Wiki eingetragen? War das womöglich das Wiki, das so etwas bei einem „im SELF-HTML Formum fragen“ macht?

          Schon weil das Problem ja „nur“ beim Neuaufbau einer Seite „mit DIV-Wüsten drin“ gesehen, ja verzweifelt gesucht wurde, wäre es doch seltsam, wenn dieser (immer noch) Rohbau in die Welt entlassen worden wäre.

          1. problematische Seite

            Guten Morgen!

            Ich habe den Artikel überarbeitet und selectors level 4 und das rekursive Löschen hinzugefügt.
            Danke an alle Beteiligten!

            BTW: wo hätte ich anstelle zu einer „problematischen Seite“ einen Link hier ins Wiki eingetragen? War das womöglich das Wiki, das so etwas bei einem „im SELF-HTML Formum fragen“ macht?

            Das Wiki verlinkt – anstelle zu einer Diskussionsseite wie in anderen Wikis - direkt ins Forum und schlägt diesen Betreff/Titel vor.

            Wenn du eine „Frage zum Wiki-Artikel xy“ hast, kannst du den Betreff so lassen.
            Das war in diesem Fall so richtig.

            Wenn sich deine Frage nicht auf den Wiki-Artikel selbst bezieht, steht es Dir doch frei, den Betreff zu ändern.

            Schon weil das Problem ja „nur“ beim Neuaufbau einer Seite „mit DIV-Wüsten drin“ gesehen, ja verzweifelt gesucht wurde,

            Kein Mensch baut doch eine Seite „mit DIV-Wüsten drin“ neu auf! Oder doch?

            Herzliche Grüße
            Matthias Scharwies

            1. problematische Seite

              @@Matthias Scharwies

              Ich habe den Artikel überarbeitet

              Du wolltest sagen: ‚Ich bin dabei, den Artikel zu überarbeiten‘? In der gegenwärtigen Fassung enthält er einige Fehler:

              • „Der :empty-Selektor als strukturelle Pseudoklasse ermöglicht […] beliebige leere Elemente zu selektieren, die keine untergeordneten Elemente und keinen Textinhalt (nicht einmal Leerzeichen) haben.“ – „ermöglicht“ im Präsens. Ja, das ist gegenwärtig der Stand der Dinge.

                „Beachten Sie: In Level 2 und Level 3 matchte :empty nicht bei Elementen …“ – Warum dann hier die Vergangenheitsform „matchte“, wenn es doch gegenwärtig der Stand der Dinge ist?

                „Dies wurde geändert“ – Nein, noch nicht. Zukünftig wird es das, also „Dies wird geändert werden“.

              • „In Level 2 und Level 3 matchte :empty nicht bei Elementen, die Zeilenumbrüche oder Leerzeichen wie   enthielten. Dies wurde geändert“ – Nein, für   wird sich da gar nichts ändern, weil das kein Leerzeichen in dem Sinne ist. (Das war jetzt nur einen Click entfernt.)

                Siehe Codepen spaces and :-moz-only-whitespace


              und selectors level 4 und das rekursive Löschen hinzugefügt.

              Und dabei die Zielgruppe aus den Augen verloren‽ Wen aus der Zielgruppe interessiert das? Man muss nicht über jedes Stöckchen springen.

              🖖 Live long and prosper

              --
              In our chants of “ICE out now”
              Our city’s heart and soul persists
              Through broken glass and bloody tears
              On the streets of Minneapolis

              — Bruce Springsteen, Streets of Minneapolis
            2. problematische Seite

              Kein Mensch baut doch eine Seite „mit DIV-Wüsten drin“ neu auf! Oder doch?

              Nun, hier im Kontext beispielsweise, IMO, Seiten wie https://www.westlotto.de — andere dieser Lotto-Archive sehen, was das angeht und so weit ich sie fand, kaum anders aus. — WestLotto liefert wenigstens keine seltsamen (nicht unbedingt nachvollziehbare, in der Summe zumindest vom Rundungsfehler-Problem geplagte) Umrechnungen von DM-Beträgen.

              Ansonsten hatte ich bisher meist das Glück, daß „denen“ mittlerweile <main> (wie auch hier) untergekommen ist, gefallen hat und so der da für mich interessante Inhalt damit hinreichend gut zu separieren war.

              All denen war dabei gemeinsam, daß ihr Aufbau nach „hat ’ne Maschine gemacht“ („mit KI drin?“ hatte ich doch schon erwähnt?) aussah. Und das sogar, ohne daß ich dafür Sammlungen von „alle Kombinationen aller Klassen, versammelt euch“ anheim gefallen bin. Es könnte also durchaus ein Merkmal für solche Dinge sein, daß das CSS bei denen einem Muster ähnlich dem folgenden aufgebaut ist:

              .a.b.c.d { margin: 20px; border: 30px; }
              .a.b.d.c { margin: 20px; border: 30px; }
              .a.c.b.d { margin: 20px; border: 30px; }
              .a.c.d.b { margin: 20px; border: 30px; }
              .a.d.b.c { margin: 20px; border: 30px; }
              .a.d.c.b { margin: 20px; border: 30px; }
              …
              .d.c.b.a { margin: 20px; border: 30px; }
              
  2. problematische Seite

    @@Voll?

    Ist es bekannt, daß :empty als Style anders funktioniert als :empty in einem querySelector bzw. querySelectorAll?

    Das ist weder bekannt noch ist das überhaupt so. Guckst du: :empty in CSS and JavaScript.

    Genau diejenigen Türen, die CSS per p:empty rot gefärbt hat, färbt JavaScript per querySelectorAll('p:empty') schwarz. Und die anderen bleiben farblos.

    I see a red door and I want to paint it black
    No colours anymore, I want to paint it black

    — The Rolling Stones, Paint It Black

    🖖 Live long and prosper

    --
    In our chants of “ICE out now”
    Our city’s heart and soul persists
    Through broken glass and bloody tears
    On the streets of Minneapolis

    — Bruce Springsteen, Streets of Minneapolis
    1. problematische Seite

      So, ein wenig gebastelt. Ergebnis: mit Attributen o.ä. scheitert :empty. Unabhängig davon, daß das Auftauchen von TextNodes, scheinbar durch das Löschen von Elementen als temporäre Leichen verbleibend, doch irritierend ist:

      'use strict';
      (
      	function() {
      		const theBody = new DocumentFragment();
      		for (const mains of document.getElementsByTagName('main')) {
      			theBody.append(...mains.childNodes);
      		}
      
      		{
      			const killer = document.createNodeIterator(
      				theBody,
      				NodeFilter.SHOW_ELEMENT,
      				x => /^(?:img|script|style|iframe|form)$/i.test(x.nodeName)
      				?	NodeFilter.FILTER_ACCEPT
      				:	NodeFilter.FLITER_SKIP
      			);
      			while (killer.nextNode()) killer.referenceNode.remove();
      		}
      		{
      			const cWalker = document.createNodeIterator(
      				theBody,
      				NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT
      			);
      			while (cWalker.nextNode()) {
      				const x = cWalker.referenceNode;
      				switch (x.nodeType) {
      					case Node.TEXT_NODE: {
      						const s = x.textContent.trim();
      						if (s.length) { x.textContent = s; break; }
      						//  falls through
      					}
      					case Node.COMMENT_NODE:
      						x.remove();
      						break;
      					default:
      						console.log(x.nodeType);
      				}
      			}
      
      /*!*/		theBody.normalize(); // egal, ob mit oder ohne das!
      			const empties = theBody.querySelectorAll(':empty');
      			for (const empty of empties) empty.remove();
      
      		}
      
      		document.body.replaceChildren(theBody);
      	}
      )();
      
      

      als User-Script mit Tampermonkey (jetzt mal auf // @match https://www.westlotto.de/lotto-6aus49/gewinnzahlen/*angesetzt) eingesetzt, läßt die roten Rechtecke immer noch stehen. Nur wenn das hier weggelassene Entfernen von Attributen und Klassen durchgezogen wird, paßt das „Empty-Ergebnis“.

      Werd’ noch etwas herumstochern um herauszufinden, was entfernt werden muß und was nur kann, denn etwas von dem Zeug wäre schon hilfreich — oder ich werte es vorher entsprechend aus.

      1. problematische Seite

        Hallo,

        ist klar, was passiert. Auch wenn ich es selbst erstmal ausprobieren musste, um drauf zu kommen. Mit Attributen hat das eher nichts zu tun, sondern mit Rekursivität.

        Das Ziehungsvideo ist besonders toxisch:

        <div class="mod mod-video">
          <div id="video-container-lotto" class="mod-video__player"></div>
          <div id="video-poster-lotto" class="mod-video__poster">
            <div class="copy-container">
              <button type="button" id="start-video-lotto" class="btn btn--hero play-btn">
                <span class="icon icon--arrow_right" aria-hidden="true"></span>
              </button>
            </div>
            <img src="..." alt="" class="poster cmplazyload" data-cmp-src="...">
          </div>
        </div>
        

        Wenn ich das durchlaufe, textNodes trimme, leere textNodes entferne und dann :empty Elemente rauswerfe, fliegen #video-container-lotto, und der span im Button raus. Das img hast Du ja vorher schon eliminiert.

        Aber danach ist der button :empty. Der muss also auch raus. Und danach ist .copy-container :empty. Der muss also auch raus. Und danach ist #video-poster-lotto :empty. Ad nauseam.

        Vielleicht hast Du erwartet, dass diese neuen :emptys automatisch in das querySelectorAll-Ergebnis hineinkommen. Aber das tun sie nicht, die Nodelist, die Du davon bekommst, ist ein statischer Schnappschuss zum Zeitpunkt des Aufrufs. Und das ist auch gut so, denn andernfalls hättest Du das Problem, dass Du Dir mit dem Löschen die Iteration zerstörst (modifiziere nie die Collection, die Du gerade iterierst).

        Du musst die beiden Zeilen

          const empties = theBody.querySelectorAll(':empty');
          for (const empty of empties) empty.remove();
        

        so oft durchführen, bis Du nichts mehr findest. Leider gibt's kein :empty-all oder so, das rekursiv arbeitet.

          while(true) {
            const empties = theBody.querySelectorAll(':empty');
            if (empties.length == 0)
              break;
            for (const empty of empties) empty.remove();
           }
        

        Die Frage ist nur, ob Dich die empty-Nodes so stören, dass Du Dir diese Arbeit wirklich machen musst…

        Rolf

        --
        sumpsi - posui - obstruxi
        1. problematische Seite

          Vielen Dankt für die Antwort! Zwar hab’ ich da jetzt noch nichts probiert (brauch’ wohl erst mal etwas Abstand, seh’ vor lauter Rauch aus dem Kopf nichts mehr …), aber das hört sich doch plausibel an!

          Und: verflixt! Irgendwo und wann ist mir da dann auch noch mein

          for (let rumble = true; rumble; ) {
          			rumble = false;
          …
          }
          

          abhanden gekommen.

          Wahrscheinlich werde ich, mit ein paar neuen Kenntnissen im Kopf, neu anfangen. Das ganze Herumstochern hat doch ettliche Scherbenhaufen hinterlassen …

      2. problematische Seite

        Hallo,

        by the way: du hast Glück, dass deine remove()-Aufrufe auf den Nodes, die der Node-Iterator gerade im Zugriff hat, die Iteration nicht abschießen. Wie gesagt: Modifiziere während der Iteration nie die iterierte Collection, die Chancen stehen gut, dass dann Elemente nicht verarbeitet werden oder der Iterator abstürzt - das ist eine sprachunabhängige Weisheit. In C#.net, meiner Muttersprache“, ist es sogar so, dass .net das erkennt und das Programm mit einer Exception abbricht.

        Als ich deinen Code sah, war mein erster Gedanke, dass das der Fehler sein müsste. War es nicht, JavaScript scheint im nextElement() Aufruf bereits das Folgeelement zu ermitteln, aber trotzdem - es ist grundsätzlich riskant und sollte vermieden werden.

        Empfehlung aus 40 Jahren Berufserfahrung: die zu löschenden Elemente zunächst in einem Array sammeln und sie erst nach Abschluss der Iteration löschen.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. problematische Seite

          Was das angeht, da werde ich vmlt. ganz zu TreeWalker und NodeIterator zurück(!)kehren. Irgendwo in den Texten (W3 o.ä.) steht ja drin, daß die so etwas vertragen (müßten): die Markirungen bei den verschiedenen Bewegungs-Funktionen (nextNode, previousNode, aber auch z.B. die …Sibling() liegen zwischen den Elementen und auch der Wechsel in den Bewegungsrichtungen, steht da geschrieben, wird berücksichtigt.

          Ah, da war’s!, “7.1.1.2. Robustness”.