
🎯 Selektoren in Elementor-Widgets: vollständiger Leitfaden für Entwickler
Warum Elementor-Entwickler Selektoren benötigen und wie sie funktionieren
Wenn ein Nutzer im Editor Widget-Einstellungen anpasst, erwartet er eine sofortige Reaktion auf dem Bildschirm. Ohne Selektoren müsste ein Entwickler für jede Feldänderung einen JS-Handler schreiben. Mit Selektoren wird alles über CSS gelöst.
Der Parameter selectors (und sein weniger bekannter Gegenpart selectors_dictionary) wird direkt im Array des add_control()-Aufrufs eingebettet. Elementor ersetzt dynamisch Werte aus Feldern in CSS-Regeln und gibt sie in die Post-Datei aus, etwa /wp-content/uploads/elementor/css/post-1234.css. Sobald der Nutzer den Editor verlässt, verschwinden die Inline-Styles und sauber generiertes CSS bleibt zurück.
💡 Kurzer Überblick:
- Selektoren-Syntax und die Platzhalter-Tabelle verstehen
- Live-Beispiele für Farbe und Größen sehen
- Lernen, Werte aus benachbarten Controls zu beziehen
- selectors_dictionary für die Substitution von CSS-Deklarationen beherrschen
- Das Puzzle aus CSS-Variablen und versteckten Controls zusammensetzen
Wo Selektoren definiert werden
Wenn Sie ein Widget erstellen, akzeptiert jeder add_control()-Aufruf ein Einstellungs-Array. Genau dort leben die selectors. Bei Gruppen-Controls ist die Syntax identisch, das Array wird innerhalb der Gruppenregistrierung übergeben.
Grundformat:
1 'selectors' => [ 2 '{{WRAPPER}} .my-widget-class' => 'color: {{VALUE}}', 3 ]
Der Schlüssel ist ein CSS-Selektor (beginnt mit {{WRAPPER}}, um benachbarte Widgets auf der Seite nicht zu beeinflussen). Der Wert besteht aus einer oder mehreren CSS-Deklarationen mit dynamischen Platzhaltern. Elementor nimmt den aktuellen Control-Wert und setzt ihn anstelle des Platzhalters ein.
Das Ergebnis wird in die externe CSS-Datei des Posts geschrieben, die Styles existieren nur, solange der Editor geöffnet ist und unmittelbar nach dem Speichern. Kein Inline-Chaos.
Variablentabelle mit geschweiften Klammern
Keine Magie, nur Suchen und Ersetzen. Aber die Vielfalt der Platzhalter öffnet Türen zu recht raffinierten Konstruktionen.
Für Selektoren (Array-Schlüssel)
Platzhalter | Was er ersetzt |
|---|---|
| Eindeutiger Widget-Instanz-Selektor, zum Beispiel |
| Nur die Widget-ID (der Teil nach dem Bindestrich, |
| Beschränkt die Regel auf das angegebene Gerät. Mit |
| Aktives Element eines Repeater-Controls |
Für Deklarationen (Array-Wert)
Platzhalter | Was er ersetzt |
|---|---|
| Roher Control-Wert. Kann durch |
| Zahl und Maßeinheit aus numerischen Controls. Treten üblicherweise paarweise auf: |
| Richtungen aus dem Dimensions-Control |
| Zugriff auf benannte Eigenschaften zusammengesetzter Controls: Das Media Control liefert beispielsweise ein Array mit den Feldern |
| Wert eines anderen Controls anhand der ID. Suffixe |
| Fallback: Ist das Control leer, wird |
Einfache Beispiele, von Farbe bis Hintergrundbild
Farbe aus der Palette. Nichts weiter:
1 'selectors' => [ 2 '{{WRAPPER}} .elementor-svg-divider-basic-text' => 'color: {{VALUE}}', 3 ],
Numerisches Control mit und ohne Einheit. Die zweite Eigenschaft (stroke-width) steht bewusst ohne {{UNIT}}, die Strichstärke ist in Pixeln, ohne px:
1 'selectors' => [ 2 '{{WRAPPER}} svg.sde-classic' => 3 'height: {{SIZE}}{{UNIT}}; stroke-width: {{SIZE}};', 4 ],
Abstände aus dem Dimensions-Control, jede Richtung separat:
1 'selectors' => [ 2 '{{WRAPPER}} .elementor-svg-divider-basic-button' => 3 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', 4 ],
Hintergrundbild eines Slides im Repeater-Control:
1 'selectors' => [ 2 '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-bg' => 3 'background-image: url({{URL}})', 4 ],
Bedingte Positionierung für RTL. Dasselbe Control liefert je nach Textrichtung unterschiedliche Eigenschaften:
1 'selectors' => [ 2 'body:not(.rtl) {{WRAPPER}} .dialog-close-button' => 'right: {{SIZE}}{{UNIT}}', 3 'body.rtl {{WRAPPER}} .dialog-close-button' => 'left: {{SIZE}}{{UNIT}}', 4 ],
Wie man einen Wert aus einem anderen Control bezieht
Wenn zwei Felder dasselbe CSS beeinflussen, duplizieren Sie nicht das Array, sondern referenzieren Sie einfach das benachbarte Control:
1 'selectors' => [ 2 '{{WRAPPER}} svg.sde-classic' => 3 'stroke-dasharray: {{dash_length.SIZE}} {{whitespace_length.SIZE}};', 4 ],
Hier sind dash_length und whitespace_length IDs anderer Controls im selben Widget. Keine zusätzlichen Aufrufe, nur Punktnotation.
Responsive Version, Werte werden geräteabhängig abgerufen. Praxisbeispiel aus Elementor Pro:
1 'selectors' => [ 2 '(desktop).elementor-msie {{WRAPPER}} .elementor-portfolio-item' => 3 'width: calc( 100% / {{columns.SIZE}} ); border: {{SIZE}}px solid transparent', 4 '(tablet).elementor-msie {{WRAPPER}} .elementor-portfolio-item' => 5 'width: calc( 100% / {{columns_tablet.SIZE}} ); border: {{SIZE}}px solid transparent', 6 '(mobile).elementor-msie {{WRAPPER}} .elementor-portfolio-item' => 7 'width: calc( 100% / {{columns_mobile.SIZE}} ); border: {{SIZE}}px solid transparent', 8 ],
Jeder Breakpoint erhält seinen eigenen columns-Wert. Die übrigen Eigenschaften (border, SIZE) sind gemeinsam, sie sind nicht an das Gerät gebunden.
Selectors_dictionary, switch-case für CSS
Die am meisten unterschätzte Fähigkeit. selectors_dictionary ersetzt {{VALUE}} durch eine fest codierte Zeichenkette und macht den Steuerungswert damit praktisch zu einem Dictionary-Schlüssel.
Nehmen Sie das standardmäßige Align-Control mit den Optionen left/center/right. Ohne Dictionary würden Sie etwas Unnatürliches schreiben:
1 'selectors' => [ 2 $sde_selector => 'margin: 0 auto; margin-{{VALUE}}: 0;', 3 ],
Für center erzeugt das margin: 0 auto; margin-center: 0;. Die Eigenschaft margin-center existiert nicht, der Browser ignoriert sie stillschweigend. Aber es sieht unsauber aus.
Das Dictionary erledigt dasselbe auf saubere Weise:
1 'selectors_dictionary' => [ 2 'left' => 'margin-right: auto', 3 'center' => 'margin: 0 auto', 4 'right' => 'margin-left: auto', 5 ], 6 'selectors' => [ 7 '{{WRAPPER}} .sde' => '{{VALUE}}', 8 ],
Control-Wert center → {{VALUE}} wird zu margin: 0 auto. Das ist alles.
Wichtige Einschränkung: Nach der Aktivierung von selectors_dictionary verlieren Sie das ursprüngliche {{VALUE}}. Enthält dasselbe Array ein weiteres Selektor-Deklaration-Paar, das den ursprünglichen Wert benötigt, erhält es die bereits ersetzte Zeichenkette. Hier ein problematisches Beispiel:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => '{{VALUE}}', 3 '{{WRAPPER}}.elementor-sde-scale-the-cropped .sde-cropping-allow .sde' => 4 'transform-origin: {{VALUE}} 0;', 5 ],
Hier erhält transform-origin den Wert margin: 0 auto 0; anstelle von center 0;. Lösung: Lagern Sie abhängige Deklarationen in ein separates Control aus.
Das Dictionary bewältigt auch die Übersetzung einzelner CSS-Werte gut:
1 'selectors_dictionary' => [ 2 'top' => 'flex-start', 3 'middle' => 'center', 4 'bottom' => 'flex-end', 5 ], 6 'selectors' => [ 7 '{{WRAPPER}} .elementor-price-table__currency' => 'align-self: {{VALUE}}', 8 ],
Und sogar mit ganzen Deklarationssätzen, ein Schlüssel → mehrere CSS-Eigenschaften:
1 'selectors_dictionary' => [ 2 'left' => 'right: auto; left: 0', 3 'right' => 'left: auto; right: 0', 4 ], 5 'selectors' => [ 6 '{{WRAPPER}}.elementor-wc-products ul.products li.product span.onsale' => '{{VALUE}}', 7 ],
CSS-Variablen, calc() und versteckte Controls, das Puzzle zusammensetzen
Die wahre Stärke der Selektoren zeigt sich in der Kombination. Ein Control setzt eine CSS-Variable, ein anderes referenziert sie, ein drittes aktiviert oder deaktiviert einen ganzen Regelblock über eine Bedingung.
Der Scale%-Schieberegler schreibt eine Variable:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => '--sde-scale-percentage: {{SIZE}};', 3 ],
Der Toggle „Scale cropped" verwendet diese Variable an zwei Stellen, sowohl für transform als auch zur Weitergabe an das Gap-Control:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => 3 'transform: scale(var(--sde-scale-percentage)) scale(0.01); 4 --sde-scale-pct-for-gap: var(--sde-scale-percentage);', 5 ],
Verstecktes Control mit Bedingung, dasselbe transform, aber mit einem anderen Selektor (für den unbeschnittenen Zustand):
1 'condition' => [ 2 'scale_the_cropped!' => 'cropped', 3 ], 4 'selectors' => [ 5 '{{WRAPPER}} .sde svg' => 6 'transform: scale(var(--sde-scale-percentage)) scale(0.01);', 7 ],
Und das Gap-Control verwendet die übergebene Variable mit Fallback:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => 3 'padding: calc({{SIZE}}{{UNIT}} / (var(--sde-scale-pct-for-gap, 100) / 100)) 0;', 4 ],
Was hier geschieht: Gap kompensiert die Skalierung. Wird ein Element auf die Hälfte verkleinert, wird der Abstand mit 2 multipliziert, um optisch gleich zu bleiben. Ohne Verkleinerung (Variable nicht gesetzt) greift der Fallback-Wert 100 → Division durch 1 → Abstand ändert sich nicht. Reine CSS-Mathematik, ohne eine einzige Zeile JS.
Transform-Stacking, Workaround für Edge
Die Konstruktion scale(X) scale(0.01) verdient besondere Erwähnung. Warum nicht scale(calc(var(--sde-scale-percentage) / 100))? Weil Edge calc() innerhalb von transform nicht unterstützt. Überhaupt nicht.
Die Lösung: Stacking. Browser wenden Transform-Funktionen sequenziell an, eine nach der anderen. Daher:
1 transform: scale(var(--sde-scale-percentage)) scale(0.01);
Mathematisch äquivalent zu scale(var(--sde-scale-percentage) * 0.01), also einer Division durch 100. Der Nutzer erhält einen vertrauten Schieberegler von 0 bis 100, während der Wert im Hintergrund in einen Koeffizienten von 0 bis 1 umgewandelt wird.
Dasselbe Prinzip gilt für andere Transformationen: Rotate, Translate, Skew, und funktioniert in allen modernen Browsern, einschließlich Edge.
⁉️🤔 Häufig gestellte Fragen
Was genau landet in der generierten CSS-Datei?
Elementor sammelt alle Selektoren aus den registrierten Widget-Controls, setzt die aktuellen Werte aus den Benutzereinstellungen ein und schreibt das Ergebnis nach
/wp-content/uploads/elementor/css/post-XXXX.css. Das sind keine Inline-Styles und kein dynamisches CSS zur Laufzeit: Es handelt sich um eine statische Datei, die vom Browser gecacht wird und bis zur nächsten Einstellungsänderung im Editor bestehen bleibt. Die Platzhalter selbst{{VALUE}}{{SIZE}}und andere sind weder Teil von WordPress noch der Blade-Template-Engine: Elementor führt während der CSS-Generierung ein reguläresstr_replacedurch, iteriert über alle Selektor-Deklarations-Paare und ersetzt die Token durch die tatsächlichen Control-Werte.
Wie debugge ich Selektoren, wenn das CSS nicht greift?
Öffnen Sie die generierte CSS-Datei des Beitrags (der Pfad ist im Seitenquelltext sichtbar) und prüfen Sie, ob die Regel vorhanden ist. Fehlt die Regel, suchen Sie nach einem Tippfehler in der Control-ID oder einem Syntaxfehler im
selectors-Array. Ist die Regel vorhanden, funktioniert aber nicht, prüfen Sie die Selektorenspezifität:{{WRAPPER}}sorgt für hohe Priorität, aber verschachtelte Themes können durch!importantüberschreiben. Aktivieren SieWP_DEBUGund beobachten Sie die PHP-Logs: Elementor überspringt fehlerhafte Arrays stillschweigend, ohne Fehler auf dem Bildschirm anzuzeigen. Verwenden Sie{{WRAPPER}}IMMER, außer in Fällen, in denen Sie gezielt body oder html ansprechen möchten.
Wie unterscheiden sich Selektoren von benutzerdefiniertem CSS in den Widget-Einstellungen?
Benutzerdefiniertes CSS (Tab „Erweitert") wird manuell vom Benutzer geschrieben, das sind statische Regeln, die nicht auf Einstellungsänderungen reagieren. Selektoren verknüpfen Controls dynamisch mit CSS: Sie bewegen den Schieberegler,
widthändert sich; Sie schalten die Ausrichtung um,marginwird neu aufgebaut. Der Benutzer sieht diesen Mechanismus nicht, er erhält lediglich eine Live-Vorschau. Für den Entwickler liegt der Hauptgewinn im Wegfall der_content_template()-Methode: Ohne Selektoren müssten Sie für jedes Control ein JS-Vorschau-Rendering schreiben.
Brauche ich selectors_dictionary, wenn ich bereits selectors verwende?
Ja, für einen qualitativen Sprung bei der Code-Sauberkeit. Ohne Dictionary verarbeiten Sie den Control-Wert implizit, über seltsame CSS-Eigenschaften wie das nicht existierende
margin-center, das der Browser ignoriert. Mit einem Dictionary legen Sie explizit fest: „Wenn der Wert left ist, setze margin-right: auto, wenn center, dann margin: 0 auto". Der Code wird selbstdokumentierend, und vor allem schleppt{{VALUE}}den ursprünglichen Control-Wert nicht mehr in andere Deklarationen desselben Arrays.
Kann ich selectors und _content_template() in einem Widget kombinieren?
Technisch ja, aber in der Praxis ist das ein Signal, die Architektur zu überdenken. Wenn Selektoren für die meisten Controls ausreichen, aber ein paar Felder JS-Rendering erfordern, extrahieren Sie die JS-Logik in eine separate Methode und rufen Sie diese gezielt auf. Der vollständige Verzicht auf Selektoren zugunsten von
_content_template()bedeutet, dass Sie ein JS-Duplikat der gesamten PHP-Control-Logik schreiben, die Wartung eines solchen Widgets wird schnell zum Problem.
Lohnt es sich, Selektoren im Jahr 2026 zu beherrschen
Elementor entwickelt die atomare Infrastruktur, den Variables Manager, Grid- und Flexbox-Container sowie globale Stile kontinuierlich weiter. Aber das Fundament der Widget-Mechanik hat sich seit Version 4 nicht geändert: selectors und selectors_dictionary bleiben der primäre Weg, um ein Control mit der Live-Vorschau zu verknüpfen.
Wenn Sie diese Technik beherrschen, eliminieren Sie gut die Hälfte der gesamten JS-Logik in einem typischen Widget. Statt Handler für jedes Feld ein selectors-Array pro Control. Statt komplexer Positionierung in der Vorschau eine Kombination aus CSS-Variablen mit calc() und ein paar versteckten Controls. Das Plugin SVG Divider for Elementor ist ein lebendes Beispiel: Mehr als die Hälfte seiner Controls wird ausschließlich über Selektoren gesteuert, ohne einen einzigen _content_template()-Aufruf.
Die wichtigste Regel lautet: Überkomplizieren Sie nicht. Wenn Sie sich dabei ertappen, wie Sie ein viertes verschachteltes calc() mit drei Variablen schreiben, halten Sie inne. Vielleicht ist es einfacher, ein verstecktes Zwischen-Control hinzuzufügen oder die Logik auf zwei separate Felder aufzuteilen. Und der Elementor-Quellcode ist das beste Lehrbuch: Die Methode add_control_rules() in core/files/css/base.php zeigt, wie Selektoren intern verarbeitet werden.



