Skip to content

Tutto per WordPress, lo sviluppo web — e non solo

🎯 Selettori nei widget di Elementor: guida completa per sviluppatori

🎯 Selettori nei widget di Elementor: guida completa per sviluppatori

Perché gli sviluppatori Elementor hanno bisogno dei selettori e come funzionano

Quando un utente modifica le impostazioni di un widget nell'editor, si aspetta una risposta immediata sullo schermo. Senza i selettori, uno sviluppatore dovrebbe scrivere un gestore JS per ogni modifica di campo. Con i selettori, tutto si risolve con i CSS.

Il parametro selectors (e la sua controparte meno nota selectors_dictionary) è incorporato direttamente nell'array della chiamata add_control(). Elementor sostituisce dinamicamente i valori dei campi nelle regole CSS e li scrive nel file del post, qualcosa come /wp-content/uploads/elementor/css/post-1234.css. Non appena l'utente esce dall'editor, gli stili inline scompaiono, lasciando CSS generato pulito.

💡 Panoramica rapida:

  • Comprendere la sintassi dei selettori e la tabella dei segnaposto
  • Vedere esempi pratici per colore e dimensioni
  • Imparare a recuperare valori da controlli adiacenti
  • Padroneggiare selectors_dictionary per la sostituzione di dichiarazioni CSS
  • Mettere insieme il puzzle delle variabili CSS e dei controlli nascosti

Dove si definiscono i selettori

Quando crei un widget, ogni chiamata add_control() accetta un array di impostazioni. È esattamente lì che risiedono i selectors. Per i controlli di gruppo la sintassi è la stessa, l'array viene passato all'interno della registrazione del gruppo.

Formato base:

1'selectors' => [
2 '{{WRAPPER}} .my-widget-class' => 'color: {{VALUE}}',
3]

La chiave è un selettore CSS (inizia con {{WRAPPER}} per evitare di influenzare i widget vicini nella pagina). Il valore è una o più dichiarazioni CSS con segnaposto dinamici. Elementor prende il valore corrente del controllo e lo sostituisce al posto del segnaposto.

Il risultato viene renderizzato nel file CSS esterno del post, gli stili esistono solo mentre l'editor è aperto e subito dopo il salvataggio. Nessun pasticcio inline.

Tabella delle variabili con parentesi graffe

Nessuna magia, solo trova e sostituisci. Ma la varietà di segnaposto apre le porte a costruzioni piuttosto ingegnose.

Per i selettori (chiave dell'array)

Segnaposto

Cosa sostituisce

{{WRAPPER}}

Selettore univoco dell'istanza del widget, ad esempio .elementor-50 .elementor-element.elementor-element-092e113. Si usa quasi sempre

{{ID}}

Solo l'ID del widget (la parte dopo il trattino, 092e113)

(desktop) / (tablet) / (mobile)

Limita la regola al dispositivo specificato. Con + significa "da questa risoluzione in su": (tablet+) = tablet e superiori

{{CURRENT_ITEM}}

Elemento attivo di un controllo ripetitore

Per le dichiarazioni (valore dell'array)

Segnaposto

Cosa sostituisce

{{VALUE}}

Valore grezzo del controllo. Può essere sovrascritto da selectors_dictionary

{{SIZE}} e {{UNIT}}

Numero e unità di misura dai controlli numerici. Di solito vanno in coppia: {{SIZE}}{{UNIT}}

{{TOP}} / {{LEFT}} / {{RIGHT}} / {{BOTTOM}}

Direzioni dal controllo dimensioni

{{URL}} o altro nome

Accesso alla proprietà nominata dei controlli compositi: ad esempio, il Controllo Media restituisce un array con i campi url id alt

{{other.SIZE}}

Valore di un altro controllo tramite ID. I suffissi _tablet e _mobile forniscono dati responsive

{{setting.SIZE \|\| 5}}

Fallback: se il controllo è vuoto, verrà sostituito 5. Funziona con stringhe tra virgolette e con il DEFAULT di un altro controllo

Esempi semplici, dal colore all'immagine di sfondo

Colore da palette. Niente di più:

1'selectors' => [
2 '{{WRAPPER}} .elementor-svg-divider-basic-text' => 'color: {{VALUE}}',
3],

Controllo numerico con e senza unità. La seconda proprietà (stroke-width) è intenzionalmente senza {{UNIT}}, lo spessore del tratto è in pixel, senza px:

1'selectors' => [
2 '{{WRAPPER}} svg.sde-classic' =>
3 'height: {{SIZE}}{{UNIT}}; stroke-width: {{SIZE}};',
4],

Spaziatura dal controllo dimensioni, ogni direzione separatamente:

1'selectors' => [
2 '{{WRAPPER}} .elementor-svg-divider-basic-button' =>
3 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
4],

Immagine di sfondo di una slide in un controllo ripetitore:

1'selectors' => [
2 '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-bg' =>
3 'background-image: url({{URL}})',
4],

Posizione condizionale per RTL. Lo stesso controllo fornisce proprietà diverse a seconda della direzione del testo:

1'selectors' => [
2 'body:not(.rtl) {{WRAPPER}} .dialog-close-button' => 'right: {{SIZE}}{{UNIT}}',
3 'body.rtl {{WRAPPER}} .dialog-close-button' => 'left: {{SIZE}}{{UNIT}}',
4],

Come recuperare un valore da un altro controllo

Se due campi influenzano lo stesso CSS, non duplicare l'array, fai semplicemente riferimento al controllo adiacente:

1'selectors' => [
2 '{{WRAPPER}} svg.sde-classic' =>
3 'stroke-dasharray: {{dash_length.SIZE}} {{whitespace_length.SIZE}};',
4],

Qui dash_length e whitespace_length sono ID di altri controlli nello stesso widget. Nessuna chiamata aggiuntiva, solo notazione a punto.

Versione responsive, i valori vengono recuperati in base al dispositivo. Esempio reale da 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],

Ogni breakpoint riceve il proprio valore columns. Le altre proprietà (border, SIZE) sono comuni, non sono legate al dispositivo.

Selectors_dictionary, switch-case per i CSS

La principale capacità sottovalutata. selectors_dictionary sostituisce {{VALUE}} con una stringa cablata, trasformando di fatto il valore del controllo in una chiave di dizionario.

Prendiamo il controllo standard Allinea con le opzioni sinistra/centro/destra. Senza un dizionario scriveresti qualcosa di innaturale:

1'selectors' => [
2 $sde_selector => 'margin: 0 auto; margin-{{VALUE}}: 0;',
3],

Per center questo produce margin: 0 auto; margin-center: 0;. La proprietà margin-center non esiste, il browser la ignora silenziosamente. Ma il codice appare disordinato.

Il dizionario fa la stessa cosa in modo pulito:

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],

