
🎯 Selektory w widgetach Elementor: pełny przewodnik dla programistów
Do czego programiście Elementora służą selektory i jak są zbudowane
Kiedy użytkownik zmienia ustawienia widżetu w edytorze, oczekuje natychmiastowej reakcji na ekranie. Bez selektorów programista musiałby pisać procedurę JS dla każdej zmiany pola. Z selektorami wszystko załatwia CSS.
Parametr selectors (i jego mniej znany odpowiednik selectors_dictionary) jest osadzany bezpośrednio w tablicy wywołania add_control(). Elementor w locie podstawia wartości z pól do reguł CSS i wyprowadza je do pliku posta, coś w rodzaju /wp-content/uploads/elementor/css/post-1234.css. Gdy tylko użytkownik wyjdzie z edytora, style inline znikają, zostaje czysty wygenerowany CSS.
💡 Szybki przegląd:
- Proszę poznać składnię selectors i tabelę placeholderów
- Proszę zobaczyć działające przykłady dla koloru i rozmiarów
- Proszę nauczyć się pobierać wartości z sąsiednich kontrolek
- Proszę opanować selectors_dictionary do podmiany deklaracji CSS
- Proszę ułożyć puzzle ze zmiennych CSS i ukrytych kontrolek
Gdzie definiuje się selektory
Kiedy tworzą Państwo widżet, każde wywołanie add_control() przyjmuje tablicę ustawień. To właśnie w niej znajdują się selectors. Dla kontrolek grupowych składnia jest taka sama, tablica jest przekazywana wewnątrz rejestracji grupowej.
Podstawowy format:
1 'selectors' => [ 2 '{{WRAPPER}} .my-widget-class' => 'color: {{VALUE}}', 3 ]
Klucz, selektor CSS (zaczyna się od {{WRAPPER}}, aby nie naruszyć sąsiednich widżetów na stronie). Wartość, jedna lub kilka deklaracji CSS z dynamicznymi placeholderami. Elementor pobiera bieżącą wartość kontrolki i podstawia ją w miejsce placeholdera.
Wynik jest renderowany do zewnętrznego pliku CSS posta, style istnieją tylko gdy edytor jest otwarty i zaraz po zapisaniu. Żadnych śmieci inline.
Tabela zmiennych w nawiasach klamrowych
Żadnej magii, zwykłe szukaj-i-zamień. Ale różnorodność placeholderów otwiera drzwi do całkiem sprytnych konstrukcji.
Dla selektorów (klucz tablicy)
Placeholder | Co podstawia |
|---|---|
| Unikalny selektor instancji widżetu, np. |
| Tylko ID widżetu (ta część po myślniku, |
| Ogranicza regułę do wskazanego urządzenia. Z |
| Aktywny element kontrolki typu repeater |
Dla deklaracji (wartość tablicy)
Placeholder | Co podstawia |
|---|---|
| Surowa wartość kontrolki. Może być nadpisana przez |
| Liczba i jednostka miary z kontrolek numerycznych. Zwykle występują razem: |
| Kierunki z kontrolki dimensions |
| Dostęp do nazwanej właściwości kontrolek złożonych: np. Media Control zwraca tablicę z polami |
| Wartość innej kontrolki po ID. Przyrostki |
| Fallback: jeśli kontrolka jest pusta, podstawione zostanie |
Proste przykłady, od koloru do obrazu tła
Kolor z palety. Nic zbędnego:
1 'selectors' => [ 2 '{{WRAPPER}} .elementor-svg-divider-basic-text' => 'color: {{VALUE}}', 3 ],
Kontrolka numeryczna z jednostką i bez. Druga właściwość (stroke-width) celowo bez {{UNIT}}, grubość obrysu w pikselach, bez px:
1 'selectors' => [ 2 '{{WRAPPER}} svg.sde-classic' => 3 'height: {{SIZE}}{{UNIT}}; stroke-width: {{SIZE}};', 4 ],
Odstępy z kontrolki dimensions, każdy kierunek osobno:
1 'selectors' => [ 2 '{{WRAPPER}} .elementor-svg-divider-basic-button' => 3 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', 4 ],
Obraz tła slajdu w kontrolce repeater:
1 'selectors' => [ 2 '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-bg' => 3 'background-image: url({{URL}})', 4 ],
Warunkowa pozycja dla RTL. Ta sama kontrolka daje różne właściwości w zależności od kierunku tekstu:
1 'selectors' => [ 2 'body:not(.rtl) {{WRAPPER}} .dialog-close-button' => 'right: {{SIZE}}{{UNIT}}', 3 'body.rtl {{WRAPPER}} .dialog-close-button' => 'left: {{SIZE}}{{UNIT}}', 4 ],
Jak pobrać wartość z innej kontrolki
Jeśli dwa pola wpływają na ten sam CSS, proszę nie duplikować tablicy, wystarczy odwołać się do sąsiedniej kontrolki:
1 'selectors' => [ 2 '{{WRAPPER}} svg.sde-classic' => 3 'stroke-dasharray: {{dash_length.SIZE}} {{whitespace_length.SIZE}};', 4 ],
Tutaj dash_length i whitespace_length, ID innych kontrolek tego samego widżetu. Żadnych dodatkowych wywołań, po prostu notacja z kropką.
Wersja adaptacyjna, wartości są pobierane z uwzględnieniem urządzenia. Rzeczywisty przykład z 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 ],
Każdy punkt kontrolny otrzymuje własną wartość columns. Pozostałe właściwości (border, SIZE) są wspólne, nie są powiązane z urządzeniem.
Selectors_dictionary, switch-case dla CSS
Główna niedoceniana możliwość. selectors_dictionary zastępuje {{VALUE}} sztywno zdefiniowanym ciągiem znaków, w istocie przekształca wartość kontrolki w klucz słownika.
Weźmy standardową kontrolkę Align z opcjami left/center/right. Bez słownika napisaliby Państwo coś nienaturalnego:
1 'selectors' => [ 2 $sde_selector => 'margin: 0 auto; margin-{{VALUE}}: 0;', 3 ],
Dla center da to margin: 0 auto; margin-center: 0;. Właściwość margin-center nie istnieje, przeglądarka po cichu ją ignoruje. Ale wygląda to nieczysto.
Słownik robi to samo czysto:
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 ],
Wartość kontrolki center → {{VALUE}} staje się margin: 0 auto. To wszystko.
Ważne ograniczenie: po aktywacji selectors_dictionary tracą Państwo pierwotne {{VALUE}}. Jeśli w tej samej tablicy znajduje się inna para selektor-deklaracja, która potrzebuje oryginalnej wartości, otrzyma ona już podstawiony ciąg. Oto problematyczny przykład:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => '{{VALUE}}', 3 '{{WRAPPER}}.elementor-sde-scale-the-cropped .sde-cropping-allow .sde' => 4 'transform-origin: {{VALUE}} 0;', 5 ],
Tutaj transform-origin otrzyma margin: 0 auto 0; zamiast center 0;. Rozwiązanie: wydzielić zależne deklaracje do osobnej kontrolki.
Słownik doskonale radzi sobie również z translacją pojedynczych wartości CSS:
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 ],
A nawet z całymi zestawami deklaracji, jeden klucz → kilka właściwości CSS:
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 ],
Zmienne CSS, calc() i ukryte kontrolki, składamy puzzle
Prawdziwa siła selektorów ujawnia się w kombinacji. Jedna kontrolka ustawia zmienną CSS, inna się do niej odwołuje, trzecia włącza/wyłącza cały blok reguł poprzez warunek.
Suwak Scale% zapisuje zmienną:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => '--sde-scale-percentage: {{SIZE}};', 3 ],
Przełącznik „Scale cropped" używa tej zmiennej w dwóch miejscach: zarówno dla transform, jak i do przekazania do kontrolki Gap:
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 ],
Ukryta kontrolka z warunkiem, ten sam transform, ale z innym selektorem (dla stanu nieściśniętego):
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 ],
A kontrolka Gap używa przekazanej zmiennej z fallbackiem:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => 3 'padding: calc({{SIZE}}{{UNIT}} / (var(--sde-scale-pct-for-gap, 100) / 100)) 0;', 4 ],
Co tu się dzieje: Gap kompensuje skalowanie. Jeśli element jest ściśnięty dwukrotnie, odstęp jest mnożony przez 2, aby wizualnie pozostał taki sam. Bez skalowania (zmienna nie jest ustawiona) działa fallback 100 → dzielenie przez 1 → odstęp nie zmienia się. Czysta matematyka na CSS, bez ani jednej linii JS.
Stackowanie transform, manewr omijający dla Edge
Na osobną wzmiankę zasługuje konstrukcja scale(X) scale(0.01). Dlaczego nie scale(calc(var(--sde-scale-percentage) / 100))? Ponieważ Edge nie obsługuje calc() wewnątrz transform. W ogóle.
Rozwiązanie: stackowanie. Przeglądarki stosują funkcje transform sekwencyjnie, jedna po drugiej. Dlatego:
1 transform: scale(var(--sde-scale-percentage)) scale(0.01);
Matematycznie jest to równoważne scale(var(--sde-scale-percentage) * 0.01), czyli podzieleniu przez 100. Użytkownik otrzymuje zwykły suwak 0-100, a pod maską wartość jest przekształcana na współczynnik 0-1.
Ta sama zasada ma zastosowanie do innych transformacji, rotate, translate, skew, i działa we wszystkich nowoczesnych przeglądarkach, włącznie z Edge.
⁉️🤔 Najczęściej zadawane pytania
Co dokładnie trafia do wygenerowanego pliku CSS?
Elementor zbiera wszystkie selektory z zarejestrowanych kontrolek widżetu, podstawia bieżące wartości z ustawień użytkownika i zapisuje wynik w pliku
/wp-content/uploads/elementor/css/post-XXXX.css. Nie są to style inline ani dynamiczny CSS generowany na bieżąco, lecz statyczny plik, który jest cache'owany przez przeglądarkę i istnieje do kolejnej zmiany ustawień w edytorze. Same symbole zastępcze{{VALUE}},{{SIZE}}i inne nie są częścią silnika szablonów WordPress ani Blade: Elementor wykonuje zwykłestr_replacena etapie generowania CSS, przeglądając wszystkie pary selektor-deklaracja i zastępując tokeny faktycznymi wartościami kontrolek.
Jak debugować selektory, jeśli CSS nie jest stosowany?
Proszę otworzyć wygenerowany plik CSS posta (ścieżka jest widoczna w kodzie źródłowym strony) i sprawdzić, czy reguła tam jest. Jeśli reguły nie ma, proszę szukać literówki w ID kontrolki lub błędu składniowego w tablicy
selectors. Jeśli reguła jest, ale nie działa, proszę sprawdzić specyficzność selektora:{{WRAPPER}}daje wysoki priorytet, ale zagnieżdżone motywy mogą nadpisywać go przez!important. Proszę włączyćWP_DEBUGi sprawdzić logi PHP: Elementor po cichu pomija nieprawidłowe tablice, nie wyświetlając błędów na ekranie. Proszę używać{{WRAPPER}}ZAWSZE, z wyjątkiem przypadków świadomego targetowania body lub html.
Czym selectors różni się od niestandardowego CSS w ustawieniach widżetu?
Niestandardowy CSS (zakładka Zaawansowane) użytkownik pisze ręcznie, są to statyczne reguły, które nie reagują na zmiany ustawień. Selektory natomiast wiążą kontrolki z CSS dynamicznie: przesunięto suwak, zmienił się
width, przełączono Wyrównanie, przebudowały sięmargin. Użytkownik nie widzi tej mechaniki, po prostu otrzymuje podgląd na żywo. Dla developera główną wygraną jest brak metody_content_template(): bez selektorów trzeba by pisać renderowanie podglądu w JS dla każdej kontrolki.
Czy potrzebny jest selectors_dictionary, jeśli już używam selectors?
Tak, dla jakościowego skoku w czystości kodu. Bez słownika obsługuje Pan/Pani wartość kontrolki niejawnie, poprzez dziwne właściwości CSS, takie jak nieistniejący
margin-center, które przeglądarka ignoruje. Ze słownikiem jawnie wskazuje Pan/Pani: „jeśli wartość left, podstaw margin-right: auto, jeśli center, margin: 0 auto". Kod staje się samodokumentujący, a co najważniejsze,{{VALUE}}nie ciągnie już za sobą oryginalnej wartości kontrolki do innych deklaracji w tej samej tablicy.
Czy można łączyć selectors z _content_template() w jednym widżecie?
Technicznie tak, ale w praktyce jest to sygnał, by przejrzeć architekturę. Jeśli dla większości kontrolek wystarczają selektory, a kilka pól wymaga renderowania w JS, proszę wydzielić logikę JS do osobnej metody i wywoływać ją punktowo. Całkowita rezygnacja z selektorów na rzecz
_content_template()oznacza, że pisze Pan/Pani duplikat całej logiki PHP kontrolek w JS, a utrzymanie takiego widżetu szybko staje się problemem.
Czy warto opanować selektory w 2026 roku
Elementor nadal rozwija infrastrukturę atomową, Variables Manager, kontenery na Grid i Flexbox, style globalne. Jednak fundament mechaniki widżetów nie zmienia się od czwartej wersji: selectors i selectors_dictionary to wciąż ten sam podstawowy sposób na powiązanie kontrolki z podglądem na żywo.
Po opanowaniu tej techniki eliminuje Pan/Pani dobrą połowę całej logiki JS typowego widżetu. Zamiast handlerów dla każdego pola, jedna tablica selectors na kontrolkę. Zamiast skomplikowanego pozycjonowania w podglądzie, powiązanie zmiennych CSS z calc() i parą ukrytych kontrolek. Wtyczka SVG Divider for Elementor jest żywym przykładem: ponad połowa jej kontrolek jest zarządzana wyłącznie przez selektory, bez ani jednego wywołania _content_template().
Główna zasada: nie komplikować. Jeśli przyłapuje się Pan/Pani na pisaniu czwartego zagnieżdżonego calc() z trzema zmiennymi, proszę się zatrzymać. Być może prościej jest dodać ukrytą kontrolkę pośredniczącą lub rozbić logikę na dwa osobne pola. A kod źródłowy Elementora to najlepszy podręcznik: metoda add_control_rules() w core/files/css/base.php pokazuje, jak selektory są przetwarzane od wewnątrz.



