Perché i browser corrispondono ai selettori CSS da destra a sinistra?


560

I selettori CSS sono abbinati dai motori del browser da destra a sinistra. Quindi prima trovano i bambini e poi controllano i loro genitori per vedere se corrispondono al resto delle parti della regola.

  1. Perchè è questo?
  2. È solo perché dice la specifica?
  3. Influisce sul layout finale se è stato valutato da sinistra a destra?

Per me il modo più semplice per farlo sarebbe usare i selettori con il minor numero di elementi. Quindi prima gli ID (in quanto dovrebbero restituire solo 1 elemento). Quindi forse le classi o un elemento che ha il minor numero di nodi, ad esempio potrebbe esserci solo un intervallo nella pagina, quindi vai direttamente a quel nodo con qualsiasi regola che fa riferimento a un intervallo.

Ecco alcuni link a sostegno delle mie affermazioni

  1. http://code.google.com/speed/page-speed/docs/rendering.html
  2. https://developer.mozilla.org/en/Writing_Efficient_CSS

Sembra che sia fatto in questo modo per evitare di guardare tutti i figli dei genitori (che potrebbero essere molti) piuttosto che tutti i genitori di un figlio che devono essere uno. Anche se il DOM è profondo, guarderebbe solo un nodo per livello anziché multiplo nella corrispondenza RTL. È più facile / veloce valutare i selettori CSS LTR o RTL?


5
3. No - non importa come lo leggi, il selettore corrisponde sempre allo stesso insieme di elementi.
Šime Vidas,

39
Per quello che vale, un browser non può presumere che i tuoi ID siano univoci. Potresti applicare lo stesso id = "pippo" su tutto il DOM e un #fooselettore dovrebbe corrispondere a tutti quei nodi. jQuery ha la possibilità di dire che $ ("# foo") restituirà sempre solo un elemento, perché stanno definendo la propria API con le proprie regole. Ma i browser devono implementare CSS e CSS dice di abbinare tutto nel documento con l'ID specificato.
Boris Zbarsky,

4
@Quentin In un documento "non conforme" (in HTML) gli ID documento possono essere non univoci e in quei documenti CSS richiede la corrispondenza di tutti gli elementi con quell'ID. Il CSS stesso non impone requisiti normativi affinché gli ID siano univoci; il testo che citi è informativo.
Boris Zbarsky,

5
@Boris Zbarsky Cosa fa jQuery dipende dal percorso del codice all'interno di jQuery. In alcuni casi, jQuery utilizza l'API NodeSelector (nativa querySelectorAll). In altri casi, viene utilizzato Sizzle. Sizzle non corrisponde a più ID ma QSA lo fa (AYK). Il percorso preso dipende dal selettore, dal contesto, dal browser e dalla sua versione. L'API Query di jQuery utilizza ciò che ho definito "Native First, Dual Approach". Ho scritto un articolo su questo, ma non funziona. Anche se puoi trovare qui: fortybelow.ca/hosted/dhtmlkitchen/JavaScript-Query-Engines.html
Garrett

5
Non sono nemmeno sicuro che nessuno, tranne voi due, sia in grado di capire cosa sta succedendo con una conversazione così lunga. Abbiamo chat per queste discussioni estese. Qualunque cosa tu voglia davvero tenere in giro dovrebbe essere inserita nella domanda o nella risposta, specialmente se si tratta di informazioni chiarificatrici. Stack Overflow non gestisce bene la discussione nei commenti.
George Stocker,

Risposte:


824

Tieni presente che quando un browser esegue la corrispondenza del selettore ha un elemento (quello per cui sta cercando di determinare lo stile) e tutte le tue regole e i relativi selettori e deve trovare quali regole corrispondono all'elemento. Questo è diverso dalla solita cosa jQuery, diciamo, dove hai solo un selettore e devi trovare tutti gli elementi che corrispondono a quel selettore.

Se hai solo un selettore e un solo elemento da confrontare con quel selettore, in alcuni casi ha più senso da sinistra a destra. Ma questa non è decisamente la situazione del browser. Il browser sta provando a eseguire il rendering di Gmail o qualsiasi altra cosa e ha quello <span>che sta cercando di modellare e le oltre 10.000 regole che Gmail inserisce nel suo foglio di stile (non sto inventando quel numero).

In particolare, nella situazione in cui il browser sta osservando la maggior parte dei selettori che sta considerando , non corrisponde all'elemento in questione. Quindi il problema diventa quello di decidere che un selettore non corrisponde il più velocemente possibile; se ciò richiede un po 'di lavoro extra nei casi che corrispondono, vinci comunque a causa di tutto il lavoro che risparmi nei casi che non corrispondono.

Se inizi semplicemente abbinando la parte più a destra del selettore con il tuo elemento, allora è probabile che non corrisponda e il gioco è fatto. Se corrisponde, devi fare più lavoro, ma solo in proporzione alla profondità dell'albero, che nella maggior parte dei casi non è così grande.

D'altra parte, se inizi abbinando la parte più a sinistra del selettore ... a cosa lo abbini? Devi iniziare a camminare sul DOM, alla ricerca di nodi che potrebbero corrispondere. Scoprire che non c'è nulla di simile alla parte più a sinistra potrebbe richiedere del tempo.

