Por que os navegadores correspondem aos seletores de CSS da direita para a esquerda?

560

Os seletores CSS são correspondidos pelos mecanismos do navegador da direita para a esquerda. Então, eles primeiro encontram as crianças e depois checam os pais para ver se elas correspondem ao restante das partes da regra.

  1. Por que é isso?
  2. É apenas porque a especificação diz?
  3. Isso afeta o layout final se for avaliado da esquerda para a direita?

Para mim, a maneira mais simples de fazer isso seria usar os seletores com o menor número de elementos. Portanto, os IDs primeiro (como devem retornar apenas 1 elemento). Então talvez classes ou um elemento que tenha o menor número de nós - por exemplo, pode haver apenas um período na página; portanto, vá diretamente para esse nó com qualquer regra que faça referência a um período.

Aqui estão alguns links para fazer backup de minhas reivindicações

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

Parece que isso é feito dessa maneira para evitar ter que olhar para todos os filhos dos pais (que podem ser muitos) e não para todos os pais de um filho que deve ser um. Mesmo que o DOM seja profundo, ele analisaria apenas um nó por nível, em vez de vários na correspondência RTL. É mais fácil / rápido avaliar os seletores CSS LTR ou RTL?

tgandrews
fonte
5
3. Não - não importa como você o leia, o seletor sempre corresponde ao mesmo conjunto de elementos.
Šime Vidas
39
Pelo que vale, um navegador não pode assumir que seus IDs são únicos. Você pode colocar o mesmo id = "foo" em todo o seu DOM, e um #fooseletor precisará corresponder a todos esses nós. O jQuery tem a opção de dizer que $ ("# foo") sempre retornará apenas um elemento, porque eles estão definindo sua própria API com suas próprias regras. Mas os navegadores precisam implementar o CSS, e o CSS diz que corresponde a tudo no documento com o ID fornecido.
precisa saber é o seguinte
4
@Quentin Em um documento "não conforme" (para HTML), os IDs podem não ser únicos e, nesses documentos, o CSS exige a correspondência de todos os elementos com esse ID. O próprio CSS não exige requisitos normativos para que os IDs sejam únicos; o texto que você cita é informativo.
Boris Zbarsky
5
@Boris Zbarsky O que o jQuery faz depende do caminho do código no jQuery. Em alguns casos, o jQuery usa a API NodeSelector (nativa querySelectorAll). Em outros casos, o Sizzle é usado. Sizzle não corresponde a vários IDs, mas o QSA (AYK). O caminho percorrido depende do seletor, do contexto e do navegador e sua versão. A API de consulta do jQuery usa o que eu chamei de "Primeiro nativo, abordagem dupla". Eu escrevi um artigo sobre isso, mas está em baixo. Embora você possa encontrar aqui: fortybelow.ca/hosted/dhtmlkitchen/JavaScript-Query-Engines.html
Garrett
5
Eu nem tenho certeza de que alguém, mas vocês dois possam descobrir o que está acontecendo com essa longa conversa. Temos bate-papo para essas discussões estendidas. Tudo o que você realmente deseja manter por perto deve ser colocado na pergunta ou resposta, especialmente se estiver esclarecendo informações. O estouro de pilha não lida bem com a discussão nos comentários.
precisa

Respostas:

824

Lembre-se de que, quando um navegador faz a correspondência de seletores, ele tem um elemento (aquele para o qual está tentando determinar o estilo) e todas as suas regras e seus seletores, e ele precisa descobrir quais regras correspondem ao elemento. Isso é diferente da coisa usual do jQuery, digamos, onde você só tem um seletor e precisa encontrar todos os elementos que correspondem a esse seletor.

Se você tiver apenas um seletor e apenas um elemento para comparar com esse seletor, da esquerda para a direita faz mais sentido em alguns casos. Mas essa definitivamente não é a situação do navegador. O navegador está tentando renderizar o Gmail ou o que quer que seja e possui o <span>que está tentando estilizar e as mais de 10.000 regras que o Gmail coloca em sua folha de estilo (não estou inventando esse número).

Em particular, na situação em que o navegador está olhando a maioria dos seletores que está considerando , não corresponde ao elemento em questão. Portanto, o problema passa a ser decidir se um seletor não corresponde o mais rápido possível; se isso exigir um pouco de trabalho extra nos casos correspondentes, você ainda ganha devido a todo o trabalho que você salva nos casos que não correspondem.

Se você começar apenas combinando a parte mais à direita do seletor com o seu elemento, é provável que não corresponda e pronto. Se corresponder, você precisará fazer mais trabalho, mas apenas proporcional à profundidade da sua árvore, o que não é tão grande na maioria dos casos.

Por outro lado, se você começar combinando a parte mais à esquerda do seletor ... contra o que você o combina? É necessário começar a percorrer o DOM, procurando nós que possam corresponder a ele. Apenas descobrir que não há nada que corresponda à parte mais à esquerda pode demorar um pouco.

