Nehmen wir an, wir möchten die folgende HTML-Struktur haben
<div class='boo'>
<div class='boo'>
<div class='boo'>
<div class='boo'>
<div class='boo'></div>
</div>
</div>
</div>
</div>
Das ist wirklich mühsam, manuell zu schreiben. Und der Grund, warum dieser Beitrag entstanden ist, war die Entrüstung, als ich sah, wie er mit Haml so generiert wurde
.boo
.boo
.boo
.boo
.boo
Im Code, den ich sah, gab es tatsächlich etwa zwanzig Verschachtelungsebenen, aber vielleicht lesen einige Leute auf einem Mobiltelefon, also füllen wir nicht den gesamten Viewport mit „Boo“, auch wenn Halloween naht.
Wie Sie wahrscheinlich erkennen können, ist das manuelle Ausschreiben jeder Ebene alles andere als ideal, insbesondere wenn die HTML von einem Präprozessor (oder von JavaScript oder sogar einer serverseitigen Sprache wie PHP) generiert wird. Ich persönlich bin kein Fan von tiefer Verschachtelung und verwende sie nicht oft selbst, aber wenn Sie es dennoch tun, denke ich, dass es sich lohnt, dies auf eine Weise zu tun, die gut skalierbar und leicht zu warten ist.
Betrachten wir also zuerst einige bessere Lösungen für diesen Basisfall und seine Variationen, und sehen Sie sich dann einige unterhaltsame Dinge an, die mit dieser Art von tiefer Verschachtelung gemacht werden!
Die Basislösung
Was wir hier brauchen, ist ein rekursiver Ansatz. Zum Beispiel macht mit Haml das folgende Code-Snippet den Trick
- def nest(cls, n);
- return '' unless n > 0;
- "<div class='#{cls}'>#{nest(cls, n - 1)}</div>"; end
= nest('👻', 5)
Darin gibt es eine Emoji-Klasse, weil wir das können und weil dies nur ein kleines unterhaltsames Beispiel ist. Ich würde definitiv keine Emoji-Klassen auf einer tatsächlichen Website verwenden, aber in anderen Situationen spiele ich gerne ein bisschen mit dem Code, den ich schreibe.
Wir können das HTML auch mit Pug generieren
mixin nest(cls, n)
div(class=cls)
if --n
+nest(cls, n)
+nest('👻', 5)
Dann gibt es noch die JavaScript-Option
function nest(_parent, cls, n) {
let _el = document.createElement('div');
if(--n) nest(_el, cls, n);
_el.classList.add(cls);
_parent.appendChild(_el)
};
nest(document.body, '👻', 5)
Mit PHP können wir etwas Ähnliches verwenden
<?php
function nest($cls, $n) {
echo "<div class='$cls'>";
if(--$n > 0) nest($cls, $n);
echo "</div>";
}
nest('👻', 5);
?>
Beachten Sie, dass der Hauptunterschied zwischen dem, was jede dieser erzeugt, mit Formatierung und Leerzeichen zusammenhängt. Das bedeutet, dass das Anvisieren des innersten „Boo“ mit .👻:empty für das mit Haml, JavaScript und PHP generierte HTML funktioniert, aber für das mit Pug generierte fehlschlägt.
Hinzufügen von Ebenenindikatoren
Nehmen wir an, wir möchten, dass jedes unserer „Boo“ einen Ebenenindikator als benutzerdefinierte Eigenschaft --i hat, die dann beispielsweise verwendet werden könnte, um jedem von ihnen einen anderen background zu geben.
Sie denken vielleicht, wenn wir nur die Farbton ändern wollen, dass wir das mit filter: hue-rotate() tun können und ohne Ebenenindikatoren auskommen. hue-rotate() beeinflusst jedoch nicht nur den Farbton, sondern auch die Sättigung und Helligkeit. Es bietet auch nicht das gleiche Maß an Kontrolle wie die Verwendung unserer eigenen benutzerdefinierten Funktionen, die von einem Ebenenindikator --i abhängen.
Dies ist zum Beispiel etwas, das ich in einem kürzlich durchgeführten Projekt verwendet habe, um background-Komponenten reibungslos von Ebene zu Ebene ändern zu lassen (die $c-Werte sind Polynomkoeffizienten)
--sq: calc(var(--i)*var(--i)); /* square */
--cb: calc(var(--sq)*var(--i)); /* cube */
--hue: calc(#{$ch0} + #{$ch1}*var(--i) + #{$ch2}*var(--sq) + #{$ch3}*var(--cb));
--sat: calc((#{$cs0} + #{$cs1}*var(--i) + #{$cs2}*var(--sq) + #{$cs3}*var(--cb))*1%);
--lum: calc((#{$cl0} + #{$cl1}*var(--i) + #{$cl2}*var(--sq) + #{$cl3}*var(--cb))*1%);
background: hsl(var(--hue), var(--sat), var(--lum));
Das Tuning von Pug zum Hinzufügen von Ebenenindikatoren sieht wie folgt aus
mixin nest(cls, n, i = 0)
div(class=cls style=`--i: ${i}`)
if ++i < n
+nest(cls, n, i)
+nest('👻', 5)
Die Haml-Version ist auch nicht viel anders
- def nest(cls, n, i = 0);
- return '' unless i < n;
- "<div class='#{cls}' style='--i: #{i}'>#{nest(cls, n, i + 1)}</div>"; end
= nest('👻', 5)
Mit JavaScript haben wir
function nest(_parent, cls, n, i = 0) {
let _el = document.createElement('div');
_el.style.setProperty('--i', i);
if(++i < n) nest(_el, cls, n, i);
_el.classList.add(cls);
_parent.appendChild(_el)
};
nest(document.body, '👻', 5)
Und mit PHP sieht der Code so aus
<?php
function nest($cls, $n, $i = 0) {
echo "<div class='$cls' style='--i: $i'>";
if(++$i < $n) nest($cls, $n, $i);
echo "</div>";
}
nest('👻', 5);
?>
Eine baumähnlichere Struktur
Nehmen wir an, wir möchten, dass jedes unserer „Boo“ zwei „Boo“-Kinder hat, für eine Struktur, die so aussieht
.boo
.boo
.boo
.boo
.boo
.boo
.boo
.boo
.boo
.boo
.boo
.boo
.boo
.boo
.boo
Glücklicherweise müssen wir unseren Pug-Mixin nicht wesentlich ändern, um das zu erreichen (Demo)
mixin nest(cls, n)
div(class=cls)
if --n
+nest(cls, n)
+nest(cls, n)
+nest('👻', 5)
Das Gleiche gilt für die Haml-Version
- def nest(cls, n);
- return '' unless n > 0;
- "<div class='#{cls}'>#{nest(cls, n - 1)}#{nest(cls, n - 1)}</div>"; end
= nest('👻', 5)
Die JavaScript-Version erfordert etwas mehr Aufwand, aber nicht zu viel
function nest(_parent, cls, n) {
let _el = document.createElement('div');
if(n > 1) {
nest(_el, cls, n - 1);
nest(_el, cls, n - 1)
}
_el.classList.add(cls);
_parent.appendChild(_el)
};
nest(document.body, '👻', 5)
Mit PHP müssen wir die Funktion nest() nur einmal mehr im if-Block aufrufen
<?php
function nest($cls, $n) {
echo "<div class='$cls'>";
if(--$n > 0) {
nest($cls, $n);
nest($cls, $n);
}
echo "</div>";
}
nest('👻', 5);
?>
Das Top-Level-Element anders stylen
Wir könnten natürlich eine spezielle Klasse .top (oder .root oder etwas Ähnliches) nur für die oberste Ebene hinzufügen, aber ich ziehe es vor, dies dem CSS zu überlassen
:not(.👻) > .👻 {
/* Top-level styles*/
}
Vorsicht!
Einige Eigenschaften wie transform, filter, clip-path, mask oder opacity wirken sich nicht nur auf ein Element aus, sondern auch auf alle seine Nachkommen. Manchmal ist dies der gewünschte Effekt und genau der Grund, warum die Verschachtelung dieser Elemente gegenüber Geschwistern bevorzugt wird.
Manchmal ist dies jedoch nicht das, was wir wollen, und obwohl es möglich ist, die Auswirkungen von transform und manchmal sogar filter umzukehren, können wir bei den anderen nichts tun. Wir können zum Beispiel nicht opacity: 1.25 auf ein Element setzen, um seine Eltern mit opacity: .8 zu kompensieren.
Beispiele!
Zuerst haben wir diesen reinen CSS-Punktlader, den ich kürzlich für eine CodePen-Herausforderung erstellt habe
Hier addieren sich die Effekte der Skalierungstransformationen und der animierten Rotationen auf den inneren Elementen, ebenso wie die Deckkraft.
Als Nächstes kommt dieser Yin-und-Yang-Tanz, der die baumähnliche Struktur verwendet
Für jedes Element, außer dem äußersten (:not(.☯️) > .☯️), ist der Durchmesser gleich der Hälfte des Durchmessers seines Elternelements. Für die innersten Elemente (.☯️:empty, die wir wohl als Baumblätter bezeichnen können), hat der background zwei zusätzliche radial-gradient()-Schichten. Und genau wie beim ersten Demo addieren sich die Effekte der animierten Rotationen auf den inneren Elementen.
Ein weiteres Beispiel wären diese sich drehenden Bonbon-Tentakel
Jeder der konzentrischen Ringe stellt eine Verschachtelungsebene dar und kombiniert die Effekte der animierten Rotationen all seiner Vorfahren mit seinen eigenen.
Schließlich haben wir diese Demo mit dreieckigen Öffnungen (beachten Sie, dass sie einzelne Transformationseigenschaften wie rotate und scale verwendet, daher muss das Flag **Experimental Web Platform features** in chrome://flags aktiviert sein, um es in Chromium-Browsern zu sehen
Dies verwendet eine leicht modifizierte Version des grundlegenden Verschachtelungs-Mixins, um jedem Level auch eine color zuzuweisen
- let c = ['#b05574', '#f87e7b', '#fab87f', '#dcd1b4', '#5e9fa3'];
- let n = c.length;
mixin nest(cls, n)
div(class=cls style=`color: ${c[--n]}`)
if n
+nest(cls, n)
body(style=`background: ${c[0]}`)
+nest('🔺', n)
Animiert werden hier die einzelnen Transformationseigenschaften scale und rotate. Dies geschieht, damit wir ihnen unterschiedliche Timing-Funktionen zuweisen können.
Ich habe aufgehört zu lesen, als ich gesehen habe, dass Sie in PHP-Code doppelte Anführungszeichen verwenden.
Hey, das war ziemlich cool! Nur eine Anmerkung, Sie haben eine Endlosschleife in Ihrer JavaScript-Funktion, wenn Sie den zweiten Verschachtelungsaufruf hinzufügen
Behoben, danke!
Obwohl ich Pug schon einmal benutzt habe, ist mir irgendwie nicht eingefallen, wie sehr man repetitives SVG-Markup vereinfachen kann. Aber sobald die Idee in meinem Kopf ist...
Eines dieser Tage werden wir einen Code-Golf-Kampf CSS gegen SVG machen müssen. Kreativität und Coding-Zeit, Sie werden definitiv gewinnen. Aber bei der Anzahl der Zeilen fordere ich Sie heraus.
Für den Spinner
Sie in reinem CSS haben 6 Zeilen Pug und 32 Zeilen SCSS
Ich in reinem SVG habe 20 Zeilen Pug und 9 Zeilen Sass
Siehe den Pen
jOrMjLW von ccprog (@ccprog)
auf CodePen.
Das ist ohne präzise Regeln. Ich denke, wir beide folgen "keine Minifizierung, keine Zeilen länger als 80 Zeichen".
Ich habe gerade versucht, eine Regeln für Web-Dev-Code-Golf zu entwerfen: Sandbox für vorgeschlagene Herausforderungen
Ich vermute, dass die meisten von Ihnen diese StackExchange-Community nicht gesehen haben, aber ich gehe dorthin wegen der Spezialisten für die Formulierung guter Regeln, unabhängig vom Sprachhintergrund. Ich würde mich aber über Input von der breiteren Frontend-Community freuen. Da der Vorschlag in einer Antwort liegt, sind Verbesserungen wahrscheinlich in den Kommentaren zu erwarten. Eine Beschreibung, worum es in dieser Sandbox geht, finden Sie in der Frage.
Das ist eine interessante Idee.
Ich denke, wenn ich mich für SVG entscheiden würde, würde ich mich für so etwas wie das entscheiden, damit ich auch im kompilierten Code keine Wiederholung habe*. Ich schätze, es ist etwas mehr Code zu schreiben als in Ihrem Beispiel, aber es resultiert in einem deutlich kleineren SVG (super frustrierend, dass
pathLengthnicht funktioniert, es sei denn, ich setze es auf jedencircle, weil das eine Quelle der Wiederholung ist, die meine Netzhaut wirklich kratzt).*Ironischerweise habe ich angefangen, mich stärker auf Pug zu verlassen, um den kompilierten Code zu vereinfachen, sobald CSS-Variablen eingeführt wurden. Der Präprozessor-Code war bereits ziemlich kompakt, da das Schleifen das war, was mich 2013 überhaupt erst dazu brachte, HTML- und CSS-Präprozessoren zu verwenden, aber mit Variablen konnte ich eine Reihe von Elementen mit Pug generieren, ihnen einen Index als Inline-Benutzereigenschaft zuweisen und dann CSS-Eigenschaftswerte von diesen Indizes abhängig machen, anstatt einen
nth-child-Block für jedes der Elemente aus Sass zu generieren.Wenn Sie auch die kompilierte Länge berücksichtigen, hier ist eine optimierte, gemischte Version. Ich gebe zu, die CSS-Transformationssyntax ist kürzer, also habe ich sie integriert und die Verwendung von
<use>weiter optimiert.Die neuen Zahlen sind (keine Minifizierung für die Bytes angewendet)
Ihre: Pug 6 Zeilen, SCSS 32 Zeilen, **Summe: 40 Zeilen**
Kompiliertes HTML 129 Bytes, CSS 1744 Bytes, **Summe: 1873 Bytes**
Meine, erste Version: Pug 20 Zeilen, Sass 9 Zeilen, **Summe: 29 Zeilen**
Kompiliertes HTML 2563 Bytes, CSS 127 Bytes, **Summe: 2690 Bytes**
Mix: Pug 15 Zeilen, CSS 14 Zeilen, **Summe: 29 Zeilen**
Kompiliertes HTML 736 Bytes, CSS 266 Bytes, **Summe: 1002 Bytes**
Es tut mir sehr leid, ich habe die zweite Version von Ihnen übersehen
Ihr reines SVG: 17 Zeilen + 12 Zeilen = 29 Zeilen
861 Bytes + 219 Bytes = 1080 Bytes
Wird es Probleme mit älteren Browsern geben?
Wurde Emmet nicht dafür entwickelt? Was vermisse ich?
Ich verstehe definitiv nicht, warum Sie Emmet nicht verwendet haben.
In VSCode gibt es ein großartiges Beispiel (angenommen, einige Leser hier sind Anfänger oder verwenden immer noch Notepad++), in einer Emmet Live-Erweiterung. Sie könnten nur einen Satz schreiben, Tab drücken und haben bereits Ihren vollständigen Code in HTML erhalten. Keine Notwendigkeit für irgendein PHP. Dinge sind bereits einfach gemacht, warum das Rad neu erfinden?
Abgesehen davon sind Ihre Animationen wirklich schön;) Sie haben ein schönes Beispiel gegeben, für das Sie einen Daumen nach oben verdienen!
Danke!
Zuerst einmal, weil ich nicht einmal sicher bin, was Emmet ist. Als ich zum ersten Mal Leute darüber sprechen hörte, dachte ich, es sei ein Texteditor, aber jetzt glaube ich, es ist eine Erweiterung, die man zu verschiedenen IDEs hinzufügen kann, basierend auf dem, was Sie hier schreiben? Jedenfalls nicht mein Ding.
Zum einen, ich und IDEs, wir kommen nicht miteinander aus. Wir kamen nicht miteinander aus, als ich C/C++, Java, PHP, Lisp und so weiter schrieb… wir kommen immer noch nicht miteinander aus und werden es auch nicht tun. Während des Jahrzehnts 2002-2011 hatte ich Post-its auf meinem Monitor mit Anweisungen von meinen versierteren Freunden und kam immer noch kaum durch (Übersetzung: Ich war nie gut und habe nie viel geschafft).
Es war lebensverändernd vor einem Jahrzehnt, Online-Spielplätze zu entdecken, wo man nur Code in eine Box schreiben und auf einen Ausführungsbutton klicken musste, um das Ergebnis seines Codes zu sehen. Kein Warten mehr von drei Wochen, bis jemand endlich herausfindet, was man mit der verdammten IDE falsch gemacht hat und einen am Laufen hindert. Kein Anblick mehr von einer Zillionen von Knöpfen, Menüs und Optionen, die einen verwirren. Keine Dinge, die erscheinen/verschwinden/sich auf irgendeine andere Weise verschlimmern, weil man versehentlich eine Taste oder Tastenkombination gedrückt hat, von der man nicht einmal weiß, was sie war oder wie man sie rückgängig macht, ohne seine Arbeit zu verlieren.
Ich benutze vielleicht manchmal den einfachen Texteditor, der bei Ubuntu dabei ist, aber auch der hat den Vorteil, keine verrückten Funktionen zu haben, die mir nur im Weg wären.
Wie auch immer, soweit ich das verstehe, meinen Sie, warum keine Erweiterung verwenden, die den vollständig generierten verschachtelten HTML-Code in den Code einfügt? Das möchte ich nicht sehen. In dem Code, mit dem ich arbeite, sehe ich lieber einen rekursiven Aufruf anstatt dessen, was das generiert und einfach dort platziert wird. Es ist leichter zu verstehen, der vertikale Platz auf meinem Bildschirm ist begrenzt und ich finde es wirklich verwirrend, durch meinen Code scrollen zu müssen, daher gilt: je kürzer, desto besser.
Was die Animation angeht, da kratze ich mir am Kopf, da Animation das trivialste an diesen Demos ist. Außer vielleicht beim letzten, wo die Dinge dadurch etwas komplizierter werden, dass kein Browser zu diesem Zeitpunkt CSS-trigonometrische Funktionen unterstützt, aber ich dachte mir, da ich trigonometrische Funktionen verwendet habe, um CSS-Timing-Funktionen aus dem JS zu emulieren, könnte ich das Gegenteil in CSS tun.