ResizeObserver
informa quando o tamanho de um elemento muda.
Antes de ResizeObserver
, era necessário anexar um listener ao evento resize
do documento para receber notificações sobre qualquer mudança nas dimensões da janela de visualização. No manipulador
de eventos, você teria que descobrir quais elementos foram afetados por
essa mudança e chamar uma rotina específica para reagir de maneira adequada. Se você precisasse
das novas dimensões de um elemento após um redimensionamento, teria que chamar
getBoundingClientRect()
ou getComputedStyle()
, o que pode causar
uma sobrecarga no layout se você não agrupar todas as leituras e todas as
gravações.
Isso nem mesmo abrangeu casos em que os elementos mudam de tamanho sem que a janela
principal fosse redimensionada. Por exemplo, anexar novos filhos, definir o
estilo display
de um elemento como none
ou ações semelhantes podem mudar o tamanho de
um elemento, de seus irmãos ou de seus ancestrais.
É por isso que ResizeObserver
é um primitivo útil. Ele reage a mudanças no
tamanho de qualquer um dos elementos observados, independente do que causou a mudança.
Ela também fornece acesso ao novo tamanho dos elementos observados.
API
Todas as APIs com o sufixo Observer
mencionado acima têm um design simples. ResizeObserver
não é exceção. Crie um objeto ResizeObserver
e transmita um callback para o construtor. O callback recebe uma matriz de objetos ResizeObserverEntry
(uma entrada por elemento observado), que contém as novas dimensões do elemento.
var ro = new ResizeObserver(entries => {
for (let entry of entries) {
const cr = entry.contentRect;
console.log('Element:', entry.target);
console.log(`Element size: ${cr.width}px x ${cr.height}px`);
console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
}
});
// Observe one or multiple elements
ro.observe(someElement);
Alguns detalhes
O que está sendo denunciado?
Geralmente, um
ResizeObserverEntry
informa a caixa de conteúdo de um elemento com uma propriedade chamada
contentRect
, que retorna um
objeto
DOMRectReadOnly
. A caixa de conteúdo é a caixa em que o conteúdo pode ser colocado. É a caixa de borda menos o padding.
É importante observar que, embora ResizeObserver
informe as dimensões
do contentRect
e do padding, ele apenas observa o contentRect
.
Não confunda contentRect
com a caixa delimitadora do elemento. A caixa delimitadora, conforme informado por getBoundingClientRect()
, é a caixa que contém o elemento inteiro e os descendentes dele. Os SVGs são uma exceção à regra, em que
ResizeObserver
informa as dimensões da caixa delimitadora.
A partir do Chrome 84, ResizeObserverEntry
tem três novas propriedades para fornecer
informações mais detalhadas. Cada uma dessas propriedades retorna um objeto ResizeObserverSize
que contém uma propriedade blockSize
e uma propriedade inlineSize
. Essas
informações são sobre o elemento observado no momento em que o callback é invocado.
borderBoxSize
contentBoxSize
devicePixelContentBoxSize
Todos esses itens retornam matrizes somente leitura porque, no futuro, espera-se que eles ofereçam suporte a elementos com vários fragmentos, que ocorrerão em cenários com várias colunas. Por enquanto, essas matrizes conterão apenas um elemento.
O suporte da plataforma a essas propriedades é limitado, mas o Firefox já é compatível com as duas primeiras.
Quando ela está sendo reportada?
A especificação determina que ResizeObserver
precisa processar todos os eventos de redimensionamento
antes da pintura e depois do layout. Isso faz do callback de um ResizeObserver
o
local ideal para fazer mudanças no layout da página. Como o processamento de ResizeObserver
acontece entre layout e pintura, isso vai invalidar o
layout, não a pintura.
Peguei você
Você pode estar se perguntando: o que acontece se eu mudar o tamanho de um elemento
observado dentro do callback para ResizeObserver
? A resposta é: você acionará
outra chamada imediatamente para o callback. Felizmente, ResizeObserver
tem um
mecanismo para evitar loops de callback infinitos e dependências cíclicas. As alterações só serão processadas no mesmo frame se o elemento redimensionado estiver mais profundo na árvore DOM do que o elemento shallowest processado no callback anterior.
Caso contrário, eles serão adiados para o próximo frame.
legado
Uma coisa que ResizeObserver
permite que você faça é implementar consultas de mídia
por elemento. Observando os elementos, é possível definir de forma imperativa os
pontos de interrupção do design e mudar os estilos de um elemento. No exemplo a seguir, a segunda caixa mudará o raio da borda de acordo com a largura dela.
const ro = new ResizeObserver(entries => {
for (let entry of entries) {
entry.target.style.borderRadius =
Math.max(0, 250 - entry.contentRect.width) + 'px';
}
});
// Only observe the second box
ro.observe(document.querySelector('.box:nth-child(2)'));
Outro exemplo interessante é uma janela de bate-papo. O problema que surge em um layout de conversa de cima para baixo típico é o posicionamento de rolagem. Para evitar confundir o usuário, é recomendável que a janela permaneça no final da conversa, onde as mensagens mais recentes aparecem. Além disso, qualquer tipo de mudança de layout (pense em um smartphone que muda da orientação de paisagem para retrato ou vice-versa) terá o mesmo resultado.
ResizeObserver
permite que você escreva um único código para lidar com
os dois cenários. Redimensionar a janela é um evento que um ResizeObserver
pode
capturar por definição, mas chamar appendChild()
também redimensiona esse elemento
(a menos que overflow: hidden
esteja definido), porque ele precisa liberar espaço para os novos
elementos. Pensando nisso, são necessárias poucas linhas para alcançar o efeito
pretendido:
const ro = new ResizeObserver(entries => {
document.scrollingElement.scrollTop =
document.scrollingElement.scrollHeight;
});
// Observe the scrollingElement for when the window gets resized
ro.observe(document.scrollingElement);
// Observe the timeline to process new messages
ro.observe(timeline);
Isso é muito legal, não é?
Aqui, é possível adicionar mais código para processar o caso em que o usuário rolou para cima manualmente e quer que a rolagem siga essa mensagem quando uma nova chegar.
Outro caso de uso é para qualquer tipo de elemento personalizado que tenha o próprio layout.
Até ResizeObserver
, não havia uma maneira confiável de ser notificado quando as
dimensões mudassem para que os filhos pudessem ser dispostos novamente.
Efeitos na interação com a próxima exibição (INP, na sigla em inglês)
A Interação com a próxima exibição (INP, na sigla em inglês) é uma métrica que mede a capacidade geral de uma página em relação às interações do usuário. Se o INP de uma página estiver no limite "bom", ou seja, 200 milissegundos ou menos, é possível dizer que a página é responsiva de forma confiável às interações do usuário com ela.
O tempo necessário para executar callbacks de eventos em resposta a uma interação do usuário pode contribuir significativamente para a latência total de uma interação, mas esse não é o único aspecto do INP a ser considerado. O INP também considera o tempo necessário para a próxima exibição da interação. Esse é o tempo necessário para que o trabalho de renderização atualize a interface do usuário em resposta a uma interação para concluir.
Quando ResizeObserver
está relacionado, isso é importante porque o callback que
uma instância ResizerObserver
executa ocorre logo antes do trabalho de renderização. Isso
é intencional, já que o trabalho que ocorre no callback precisa ser considerado, porque o resultado desse trabalho vai exigir uma mudança na
interface do usuário.
Realize o mínimo de trabalho de renderização necessário em um callback ResizeObserver
, porque o excesso de renderização pode criar situações em que o navegador
atrasa para realizar tarefas importantes. Por exemplo, se alguma interação tiver um
callback que faça com que um callback ResizeObserver
seja executado, faça o
seguinte para facilitar a experiência mais tranquila possível:
- Verifique se os seletores de CSS são o mais simples possível para evitar trabalho excessivo de recálculo de estilo. Os recálculos de estilo ocorrem logo antes do layout, e seletores de CSS complexos podem atrasar operações de layout.
- Evite realizar qualquer trabalho no callback
ResizeObserver
que possa acionar reflows forçados. - O tempo necessário para atualizar o layout de uma página geralmente aumenta com o
número de elementos DOM de uma página. Isso é válido independentemente de as páginas usarem ou não
ResizeObserver
, mas o trabalho realizado em um callbackResizeObserver
pode se tornar significativo à medida que a complexidade estrutural de uma página aumenta.
Conclusão
O ResizeObserver
está disponível em todos os principais navegadores e fornece uma maneira eficiente de monitorar redimensionamentos de elementos no nível do elemento. Só tome cuidado para não atrasar muito a renderização com essa API poderosa.