Portanto, os navegadores correspondem da direita; fornece um ponto de partida óbvio e permite que você se livre da maioria dos seletores de candidatos rapidamente. Você pode ver alguns dados em http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665 (embora a notação seja confusa), mas o resultado é o Gmail em particular há dois anos, para 70% dos pares (regra, elemento) você pode decidir que a regra não corresponde depois de apenas examinar as partes de tag / class / id do seletor mais à direita da regra. O número correspondente para o conjunto de testes de desempenho de carregamento de páginas da Mozilla foi de 72%. Portanto, vale a pena tentar livrar-se dessas 2/3 de todas as regras o mais rápido possível e só se preocupar em combinar as 1/3 restantes.

Observe também que existem outras otimizações que os navegadores já fazem para evitar tentar corresponder a regras que definitivamente não coincidem. Por exemplo, se o seletor mais à direita tiver um ID e esse ID não corresponder ao ID do elemento, não haverá nenhuma tentativa de corresponder esse seletor com esse elemento no Gecko: o conjunto de "seletores com IDs" tentados vem de uma pesquisa de hashtable no ID do elemento. Portanto, essas são 70% das regras que têm uma boa chance de correspondência que ainda não coincidem depois de considerar apenas a tag / classe / ID do seletor mais à direita.

Boris Zbarsky
fonte
5
Como um pequeno bônus, faz mais sentido ler RTL do que LTR, mesmo em inglês. Um exemplo: stackoverflow.com/questions/3851635/css-combinator-precedence/…
BoltClock
6
Observe que a correspondência RTL se aplica apenas a combinadores . Ele não detalha o nível do seletor simples. Ou seja, um navegador pega o seletor composto mais à direita ou a sequência de seletores simples e tenta correspondê-lo atomicamente. Então, se houver uma correspondência, segue o combinador para a esquerda até o próximo seletor composto e verifica o elemento nessa posição e assim por diante. Não há evidências de que um navegador leia cada parte de um seletor composto RTL; de fato, o último parágrafo mostra exatamente o contrário (as verificações de identificação sempre vêm em primeiro lugar).
BoltClock
5
Na verdade, no momento em que você está combinando seletores, pelo menos no Gecko, o nome da tag e o espaço para nome vêm em primeiro lugar. O ID (assim como o nome da tag e os nomes de classe) é considerado em uma etapa de pré-filtragem que elimina a maioria das regras sem realmente tentar corresponder aos seletores.
Boris Zbarsky 31/08/2012
Isso pode ajudar dependendo das otimizações que o UA está fazendo, mas não ajudará na etapa de pré-filtragem no Gecko que descrevi acima. Há uma segunda etapa de filtragem que funciona em IDs e classes que são usadas apenas para combinadores descendentes, mas que podem ajudar.
Boris Zbarsky
@ Benito Ciaro: Sem mencionar problemas de especificidade também.
BoltClock
33

A análise da direita para a esquerda, também chamada de análise de baixo para cima, é realmente eficiente para o navegador.

Considere o seguinte:

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

O navegador primeiro verifica a, depois li, depois ule depois #menu.

Isso ocorre porque, como o navegador está varrendo a página, ele precisa apenas olhar o elemento / nó atual e todos os nós / elementos anteriores que ele varreu.

O ponto a ser observado é que o navegador começa a processar no momento em que obtém uma tag / nó completo e não precisa esperar a página inteira, exceto quando encontra um script, caso em que pausa temporariamente e conclui a execução do script e, em seguida, avança.

Se ocorrer o contrário, será ineficiente porque o navegador encontrou o elemento que estava sendo digitalizado na primeira verificação, mas foi forçado a continuar procurando no documento todos os seletores adicionais. Para isso, o navegador precisa ter o html inteiro e pode precisar digitalizar a página inteira antes de iniciar a pintura em css.

Isso é contrário ao modo como a maioria das bibliotecas analisa. Lá, o dom é construído e não é necessário escanear a página inteira, basta encontrar o primeiro elemento e, em seguida, continuar a combinar outros dentro dele.

aWebDeveloper
fonte
20

Permite cascata do mais específico para o menos específico. Também permite um curto-circuito na aplicação. Se a regra mais específica se aplicar a todos os aspectos aos quais a regra pai se aplica, todas as regras pai serão ignoradas. Se houver outros bits no pai, eles serão aplicados.

Se você seguisse o caminho inverso, formataria de acordo com o pai e substituiria toda vez que a criança tiver algo diferente. A longo prazo, isso é muito mais trabalhoso do que ignorar itens nas regras já tratadas.

Gregory A Beamer
fonte
11
Essa é uma questão separada. Você faz a cascata classificando as regras por especificidade e, em seguida, comparando-as em ordem de especificidade. Mas a questão aqui é por que, para uma determinada regra, você combina seus seletores de uma maneira específica.
Boris Zbarsky