Valore del controllo center{{VALUE}} diventa margin: 0 auto. Tutto qui.

Limitazione importante: dopo aver attivato selectors_dictionary perdi il {{VALUE}} originale. Se lo stesso array ha un'altra coppia selettore-dichiarazione che necessita del valore originale, riceverà la stringa già sostituita. Ecco un esempio problematico:

1'selectors' => [
2 '{{WRAPPER}} .sde' => '{{VALUE}}',
3 '{{WRAPPER}}.elementor-sde-scale-the-cropped .sde-cropping-allow .sde' =>
4 'transform-origin: {{VALUE}} 0;',
5],

Qui transform-origin riceverà margin: 0 auto 0; invece di center 0;. Soluzione: estrarre le dichiarazioni dipendenti in un controllo separato.

Il dizionario gestisce bene anche la traduzione di singoli valori 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],

E persino con interi set di dichiarazioni, una chiave → più proprietà 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],

Variabili CSS, calc() e controlli nascosti, comporre il puzzle

La vera potenza dei selettori si rivela in combinazione. Un controllo imposta una variabile CSS, un altro vi fa riferimento, un terzo abilita o disabilita un intero blocco di regole tramite una condizione.

Il cursore Scala% scrive una variabile:

1'selectors' => [
2 '{{WRAPPER}} .sde' => '--sde-scale-percentage: {{SIZE}};',
3],

L'interruttore "Scala ritagliata" usa questa variabile in due punti, sia per transform sia per passarla al controllo 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],

Controllo nascosto con condizione, lo stesso transform ma con un selettore diverso (per lo stato non ritagliato):

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],

E il controllo Gap usa la variabile passata con fallback:

1'selectors' => [
2 '{{WRAPPER}} .sde' =>
3 'padding: calc({{SIZE}}{{UNIT}} / (var(--sde-scale-pct-for-gap, 100) / 100)) 0;',
4],

Quello che accade: il gap compensa il ridimensionamento. Se un elemento viene ridotto della metà, il gap viene moltiplicato per 2 per rimanere visivamente identico. Senza ridimensionamento (variabile non impostata) entra in gioco il fallback 100 → divisione per 1 → il gap non cambia. Puro calcolo CSS, senza una sola riga di JS.

Sovrapposizione di trasformazioni, workaround per Edge

La costruzione scale(X) scale(0.01) merita una menzione speciale. Perché non scale(calc(var(--sde-scale-percentage) / 100))? Perché Edge non supporta calc() all'interno di transform. In nessun caso.

Soluzione: la sovrapposizione. I browser applicano le funzioni di trasformazione in sequenza, una dopo l'altra. Quindi:

1transform: scale(var(--sde-scale-percentage)) scale(0.01);

