
🎯 Селектори у віджетах Elementor: повний посібник для розробників
Навіщо розробнику Elementor селектори і як вони влаштовані
Коли користувач крутить налаштування віджета в редакторі, він очікує миттєвої реакції на екрані. Без селекторів розробнику довелося б писати JS-обробник на кожну зміну поля. З селекторами все вирішує CSS.
Параметр selectors (і його менш відомий побратим selectors_dictionary) вбудовується прямо в масив виклику add_control(). Elementor на льоту підставляє значення з полів у CSS-правила та виводить їх у файл поста, щось на кшталт /wp-content/uploads/elementor/css/post-1234.css. Щойно користувач виходить із редактора, інлайн-стилі зникають, залишається чистий згенерований CSS.
💡 Швидкий огляд:
- Розберіться в синтаксисі selectors і таблиці плейсхолдерів
- Подивіться живі приклади для кольору та розмірів
- Навчіться забирати значення із сусідніх контролів
- Опануйте selectors_dictionary для підміни CSS-оголошень
- Складіть пазл із CSS-змінних і прихованих контролів
Де прописуються селектори
Коли ви створюєте віджет, кожен виклик add_control() приймає масив налаштувань. Саме в ньому й живуть selectors. Для групових контролів синтаксис той самий, масив передається всередині групової реєстрації.
Базовий формат:
1 'selectors' => [ 2 '{{WRAPPER}} .my-widget-class' => 'color: {{VALUE}}', 3 ]
Ключ, CSS-селектор (починається з {{WRAPPER}}, щоб не зачепити сусідні віджети на сторінці). Значення, одне або кілька CSS-оголошень із динамічними плейсхолдерами. Elementor бере поточне значення контрола та підставляє на місце плейсхолдера.
Результат рендериться у зовнішній CSS-файл поста, стилі живуть тільки поки відкритий редактор і одразу після збереження. Жодного інлайн-сміття.
Таблиця змінних у фігурних дужках
Жодної магії, звичайний пошук-і-заміна. Але різноманіття плейсхолдерів відчиняє двері до досить хитрих конструкцій.
Для селекторів (ключ масиву)
Плейсхолдер | Що підставляє |
|---|---|
| Унікальний селектор екземпляра віджета, наприклад |
| Тільки ID віджета (та частина після дефіса, |
| Обмежує правило вказаним пристроєм. З |
| Активний елемент repeater-контрола |
Для оголошень (значення масиву)
Плейсхолдер | Що підставляє |
|---|---|
| Сире значення контрола. Може бути перевизначене |
| Число та одиниця виміру з числових контролів. Зазвичай ідуть парою: |
| Напрямки з dimensions-контрола |
| Доступ до іменованої властивості складених контролів: наприклад, Media Control повертає масив із полями |
| Значення іншого контрола за ID. Суфікси |
| Fallback: якщо контрол порожній, підставиться |
Прості приклади, від кольору до фонового зображення
Колір із палітри. Нічого зайвого:
1 'selectors' => [ 2 '{{WRAPPER}} .elementor-svg-divider-basic-text' => 'color: {{VALUE}}', 3 ],
Числовий контрол з одиницею та без. Друга властивість (stroke-width) навмисно без {{UNIT}}, товщина обведення в пікселях, без px:
1 'selectors' => [ 2 '{{WRAPPER}} svg.sde-classic' => 3 'height: {{SIZE}}{{UNIT}}; stroke-width: {{SIZE}};', 4 ],
Відступи з dimensions-контрола, кожен напрямок окремо:
1 'selectors' => [ 2 '{{WRAPPER}} .elementor-svg-divider-basic-button' => 3 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', 4 ],
Фонове зображення слайда в repeater-контролі:
1 'selectors' => [ 2 '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-bg' => 3 'background-image: url({{URL}})', 4 ],
Умовна позиція для RTL. Один і той самий контрол дає різні властивості залежно від напрямку тексту:
1 'selectors' => [ 2 'body:not(.rtl) {{WRAPPER}} .dialog-close-button' => 'right: {{SIZE}}{{UNIT}}', 3 'body.rtl {{WRAPPER}} .dialog-close-button' => 'left: {{SIZE}}{{UNIT}}', 4 ],
Як забрати значення з іншого контрола
Якщо два поля впливають на один і той самий CSS, не дублюйте масив, просто посліться на сусідній контрол:
1 'selectors' => [ 2 '{{WRAPPER}} svg.sde-classic' => 3 'stroke-dasharray: {{dash_length.SIZE}} {{whitespace_length.SIZE}};', 4 ],
Тут dash_length і whitespace_length, ID інших контролів того ж віджета. Жодних додаткових викликів, просто точкова нотація.
Адаптивна версія, значення підтягуються з урахуванням пристрою. Реальний приклад з 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 ],
Кожна контрольна точка отримує своє значення columns. Решта властивостей (border, SIZE) спільні, вони не прив'язані до пристрою.
Selectors_dictionary, switch-case для CSS
Головна недооцінена можливість. selectors_dictionary замінює {{VALUE}} на жорстко заданий рядок, по суті, перетворює значення контрола на ключ словника.
Візьмемо стандартний Align-контрол з опціями left/center/right. Без словника ви б писали щось неприродне:
1 'selectors' => [ 2 $sde_selector => 'margin: 0 auto; margin-{{VALUE}}: 0;', 3 ],
Для center це дасть margin: 0 auto; margin-center: 0;. Властивості margin-center не існує, браузер її мовчки ігнорує. Але виглядає брудно.
Словник робить те саме чисто:
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 ],
Значення контрола center → {{VALUE}} стає margin: 0 auto. Усе.
Важливе обмеження: після активації selectors_dictionary ви втрачаєте початкове {{VALUE}}. Якщо в тому ж масиві є інша пара селектор-оголошення, якій потрібне оригінальне значення, вона отримає вже підмінений рядок. Ось проблемний приклад:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => '{{VALUE}}', 3 '{{WRAPPER}}.elementor-sde-scale-the-cropped .sde-cropping-allow .sde' => 4 'transform-origin: {{VALUE}} 0;', 5 ],
Тут transform-origin отримає margin: 0 auto 0; замість center 0;. Рішення, винести залежні оголошення в окремий контрол.
Словник чудово справляється і з трансляцією одиничних 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 ],
І навіть із цілими наборами оголошень, один ключ → кілька 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 ],
CSS-змінні, calc() і приховані контроли, збираємо пазл
Справжня сила селекторів розкривається в комбінації. Один контрол задає CSS-змінну, інший на неї посилається, третій вмикає/вимикає цілий блок правил через умову.
Слайдер Scale% записує змінну:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => '--sde-scale-percentage: {{SIZE}};', 3 ],
Перемикач «Scale cropped» використовує цю змінну у двох місцях: і для transform, і для передачі в контрол 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 ],
Прихований контрол з умовою, той самий transform, але з іншим селектором (для нестисненого стану):
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 ],
А контрол Gap використовує передану змінну з fallback:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => 3 'padding: calc({{SIZE}}{{UNIT}} / (var(--sde-scale-pct-for-gap, 100) / 100)) 0;', 4 ],
Що тут відбувається: Gap компенсує масштабування. Якщо елемент стиснуто вдвічі, зазор множиться на 2, щоб візуально залишитися тим самим. Без стиснення (змінну не задано) спрацьовує fallback 100 → ділення на 1 → зазор не змінюється. Чиста математика на CSS, без жодного рядка JS.
Стекування transform, обхідний маневр для Edge
Окремої згадки заслуговує конструкція scale(X) scale(0.01). Чому не scale(calc(var(--sde-scale-percentage) / 100))? Тому що Edge не підтримує calc() всередині transform. Взагалі.
Рішення, стекування. Браузери застосовують transform-функції послідовно, одну за одною. Тому:
1 transform: scale(var(--sde-scale-percentage)) scale(0.01);
Математично еквівалентно scale(var(--sde-scale-percentage) * 0.01), тобто діленню на 100. Користувач отримує звичний слайдер 0-100, а під капотом значення перетворюється на коефіцієнт 0-1.
Той самий принцип застосовний до інших трансформацій, rotate, translate, skew, і працює в усіх сучасних браузерах, включно з Edge.
⁉️🤔 Поширені запитання
Що саме потрапляє до згенерованого CSS-файлу?
Elementor збирає всі селектори із зареєстрованих контролів віджета, підставляє поточні значення з налаштувань користувача та записує результат у файл
/wp-content/uploads/elementor/css/post-XXXX.css. Це не інлайн-стилі та не динамічний CSS на льоту, а статичний файл, який кешується браузером і живе до наступної зміни налаштувань у редакторі. Самі плейсхолдери{{VALUE}}{{SIZE}}та інші не є частиною шаблонного рушія WordPress чи Blade: Elementor виконує звичайнийstr_replaceна етапі генерації CSS, перебираючи всі пари селектор-оголошення та замінюючи токени на фактичні значення контролів.
Як налагоджувати селектори, якщо CSS не застосовується?
Відкрийте згенерований CSS-файл поста (шлях видно у вихідному коді сторінки) та перевірте, що правило там є. Якщо правила немає, шукайте одрук в ID контрола або синтаксичну помилку в масиві
selectors. Якщо правило є, але не працює, перевірте специфічність селектора:{{WRAPPER}}дає високий пріоритет, але вкладені теми можуть перебивати через!important. УвімкнітьWP_DEBUGі дивіться PHP-логи: Elementor мовчки пропускає некоректні масиви без виведення помилок на екран. Використовуйте{{WRAPPER}}ЗАВЖДИ, окрім випадків свідомого таргетингу body або html.
Чим selectors відрізняється від кастомного CSS у налаштуваннях віджета?
Кастомний CSS (вкладка Advanced) користувач пише вручну — це статичні правила, які не реагують на зміни налаштувань. Селектори ж пов’язують контроли з CSS динамічно: повернув слайдер, змінився
width, перемкнув Align, перебудувалисяmargin. Користувач не бачить цієї механіки, він просто отримує живий попередній перегляд. Для розробника головний виграш — це відсутність методу_content_template(): без селекторів довелося б писати JS-рендеринг попереднього перегляду для кожного контрола.
Чи потрібен selectors_dictionary, якщо я вже використовую selectors?
Так, для якісного стрибка в чистоті коду. Без словника ви обробляєте значення контрола неявно, через дивні CSS-властивості на кшталт неіснуючого
margin-center, які браузер ігнорує. Зі словником ви явно вказуєте: «якщо значення left, підстав margin-right: auto, якщо center, margin: 0 auto». Код стає самодокументованим, а головне,{{VALUE}}більше не тягне за собою оригінальне значення контрола в інші оголошення того ж масиву.
Чи можна комбінувати selectors з _content_template() в одному віджеті?
Технічно так, але на практиці це сигнал переглянути архітектуру. Якщо для більшості контролів вистачає селекторів, а пара полів потребує JS-рендерингу, винесіть JS-логіку в окремий метод і викликайте точково. Повна відмова від селекторів на користь
_content_template()означає, що ви пишете JS-дублікат усієї PHP-логіки контролів, підтримка такого віджета швидко стає проблемою.
Чи варто опановувати селектори у 2026 році
Elementor продовжує розвивати атомарну інфраструктуру, Variables Manager, контейнери на Grid і Flexbox, глобальні стилі. Але фундамент віджетної механіки не змінюється з четвертої версії: selectors і selectors_dictionary — це все той самий основний спосіб пов’язати контрол із живим попереднім переглядом.
Опанувавши цю техніку, ви закриваєте добру половину всієї JS-логіки типового віджета. Замість обробників на кожне поле, один масив selectors на контрол. Замість складного позиціювання в перегляді, зв’язка CSS-змінних із calc() і парою прихованих контролів. Плагін SVG Divider for Elementor, живий приклад: більше половини його контролів керуються виключно через селектори, без жодного виклику _content_template().
Головне правило, не ускладнювати. Якщо ви ловите себе на тому, що пишете четвертий вкладений calc() із трьома змінними, зупиніться. Можливо, простіше додати прихований контрол-посередник або розбити логіку на два окремі поля. А вихідний код Elementor — це найкращий підручник: метод add_control_rules() у core/files/css/base.php показує, як селектори обробляються зсередини.



