Suporte à camada superior no Chrome DevTools

Alina Varkki
Alina Varkki

O Chrome DevTools está adicionando suporte aos elementos da camada superior, facilitando a depuração do código que usa esses elementos.

Este artigo descreve o que são os elementos da camada superior, como o DevTools ajuda a visualizar o conteúdo da camada superior para entender e depurar a estrutura DOM que contém os elementos da camada superior e como o suporte à camada superior do DevTools é implementado.

Quais são os elementos da camada superior e da camada superior?

O que acontece exatamente quando você abre um <dialog> como modal? 🤔

Ele é colocado em uma camada superior. O conteúdo da camada superior é renderizado sobre todos os outros conteúdos. Por exemplo, uma caixa de diálogo modal precisa aparecer sobre todos os outros conteúdos do DOM, para que o navegador renderize automaticamente esse elemento em uma "camada superior", em vez de forçar os autores a combater o Z-index manualmente. Um elemento da camada superior aparece sobre um elemento, mesmo com o Z-index mais alto.

A camada superior pode ser descrita como "a camada de empilhamento mais alto". Cada documento tem uma única janela de visualização associada e, portanto, também uma única camada superior. Vários elementos podem estar dentro da camada superior ao mesmo tempo. Quando isso acontece, elas empilham umas sobre as outras, com a última em cima. Em outras palavras, todos os elementos da camada superior são colocados em uma pilha last in, first out (LIFO, na sigla em inglês) na camada superior.

O elemento <dialog> não é o único renderizado pelo navegador em uma camada superior. Atualmente, os elementos da camada superior são: popovers, caixas de diálogo modais e elementos em modo de tela cheia.

Analise a seguinte implementação de caixas de diálogo:

<main>
  <button onclick="window.dialog.showModal();">Open Dialog</button>
</main>
<dialog id="dialog"></dialog>

Veja uma demonstração com algumas caixas de diálogo com estilos aplicados aos panos de fundo (panos de fundo descritos abaixo):

O que é um pano de fundo?

Felizmente, há uma forma de personalizar o conteúdo abaixo do elemento da camada superior.

Cada elemento na camada superior tem um pseudoelemento CSS chamado pano de fundo.

O Backdrop é uma caixa do tamanho da janela de visualização que é renderizada imediatamente abaixo de qualquer elemento da camada superior. Com o pseudoelemento ::backdrop, é possível escurecer, estilizar ou ocultar completamente tudo o que estiver abaixo do elemento quando for o item de nível mais alto na camada superior.

Quando você torna vários elementos modais, o navegador desenha o pano de fundo imediatamente abaixo do elemento mais à frente e sobre outros elementos em tela cheia.

Veja como definir o estilo de um pano de fundo:

/* The browser displays the backdrop only when the dialog.showModal() function opens the dialog.*/
dialog::backdrop {
    background: rgba(255,0,0,.25);
}

Como mostrar apenas o primeiro pano de fundo?

Cada elemento da camada superior tem um pano de fundo que pertence a uma pilha de camada superior. Esses panos de fundo são projetados para se sobrepor. Portanto, se a opacidade de um pano de fundo não for 100%, os panos de fundo inferiores serão visíveis.

Se apenas o primeiro pano de fundo na pilha de camada superior precisa estar visível, faça isso monitorando os identificadores de item dessa pilha.

Se o elemento adicionado não for o primeiro na camada de cima, a função chamada quando ele for colocado na camada de cima vai aplicar uma classe hiddenBackdrop ao ::backdrop. Essa classe é removida quando o elemento é removido da camada superior.

Confira o código nesta demonstração de exemplo:

Design de suporte da camada superior no DevTools

O suporte do DevTools para a camada superior ajuda os desenvolvedores a entender o conceito e visualizar como esse conteúdo muda. Esses recursos ajudam os desenvolvedores a identificar o seguinte:

  • Os elementos na camada superior a qualquer momento e a ordem deles.
  • O elemento no topo da pilha em qualquer ponto.

Além disso, o suporte à camada superior do DevTools ajuda a visualizar a posição do pseudoelemento do pano de fundo na pilha da camada superior. Mesmo que não seja um elemento de árvore, ele desempenha um papel importante no funcionamento da camada superior e pode ser útil para desenvolvedores.