Matematicamente equivalente a scale(var(--sde-scale-percentage) * 0.01), ovvero divisione per 100. L'utente vede un familiare slider 0-100, mentre dietro le quinte il valore diventa un coefficiente 0-1.

Lo stesso principio si applica ad altre trasformazioni: rotate, translate, skew, e funziona in tutti i browser moderni, incluso Edge.

⁉️🤔 Domande frequenti

Cosa finisce esattamente nel file CSS generato?

Elementor raccoglie tutti i selettori dai controlli registrati del widget, sostituisce i valori correnti presi dalle impostazioni utente e scrive il risultato in /wp-content/uploads/elementor/css/post-XXXX.css. Non si tratta di stili inline né di CSS dinamico generato al volo: è un file statico che viene messo in cache dal browser e rimane invariato fino alla successiva modifica delle impostazioni nell'editor. I segnaposto stessi {{VALUE}} {{SIZE}} e altri non fanno parte di WordPress né del motore di template Blade: Elementor esegue un semplice str_replace durante la generazione del CSS, iterando tutte le coppie selettore-dichiarazione e sostituendo i token con i valori effettivi dei controlli.

Come faccio il debug dei selettori se il CSS non viene applicato?

Apri il file CSS generato del post (il percorso è visibile nel sorgente della pagina) e verifica che la regola sia presente. Se la regola manca, cerca un errore di battitura nell'ID del controllo o un errore di sintassi nell'array selectors. Se la regola c'è ma non funziona, verifica la specificità del selettore: {{WRAPPER}} fornisce un'alta priorità, ma i temi annidati possono sovrascrivere con !important. Attiva WP_DEBUG e controlla i log PHP: Elementor salta silenziosamente gli array errati senza mostrare errori a schermo. Usa {{WRAPPER}} SEMPRE, tranne nei casi in cui devi mirare intenzionalmente a body o html.

In cosa i selettori differiscono dal CSS personalizzato nelle impostazioni del widget?

Il CSS personalizzato (scheda Avanzate) è scritto manualmente dall'utente, sono regole statiche che non reagiscono ai cambiamenti delle impostazioni. I selettori collegano dinamicamente i controlli con il CSS: muovi lo slider, width cambia, cambi Allineamento, margin si ricostruisce. L'utente non vede questo meccanismo, ottiene semplicemente un'anteprima dal vivo. Per lo sviluppatore il vantaggio principale è l'assenza del metodo _content_template(): senza selettori saresti costretto a scrivere il rendering JS dell'anteprima per ogni controllo.

Mi serve selectors_dictionary se uso già i selettori?

Sì, per un salto di qualità nella pulizia del codice. Senza dizionario elabori il valore del controllo in modo implicito, attraverso strane proprietà CSS come l'inesistente margin-center, che il browser ignora. Con un dizionario specifichi esplicitamente: «se il valore è left, sostituisci margin-right: auto, se center, margin: 0 auto». Il codice diventa auto-documentante e, cosa più importante, {{VALUE}} non trascina più il valore originale del controllo nelle altre dichiarazioni dello stesso array.

Posso combinare i selettori con _content_template() in uno stesso widget?

Tecnicamente sì, ma in pratica questo è un segnale per riconsiderare l'architettura. Se i selettori bastano per la maggior parte dei controlli, ma un paio di campi richiedono rendering JS, estrai la logica JS in un metodo separato e richiamalo in modo mirato. Rinunciare completamente ai selettori a favore di _content_template() significa scrivere un duplicato JS di tutta la logica PHP dei controlli, e mantenere un widget del genere diventa rapidamente un problema.

Vale la pena padroneggiare i selettori nel 2026

Elementor continua a sviluppare infrastruttura atomica, Variables Manager, contenitori Grid e Flexbox, stili globali. Ma le fondamenta della meccanica dei widget non sono cambiate dalla versione quattro: selectors e selectors_dictionary restano il modo principale per collegare un controllo all'anteprima dal vivo.

Padroneggiando questa tecnica, elimini una buona metà di tutta la logica JS in un widget tipico. Invece di gestori per ogni campo, un array selectors per controllo. Invece di posizionamenti complessi nell'anteprima, una combinazione di variabili CSS con calc() e un paio di controlli nascosti. Il plugin SVG Divider for Elementor è un esempio concreto: più della metà dei suoi controlli è gestita esclusivamente tramite selettori, senza una singola chiamata a _content_template().

La regola principale è non complicare troppo le cose. Se ti accorgi di star scrivendo un quarto calc() annidato con tre variabili, fermati. Forse è più semplice aggiungere un controllo intermedio nascosto o dividere la logica in due campi separati. E il codice sorgente di Elementor è il miglior libro di testo: il metodo add_control_rules() in core/files/css/base.php mostra come i selettori vengono elaborati internamente.