
🎯 Sélecteurs dans les widgets Elementor : guide complet pour les développeurs
Pourquoi les développeurs Elementor ont besoin des sélecteurs et comment ils fonctionnent
Lorsqu’un utilisateur modifie les paramètres d’un widget dans l’éditeur, il s’attend à une réaction instantanée à l’écran. Sans les sélecteurs, le développeur devrait écrire un gestionnaire JavaScript pour chaque changement de champ. Avec les sélecteurs, tout se règle par CSS.
Le paramètre selectors (et son équivalent moins connu selectors_dictionary) est intégré directement dans le tableau de l’appel add_control(). Elementor substitue dynamiquement les valeurs des champs dans les règles CSS et les écrit dans le fichier de publication, par exemple /wp-content/uploads/elementor/css/post-1234.css. Dès que l’utilisateur quitte l’éditeur, les styles en ligne disparaissent et laissent place à un CSS généré propre.
💡 Aperçu rapide:
- Comprendre la syntaxe des sélecteurs et le tableau des variables
- Voir des exemples concrets pour la couleur et les tailles
- Apprendre à récupérer les valeurs d’autres contrôles
- Maîtriser
selectors_dictionarypour la substitution de déclarations CSS - Assembler le puzzle des variables CSS et des contrôles cachés
Où les sélecteurs sont définis
Lorsque vous créez un widget, chaque appel add_control() accepte un tableau de paramètres. C’est précisément là que se trouvent les selectors. Pour les contrôles de groupe, la syntaxe est identique, le tableau est passé à l’intérieur de l’enregistrement du groupe.
Format de base:
1 'selectors' => [ 2 '{{WRAPPER}} .my-widget-class' => 'color: {{VALUE}}', 3 ]
La clé est un sélecteur CSS (qui commence par {{WRAPPER}} pour éviter d’affecter les widgets voisins sur la page). La valeur est une ou plusieurs déclarations CSS avec des variables dynamiques. Elementor prend la valeur actuelle du contrôle et la substitue à la place de la variable.
Le résultat est écrit dans le fichier CSS externe de la publication, les styles n’existent que lorsque l’éditeur est ouvert et immédiatement après la sauvegarde. Aucun désordre en ligne.
Tableau des variables entre accolades
Pas de magie, juste une recherche et un remplacement. Mais la diversité des variables ouvre la porte à des constructions assez astucieuses.
Pour les sélecteurs (clé du tableau)
Variable | Ce qu’elle substitue |
|---|---|
| Sélecteur unique de l’instance du widget, par exemple |
| Uniquement l’identifiant du widget (la partie après le tiret, |
| Restreint la règle à l’appareil spécifié. Avec |
| Élément actif d’un contrôle de type répéteur |
Pour les déclarations (valeur du tableau)
Variable | Ce qu’elle substitue |
|---|---|
| Valeur brute du contrôle. Peut être remplacée par |
| Nombre et unité de mesure issus des contrôles numériques. Vont généralement par paire: |
| Directions issues du contrôle de dimensions |
| Accès à une propriété nommée des contrôles composites: par exemple, le contrôle Média renvoie un tableau avec les champs |
| Valeur d’un autre contrôle par son ID. Les suffixes |
| Valeur de repli: si le contrôle est vide, |
Exemples simples, de la couleur à l’image de fond
Couleur depuis la palette. Rien de superflu:
1 'selectors' => [ 2 '{{WRAPPER}} .elementor-svg-divider-basic-text' => 'color: {{VALUE}}', 3 ],
Contrôle numérique avec et sans unité. La seconde propriété (stroke-width) est volontairement sans {{UNIT}}, l’épaisseur du trait est en pixels, sans px:
1 'selectors' => [ 2 '{{WRAPPER}} svg.sde-classic' => 3 'height: {{SIZE}}{{UNIT}}; stroke-width: {{SIZE}};', 4 ],
Espacement depuis le contrôle de dimensions, chaque direction séparément:
1 'selectors' => [ 2 '{{WRAPPER}} .elementor-svg-divider-basic-button' => 3 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', 4 ],
Image de fond d’un slide dans un contrôle répéteur:
1 'selectors' => [ 2 '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-bg' => 3 'background-image: url({{URL}})', 4 ],
Position conditionnelle pour le sens de lecture droite-gauche. Le même contrôle fournit des propriétés différentes selon la direction du texte:
1 'selectors' => [ 2 'body:not(.rtl) {{WRAPPER}} .dialog-close-button' => 'right: {{SIZE}}{{UNIT}}', 3 'body.rtl {{WRAPPER}} .dialog-close-button' => 'left: {{SIZE}}{{UNIT}}', 4 ],
Comment récupérer la valeur d’un autre contrôle
Si deux champs affectent le même CSS, ne dupliquez pas le tableau, référencez simplement le contrôle voisin:
1 'selectors' => [ 2 '{{WRAPPER}} svg.sde-classic' => 3 'stroke-dasharray: {{dash_length.SIZE}} {{whitespace_length.SIZE}};', 4 ],
Ici, dash_length et whitespace_length sont les identifiants d’autres contrôles dans le même widget. Aucun appel supplémentaire, juste la notation par point.
Version responsive, les valeurs sont récupérées en fonction de l’appareil. Exemple concret avec 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 ],
Chaque point de rupture reçoit sa propre valeur columns. Les autres propriétés (border, SIZE) sont communes, elles ne sont pas liées à l’appareil.
Selectors_dictionary, un switch-case pour le CSS
La principale capacité sous-estimée. selectors_dictionary remplace {{VALUE}} par une chaîne codée en dur, transformant ainsi la valeur du contrôle en clé de dictionnaire.
Prenons le contrôle Align standard avec les options left/center/right. Sans dictionnaire, vous écririez quelque chose de peu naturel:
1 'selectors' => [ 2 $sde_selector => 'margin: 0 auto; margin-{{VALUE}}: 0;', 3 ],
Pour center, cela produit margin: 0 auto; margin-center: 0;. La propriété margin-center n’existe pas, le navigateur l’ignore silencieusement. Mais le résultat est peu élégant.
Le dictionnaire fait la même chose proprement:
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 ],
Valeur du contrôle center → {{VALUE}} devient margin: 0 auto. C’est tout.
Limitation importante: après activation de selectors_dictionary, vous perdez le {{VALUE}} d’origine. Si le même tableau contient une autre paire sélecteur-déclaration qui a besoin de la valeur d’origine, elle recevra la chaîne déjà substituée. Voici un exemple problématique:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => '{{VALUE}}', 3 '{{WRAPPER}}.elementor-sde-scale-the-cropped .sde-cropping-allow .sde' => 4 'transform-origin: {{VALUE}} 0;', 5 ],
Ici, transform-origin recevra margin: 0 auto 0; au lieu de center 0;. Solution: extraire les déclarations dépendantes dans un contrôle séparé.
Le dictionnaire gère également bien la traduction de valeurs CSS individuelles:
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 ],
Et même avec des ensembles entiers de déclarations, une clé → plusieurs propriétés 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 ],
Variables CSS, calc() et contrôles cachés, assemblage du puzzle
La véritable puissance des sélecteurs se révèle en combinaison. Un contrôle définit une variable CSS, un autre y fait référence, un troisième active ou désactive tout un bloc de règles via une condition.
Le curseur Scale% écrit une variable:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => '--sde-scale-percentage: {{SIZE}};', 3 ],
La bascule «Scale cropped» utilise cette variable à deux endroits, à la fois pour la transformation et pour la transmettre au contrôle 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 ],
Contrôle caché avec condition, la même transformation mais avec un sélecteur différent (pour l’état non rogné):
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 ],
Et le contrôle Gap utilise la variable transmise avec une valeur de repli:
1 'selectors' => [ 2 '{{WRAPPER}} .sde' => 3 'padding: calc({{SIZE}}{{UNIT}} / (var(--sde-scale-pct-for-gap, 100) / 100)) 0;', 4 ],
Voici ce qui se passe: Gap compense la mise à l'échelle. Si un élément est réduit de moitié, l'écart est multiplié par 2 pour rester visuellement identique. Sans réduction (variable non définie), la valeur de repli 100 s'applique → division par 1 → l'écart ne change pas. Du calcul CSS pur, sans une seule ligne de JS.
Empilement de transformations, contournement pour Edge
La construction scale(X) scale(0.01) mérite une mention particulière. Pourquoi ne pas utiliser scale(calc(var(--sde-scale-percentage) / 100))? Parce qu'Edge ne prend pas du tout en charge calc() à l'intérieur de transform.
La solution: l'empilement. Les navigateurs appliquent les fonctions de transformation de manière séquentielle, l'une après l'autre. Ainsi:
1 transform: scale(var(--sde-scale-percentage)) scale(0.01);
C'est mathématiquement équivalent à scale(var(--sde-scale-percentage) * 0.01), autrement dit une division par 100. L'utilisateur dispose d'un curseur familier de 0 à 100, tandis qu'en coulisses la valeur est convertie en un coefficient de 0 à 1.
Le même principe s'applique aux autres transformations: rotation, translation, inclinaison, et fonctionne dans tous les navigateurs modernes, y compris Edge.
⁉️🤔 Questions fréquentes
Qu’est-ce qui est exactement écrit dans le fichier CSS généré?
Elementor collecte tous les sélecteurs des contrôles de widget enregistrés, y substitue les valeurs actuelles issues des réglages utilisateur et écrit le résultat dans
/wp-content/uploads/elementor/css/post-XXXX.css. Il ne s’agit ni de styles inline ni de CSS dynamique calculé à la volée: c’est un fichier statique, mis en cache par le navigateur et qui persiste jusqu’au prochain changement de réglage dans l’éditeur. Les placeholders eux-mêmes{{VALUE}},{{SIZE}}et les autres ne font pas partie de WordPress ni du moteur de template Blade: Elementor effectue un simplestr_replacependant la génération du CSS, en parcourant toutes les paires sélecteur-déclaration et en remplaçant les tokens par les valeurs réelles des contrôles.
Comment déboguer les sélecteurs si le CSS ne s’applique pas?
Ouvrez le fichier CSS généré du post (le chemin est visible dans la source de la page) et vérifiez que la règle est bien présente. Si la règle est absente, cherchez une faute de frappe dans l’ID du contrôle ou une erreur de syntaxe dans le tableau
selectors. Si la règle est présente mais ne fonctionne pas, vérifiez la spécificité du sélecteur:{{WRAPPER}}offre une priorité élevée, mais les thèmes imbriqués peuvent passer outre via!important. ActivezWP_DEBUGet surveillez les logs PHP: Elementor ignore silencieusement les tableaux incorrects sans afficher d’erreur à l’écran. Utilisez TOUJOURS{{WRAPPER}}, sauf dans les cas où vous ciblez intentionnellement body ou html.
En quoi les sélecteurs diffèrent-ils du CSS personnalisé dans les réglages du widget?
Le CSS personnalisé (onglet Avancé) est écrit manuellement par l’utilisateur, ce sont des règles statiques qui ne réagissent pas aux changements de réglages. Les sélecteurs lient dynamiquement les contrôles au CSS: tournez le curseur, la
widthchange; changez l’alignement, lamarginse reconstruit. L’utilisateur ne voit pas ce mécanisme, il bénéficie simplement d’un aperçu en direct. Pour le développeur, le principal avantage est l’absence de la méthode_content_template(): sans les sélecteurs, vous devriez écrire le rendu de l’aperçu en JS pour chaque contrôle.
Ai-je besoin de selectors_dictionary si j’utilise déjà les sélecteurs?
Oui, pour un saut qualitatif en matière de propreté du code. Sans dictionnaire, vous traitez la valeur du contrôle de manière implicite, via des propriétés CSS étranges comme la propriété inexistante
margin-center, que le navigateur ignore. Avec un dictionnaire, vous spécifiez explicitement: «si la valeur est left, substitue margin-right: auto, si center, margin: 0 auto». Le code devient auto-documenté, et surtout,{{VALUE}}ne traîne plus la valeur brute du contrôle dans les autres déclarations du même tableau.
Puis-je combiner les sélecteurs avec _content_template() dans un même widget?
Techniquement oui, mais en pratique c’est un signal pour reconsidérer l’architecture. Si les sélecteurs suffisent pour la plupart des contrôles, mais que quelques champs nécessitent un rendu JS, extrayez la logique JS dans une méthode séparée et appelez-la de façon ciblée. Abandonner complètement les sélecteurs au profit de
_content_template()signifie que vous écrivez un duplicata JS de toute la logique de contrôle PHP: maintenir un tel widget devient rapidement problématique.
Vaut-il la peine de maîtriser les sélecteurs en 2026
Elementor continue de développer son infrastructure atomique, le Gestionnaire de variables, les conteneurs Grid et Flexbox, les styles globaux. Mais le socle de la mécanique des widgets n’a pas changé depuis la version quatre: selectors et selectors_dictionary restent le moyen principal de lier un contrôle à l’aperçu en direct.
En maîtrisant cette technique, vous éliminez une bonne moitié de toute la logique JS dans un widget typique. Au lieu de gestionnaires pour chaque champ, un seul tableau selectors par contrôle. Au lieu d’un positionnement complexe dans l’aperçu, une combinaison de variables CSS avec calc() et quelques contrôles cachés. Le plugin SVG Divider for Elementor en est un exemple vivant: plus de la moitié de ses contrôles sont gérés exclusivement via les sélecteurs, sans un seul appel à _content_template().
La règle principale est de ne pas surcomplexifier. Si vous vous surprenez à écrire un quatrième calc() imbriqué avec trois variables, arrêtez-vous. Peut-être est-il plus simple d’ajouter un contrôle intermédiaire caché ou de scinder la logique en deux champs distincts. Et le code source d’Elementor est le meilleur manuel: la méthode add_control_rules() dans core/files/css/base.php montre comment les sélecteurs sont traités en interne.



