ResizeObserver: é como document.onresize para elementos.

O ResizeObserver informa quando o tamanho de um elemento muda.

Antes de ResizeObserver, era preciso anexar um listener ao resize do documento para receber notificações sobre qualquer alteração nas dimensões da janela de visualização. No evento de eventos, você teria que descobrir quais elementos foram afetados pelo que mudam e chamam uma rotina específica para reagir adequadamente. Se você precisasse as novas dimensões de um elemento após um redimensionamento, era preciso chamar getBoundingClientRect() ou getComputedStyle(), o que pode causar o layout se você não agrupar todas as leituras e todas as suas gravações.

Isso nem mesmo abordou os casos em que os elementos mudavam de tamanho sem a parte principal foi redimensionada. Por exemplo, anexar novos filhos, definir uma o estilo display do elemento para none, ou ações semelhantes podem mudar o tamanho um elemento, seus irmãos ou seus ancestrais.

É por isso que ResizeObserver é um primitivo útil. Ele reage a mudanças tamanho de qualquer um dos elementos observados, independentemente do que causou a alteração. Ela também fornece acesso ao novo tamanho dos elementos observados.

Compatibilidade com navegadores

  • Chrome: 64.
  • Borda: 79.
  • Firefox: 69.
  • Safari: 13.1.

Origem

API

Todas as APIs com o sufixo Observer mencionado acima compartilham uma API simples do projeto. ResizeObserver não é exceção. Você cria um objeto ResizeObserver e passar um retorno de chamada para o construtor. O retorno de chamada 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 informado?

Geralmente, uma ResizeObserverEntry informa a caixa de conteúdo de um elemento por meio de uma propriedade chamada contentRect, que retorna uma DOMRectReadOnly objeto. A caixa de conteúdo é aquela em que o conteúdo pode ser colocado. É a caixa de borda menos o preenchimento.

Um diagrama do modelo de box CSS.

É importante observar que enquanto ResizeObserver informa as duas dimensões do contentRect e do padding, ele só observa o contentRect. Não confunda contentRect com a caixa delimitadora do elemento. O elemento delimitador , como relatado por getBoundingClientRect(), é a caixa que contém o o elemento inteiro e os descendentes dele. Os SVGs são uma exceção à regra, em que ResizeObserver informará as dimensões da caixa delimitadora.

A partir do Chrome 84, o ResizeObserverEntry tem três novas propriedades para oferecer mais informações detalhadas. Cada uma dessas propriedades retorna um ResizeObserverSize. que contém uma propriedade blockSize e uma propriedade inlineSize. Isso são sobre o elemento observado no momento em que a chamada de retorno é invocada.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

Todos esses itens retornam matrizes somente leitura porque, no futuro, espera-se que eles podem oferecer suporte a elementos com vários fragmentos, que ocorrem em de várias colunas. Por enquanto, essas matrizes vão conter apenas um elemento.

A compatibilidade com a plataforma para essas propriedades é limitada, mas o Firefox já oferece suporte as duas primeiras.

Quando ela está sendo informada?

A especificação determina que ResizeObserver precisa processar todos os eventos de redimensionamento antes da pintura e depois do layout. Isso torna o callback de um ResizeObserver lugar ideal para fazer alterações no layout da página. Porque ResizeObserver o processamento ocorre entre o layout e a pintura. Isso apenas invalida o layout, não a pintura.

Entendi

Você pode estar se perguntando: o que acontece se eu mudar o tamanho de uma área dentro do callback para ResizeObserver? A resposta é: você acionará outra chamada para o retorno imediatamente. Felizmente, o ResizeObserver tem um para evitar loops infinitos de callback e dependências cíclicas. As mudanças vão ser processados apenas no mesmo frame se o elemento redimensionado estiver mais profundo no DOM do que o elemento shallowest processado no callback anterior. Caso contrário, eles serão adiados para o próximo frame.