Com os recursos de suporte à camada superior, você pode:

  1. Observe quais elementos estão na pilha da camada superior a qualquer momento. A pilha de representação da camada superior muda dinamicamente conforme os elementos são adicionados ou removidos da camada superior.
  2. Confira a posição do elemento na pilha da camada superior.
  3. Vá do pseudoelemento da camada superior ou do pano de fundo dos elementos na árvore até o pseudoelemento do elemento ou do pano de fundo no contêiner de representação da camada superior e vice-versa.

Vamos conferir como usar esses recursos.

Contêiner da camada superior

Para ajudar na visualização dos elementos da camada superior, o DevTools adiciona um contêiner da camada superior à árvore de elementos. Ele fica após a tag </html> de fechamento.

Esse contêiner permite observar os elementos na pilha da camada superior a qualquer momento. O contêiner da camada superior é uma lista de links para os elementos da camada superior e seus panos de fundo. A pilha de representação da camada superior muda dinamicamente conforme os elementos são adicionados ou removidos da camada superior.

Para encontrar os elementos da camada superior dentro da árvore de elementos ou do contêiner da camada superior, clique nos links da representação do elemento da camada superior no contêiner da camada superior para o mesmo elemento na árvore de elementos e vice-versa.

Para pular do elemento de contêiner da camada superior para o elemento da árvore da camada superior, clique no botão revelar ao lado do elemento no contêiner da camada superior.

Pulando do link do contêiner da camada superior para o elemento.

Para pular do elemento da árvore da camada superior para o link no contêiner da camada superior, clique no selo da camada superior ao lado do elemento.

Pulando de um elemento para o link do contêiner da camada superior.

É possível desativar qualquer selo, incluindo o de camada superior. Para desativar os selos, clique com o botão direito em qualquer um, escolha Configurações de selos e desmarque as marcações ao lado dos selos que você quer ocultar.

Desativando o selo.

Ordem dos elementos na pilha da camada superior

O contêiner da camada superior mostra os elementos como eles aparecem na pilha, mas na ordem inversa. O topo do elemento de pilha é o último na lista de elementos do contêiner da camada superior. Isso significa que o último elemento na lista de contêineres da camada superior é o elemento com que você pode interagir no documento no momento.

Os selos ao lado dos elementos da árvore indicam se os elementos pertencem à camada superior e contêm o número da posição de um elemento na pilha.

Nesta captura de tela, a pilha da camada superior consiste em dois elementos, com o segundo elemento no topo da pilha. Se você remover o segundo elemento, o primeiro vai para a parte superior.

A ordem dos elementos na pilha.

Panos de fundo no contêiner da camada superior

Como mencionado acima, cada elemento da camada superior tem um pseudoelemento CSS chamado pano de fundo. Como é possível definir o estilo desse elemento, é importante também inspecioná-lo e ver a representação dele.

Na árvore de elementos, um elemento do pano de fundo reside antes da tag de fechamento do elemento a que ele pertence. No entanto, no contêiner da camada superior, um link do pano de fundo é listado logo acima do elemento da camada superior ao qual ele pertence.

Posição da pilha dos panos de fundo.

Alterações na árvore do DOM

ElementsTreeElement, a classe responsável por criar e gerenciar elementos individuais da árvore DOM no DevTools, não foi suficiente para implementar um contêiner da camada superior.

Para exibir o contêiner da camada superior como um nó na árvore, adicionamos uma nova classe que cria nós do elemento de árvore do DevTools. Anteriormente, a classe responsável por criar a árvore de elementos do DevTools inicializava cada TreeElement com um DOMNode, que é uma classe com um backendNodeId e outras propriedades relacionadas ao back-end. backendNodeId, por sua vez, é atribuído no back-end.

O nó do contêiner da camada superior, que tem uma lista de links para elementos da camada superior, precisa se comportar como um nó do elemento da árvore normal. No entanto, esse nó não é um nó DOM "real", e o back-end não precisa criar o nó de contêiner da camada superior.

Para criar um nó de front-end que represente a camada superior, adicionamos um novo tipo de nó de front-end que é criado sem um DOMNode. Esse elemento de contêiner da camada superior é o primeiro nó de front-end que não tem um DOMNode, o que significa que ele existe apenas no front-end e o back-end não "sabe" dele. Para ter o mesmo comportamento que outros nós, criamos uma nova classe TopLayerContainer que estende a classe UI.TreeOutline.TreeElement, responsável pelo comportamento dos nós de front-end.