Quindi i browser corrispondono da destra; fornisce un ovvio punto di partenza e ti consente di sbarazzarti della maggior parte dei selettori candidati molto rapidamente. Puoi vedere alcuni dati su http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665 (anche se la notazione è confusa), ma il risultato è che in particolare per Gmail due anni fa, per il 70% delle coppie (regola, elemento) potresti decidere che la regola non corrisponde solo dopo aver esaminato le parti tag / class / id del selettore più a destra per la regola. Il numero corrispondente per la suite di test delle prestazioni di pageload di Mozilla era del 72%. Quindi vale davvero la pena provare a sbarazzarsi di quei 2/3 di tutte le regole il più velocemente possibile e quindi preoccuparsi solo di abbinare il rimanente 1/3.

Nota anche che ci sono altre ottimizzazioni che i browser già fanno per evitare di provare a trovare regole che sicuramente non corrisponderanno. Ad esempio, se il selettore più a destra ha un id e quell'id non corrisponde all'id dell'elemento, allora non ci sarà alcun tentativo di abbinare quel selettore a quell'elemento in Gecko: l'insieme di "selettori con ID" che viene tentato proviene da una ricerca hashtable sull'ID dell'elemento. Quindi questo è il 70% delle regole che hanno buone possibilità di abbinamento che ancora non corrispondono dopo aver considerato solo il tag / class / id del selettore più a destra.


5
Come piccolo bonus, ha più senso leggerlo RTL che LTR anche in inglese. Un esempio: stackoverflow.com/questions/3851635/css-combinator-precedence/...
BoltClock

6
Si noti che la corrispondenza RTL si applica solo a tutti i combinatori . Non esegue il drill-down fino al livello di selezione semplice. Cioè, un browser prende il selettore composto più a destra , o sequenza di selettori semplici e tenta di abbinarlo atomicamente. Quindi, se c'è una corrispondenza, segue il combinatore a sinistra verso il successivo selettore composto e controlla l'elemento in quella posizione, e così via. Non ci sono prove che un browser legga ogni parte di un selettore composto RTL; infatti, l'ultimo paragrafo mostra esattamente il contrario (i controlli ID vengono sempre per primi).
BoltClock

5
In realtà, quando abbini i selettori, almeno in Gecko, il tagname e lo spazio dei nomi vengono prima di tutto. L'id (così come il tagname e i nomi di classe) è considerato in una fase di pre-filtro che elimina la maggior parte delle regole senza davvero cercare di abbinare i selettori.
Boris Zbarsky,

Ciò potrebbe aiutare a seconda delle ottimizzazioni che UA sta facendo, ma non aiuterà il passaggio di pre-filtro in Gecko che descrivo sopra. C'è un secondo passaggio di filtraggio che funziona su ID e classi che viene utilizzato solo per i combinatori discendenti che potrebbe aiutare, però.
Boris Zbarsky,

@Benito Ciaro: per non parlare anche dei problemi di specificità.
BoltClock

33

L'analisi da destra a sinistra, chiamata anche analisi dal basso verso l'alto, è effettivamente efficace per il browser.

Considera quanto segue:

#menu ul li a { color: #00f; }

Il browser controlla prima per a, poi li, poi ule poi #menu.

Questo perché il browser sta eseguendo la scansione della pagina, deve solo guardare l'elemento / nodo corrente e tutti i nodi / elementi precedenti che ha scansionato.

La cosa da notare è che browser inizia l'elaborazione nel momento in cui ottiene un tag / nodo completo e non è necessario attendere l'intera pagina tranne quando trova uno script, nel qual caso si ferma temporaneamente e completa l'esecuzione dello script e quindi va avanti.

In caso contrario, sarà inefficiente perché il browser ha rilevato l'elemento che stava eseguendo la scansione al primo controllo, ma è stato quindi costretto a continuare a cercare nel documento tutti i selettori aggiuntivi. Per questo il browser deve avere l'intero HTML e potrebbe essere necessario scansionare l'intera pagina prima di iniziare a dipingere CSS.

Ciò è in contrasto con il modo in cui la maggior parte delle librerie analizza dom. Qui il dom è costruito e non ha bisogno di scansionare l'intera pagina, basta trovare il primo elemento e continuare ad abbinare gli altri al suo interno.


20

Consente il collegamento in cascata dal più specifico al meno specifico. Permette anche un corto circuito nell'applicazione. Se la regola più specifica si applica in tutti gli aspetti a cui si applica la regola padre, tutte le regole padre vengono ignorate. Se ci sono altri bit nel genitore, questi vengono applicati.

Se facessi il contrario, dovresti formattare in base al genitore e quindi sovrascrivere ogni volta che il bambino ha qualcosa di diverso. A lungo termine, questo è molto più lavoro che ignorare gli elementi nelle regole che sono già stati curati.


11
Questo è un problema separato. Fai il collegamento a cascata ordinando le regole per specificità e quindi confrontandole in ordine di specificità. Ma la domanda qui è perché per una data regola abbini i suoi selettori in un modo particolare.
Boris Zbarsky,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.