Aplicativo

Uma coisa que ResizeObserver permite que você faça é implementar por elemento consultas de mídia. Observando os elementos, é possível definir de modo imperativo projetar pontos de interrupção e mudar os estilos de um elemento. Nos seguintes example, a segunda caixa alterará o raio da borda de acordo com a largura.

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 é a janela de bate-papo. O problema que surge em um layout típico de conversa, de cima para baixo, é o posicionamento de rolagem. Para evitar o usuário, é útil se a janela ficar na parte inferior do em que as mensagens mais recentes aparecem. Além disso, qualquer tipo de layout mudar (pense em um celular mudando do modo paisagem para retrato ou vice-versa) conseguem o mesmo.

A ResizeObserver permite escrever apenas um trecho de código que cuida os dois cenários. O redimensionamento da janela é um evento que uma ResizeObserver pode capture por definição, mas chamar appendChild() também redimensiona esse elemento. (a menos que overflow: hidden esteja definido), porque ele precisa criar espaço para o novo os elementos. Com isso em mente, são necessárias poucas linhas para atingir o efeito:

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);

Interessante, não é?

A partir daqui, posso adicionar mais código para lidar com o caso em que o usuário rolou a tela manualmente e quer que a rolagem seja imposta à mensagem quando uma nova mensagem novos dados chegam.

Outro caso de uso é para qualquer tipo de elemento personalizado que esteja fazendo o próprio layout. Até ResizeObserver, não havia uma maneira confiável de receber notificações de quando seus as dimensões mudam para que os filhos possam ser dispostos novamente.

Efeitos na interação com a próxima exibição (INP, na sigla em inglês)

Interaction to Next Paint (INP) é uma métrica que mede a capacidade de resposta geral de uma página para as interações do usuário. Se o INP de uma página estiver em as boas limite, ou seja, 200 milissegundos ou menos, é possível dizer que a página responde de forma confiável ao as interações do usuário com ele.

O tempo necessário para executar callbacks de eventos em resposta a uma interação do usuário podem contribuir significativamente para a latência total de uma interação, esse não é o único aspecto da INP a ser considerado. A INP também considera a quantidade o tempo que leva para a next paint da interação ocorrer. Esta é a o tempo que leva para o trabalho de renderização necessário para atualizar o usuário interface em resposta a uma interação para ser concluída.

No caso de ResizeObserver, isso é importante porque o callback que uma instância do ResizerObserver é executada logo antes do trabalho de renderização. Isso ocorre por padrão, já que o trabalho que ocorre no callback precisa ser levado conta, pois esse trabalho provavelmente exigirá uma alteração na de uma ferramenta de visualização.

Tome cuidado para reduzir o trabalho de renderização necessário em um ResizeObserver. já que o excesso de trabalho de renderização pode criar situações em que o navegador atrasa a realização de um trabalho importante. Por exemplo, se alguma interação tem um callback que gera a execução de um callback ResizeObserver, verifique se você está fazendo o a seguir para facilitar a experiência:

  • Use os seletores de CSS o mais simples possível para evitar recálculo de estilo excessivo. Os recálculos de estilo ocorrem pouco antes do layout, e seletores de CSS complexos podem atrasar as operações de layout.
  • Evite fazer qualquer trabalho no callback ResizeObserver que possa ser acionado reflows forçados.
  • O tempo necessário para atualizar o layout de uma página geralmente aumenta com o número de elementos DOM em uma página. Embora isso seja verdade independentemente de as páginas usarem ou não ResizeObserver, o trabalho feito em um callback ResizeObserver pode se tornar significativo à medida que a complexidade estrutural da página aumenta.

Conclusão

O ResizeObserver está disponível em todas as principais navegadores e fornece uma maneira eficiente de monitorar o redimensionamento de elementos em um elemento nível No entanto, tome cuidado para não atrasar muito a renderização com essa API eficiente.