Para conseguir a posição desejada, a classe que renderiza um elemento anexa TopLayerContainer como o próximo irmão da tag <html>.

Um novo selo de camada superior indica que o elemento está na camada superior e serve como um link para o atalho dele no elemento TopLayerContainer.

Design inicial

Inicialmente, o plano era duplicar os elementos da camada superior no contêiner da camada superior em vez de criar uma lista de links para os elementos. Não implementamos essa solução devido à forma como a busca de filhos do elemento funciona no DevTools. Cada elemento tem um ponteiro pai usado para buscar filhos, e é impossível ter vários ponteiros. Portanto, não podemos ter um nó que se expanda corretamente e contenha todos os filhos em vários locais da árvore. Em geral, o sistema não foi criado pensando em subárvores duplicadas.

O compromisso que chegamos era criar links para os nós DOM front-end em vez de duplicar esses nós. A classe responsável por criar links para elementos no DevTools é ShortcutTreeElement, que estende a classe UI.TreeOutline.TreeElement. ShortcutTreeElement tem o mesmo comportamento que outros elementos da árvore DOM do DevTools, mas não tem um nó correspondente no back-end e tem um botão que é vinculado a um ElementsTreeElement. Cada ShortcutTreeElement para o nó da camada superior tem um ShortcutTreeElement filho que é vinculado à representação de um pseudoelemento ::backdrop na árvore DOM do DevTools.

Design inicial:

Projeto inicial.

Mudanças no protocolo do Chrome DevTools (CDP)

Para implementar o suporte da camada superior, são necessárias mudanças no protocolo do Chrome DevTools (CDP). O CDP atua como um protocolo de comunicação entre o DevTools e o Chromium.

Precisamos adicionar o seguinte:

  • Um comando para chamar do front-end a qualquer momento.
  • Um evento a ser acionado no front-end pelo back-end.

CDP: comando DOM.getTopLayerElements

Para exibir os elementos atuais da camada superior, precisamos de um novo comando CDP experimental que retorne uma lista de IDs dos elementos que estão na camada superior. O DevTools chama esse comando sempre que é aberto ou quando os elementos da camada superior mudam. O comando se parece com isto:

  # Returns NodeIds of the current top layer elements.
  # Top layer renders closest to the user within a viewport, therefore, its elements always
  # appear on top of all other content.
  experimental command getTopLayerElements
    returns
      # NodeIds of the top layer elements.
      array of NodeId nodeIds

CDP: DOM.topLayerElementsUpdated evento

Para acessar a lista atualizada dos elementos da camada superior, é necessário fazer todas as alterações nesses elementos para acionar um evento experimental de CDP. Esse evento informa o front-end da mudança, que chama o comando DOM.getTopLayerElements e recebe a nova lista de elementos.

O evento é semelhante ao seguinte:

  # Called by the change of the top layer elements.
  experimental event topLayerElementsUpdated

Considerações sobre o CDP

Havia várias opções de implementação do suporte ao CDP da camada superior. Outra opção que consideramos foi criar um evento que retornasse a lista dos elementos da camada superior em vez de apenas informar ao front-end sobre a adição ou remoção de um elemento da camada superior.

Como alternativa, podemos criar dois eventos em vez do comando: topLayerElementAdded e topLayerElementRemoved. Nesse caso, receberíamos um elemento e teríamos que gerenciar a matriz dos elementos da camada superior no front-end.

No momento, um evento de front-end chama o comando getTopLayerElements para acessar uma lista de elementos atualizados. Se enviássemos uma lista de elementos ou um elemento específico que causou a alteração sempre que um evento for acionado, poderíamos evitar uma etapa de chamada do comando. No entanto, nesse caso, o front-end perderia o controle sobre quais elementos são enviados.

Nós o implementamos dessa forma porque, na nossa opinião, é melhor que o front-end decida quando solicitar os nós da camada superior. Por exemplo, se a camada superior estiver recolhida na IU ou o usuário estiver usando um painel do DevTools sem a árvore de elementos, não será necessário acessar os nós extras que poderiam estar mais profundos na árvore.