ResizeObserver - uma nova ferramenta poderosa para trabalhar com adaptabilidade

Bom dia amigos

Responsive é um dos padrões de desenvolvimento web. Há um grande número de resoluções de tela, e esse número está aumentando o tempo todo. Nós nos esforçamos para oferecer suporte a todos os tamanhos de tela possíveis, mantendo uma interface de usuário amigável. Uma excelente solução para esse problema são as consultas de mídia (consultas de mídia). Mas e os componentes da web? O desenvolvimento moderno da Web é baseado em componentes e precisamos de uma maneira de torná-los responsivos. Hoje, quero falar sobre a API ResizeObserver, que permite monitorar (observar) alterações no tamanho de um elemento específico, e não na janela de exibição inteira, como é o caso das consultas de mídia.

Um pouco de história


Anteriormente, tínhamos apenas consultas de mídia à nossa disposição - uma solução CSS baseada no tamanho, tipo e resolução da tela de um dispositivo de mídia (por um dispositivo de mídia, quero dizer um computador, telefone ou tablet). As consultas de mídia são bastante flexíveis e fáceis de usar. Por um longo tempo, as consultas de mídia estavam disponíveis apenas em CSS, agora também estão disponíveis em JS através de window.matchMedia (mediaQueryString). Agora podemos verificar em qual dispositivo a página é exibida e também monitorar a alteração no tamanho da área de visualização (estamos falando do método MediaQueryList.addListener () - aprox.

Consultas de elemento


O que nos faltava era a capacidade de monitorar o tamanho de um único elemento DOM, e não apenas a janela de exibição inteira. Os desenvolvedores reclamam disso há muitos anos. Este é um dos recursos mais esperados. Em 2015, até uma proposta foi apresentada - solicitações de tamanhos de contêineres ( consultas de contêineres ):
Os desenvolvedores geralmente precisam da capacidade de estilizar elementos quando redimensionam seu contêiner pai, independentemente da área de visualização. Pedidos de tamanhos de contêineres oferecem a eles essa oportunidade. Exemplo de uso de CSS:
tela .element: media (largura mínima: 30em) {***}
Parece ótimo, mas os fornecedores de navegadores tiveram um bom motivo para rejeitar esta proposta - dependência circular (dependência circular) (quando um tamanho define outro, isso leva a um loop infinito (mais sobre isso pode ser encontrado aqui ). Que outras opções existem? Podemos usar window.resize (retorno de chamada), mas esse é um "prazer caro" - um retorno de chamada será chamado toda vez que ocorrer um evento e precisaremos de muitos cálculos para determinar se o tamanho do nosso componente realmente mudou ...

Monitorando o redimensionamento do elemento usando a API ResizeObserver


Conheça a API ResizeObserver do Chrome:
A API ResizeObserver é uma interface para rastrear o redimensionamento de elementos. Este é um tipo de análogo do evento window.resize para um elemento.

A API ResizeObserver é um rascunho ativo. Já está implementado no Chrome, Firefox e Safari para PC. O suporte móvel é menos impressionante - apenas o Chrome no Android e na Internet da Samsung. Infelizmente, um polifilo completo não existe. Os polifiles disponíveis contêm algumas limitações (por exemplo, uma resposta lenta ao redimensionamento ou falta de suporte para uma transição suave). No entanto, isso não deve impedir-nos de testar esta API. Então, vamos fazê-lo!

Exemplo: alterando texto ao redimensionar um elemento


Imagine a seguinte situação - o texto dentro do elemento deve mudar dependendo do tamanho do elemento. A API ResizeObserver fornece duas ferramentas - ResizeObserver e ResizeObserverEntry. ResizeObserver é usado para rastrear o tamanho de um item e ResizeObserverEntry contém informações sobre o item que foi redimensionado.
O código é muito simples:

<h1> size </h1>
<h2> boring text </h2>

const ro = new ResizeObserver(entries => {
    for(let entry of entries){
        const width = entry.contentBoxSize
        ? entry.contentBoxSize.inlineSize
        : entry.contentRect.width

        if(entry.target.tagName === 'H1'){
            entry.target.textContent = width < 1000 'small' : 'big'
        }

        if(entry.target.tagName === 'H2' && width < 500){
            entry.target.textContent = `I won't change anymore`
            ro.unobserve(entry.target) //  ,     500px
        }
    }
})

//       
ro.observe(document.querySelector('h1'))
ro.observe(document.querySelector('h2'))

Primeiro, crie um objeto ResizeObserver e passe uma função de retorno de chamada para ele como um parâmetro:

const resizeObserver = new ResizeObserver((entries, observer) => {
    for(let entry of entries){
        // 
    }
})

A função é chamada sempre que um dos elementos contidos em ResizeObserverEntries é redimensionado. O segundo parâmetro da função de retorno de chamada é o próprio observador. Podemos usá-lo, por exemplo, para interromper o rastreamento quando uma determinada condição é atendida.

O retorno de chamada obtém uma matriz de ResizeObserverEntry. Cada entrada contém as dimensões do elemento observado (destino).

for(let entry of entries){
    const width = entry.contentBoxSize
    ? entry.contentBoxSize.inlineSize
    : entry.contentRect.width

    if(entry.target.tagName === 'H1'){
        entry.target.textContent = width < 1000 ? 'small' : 'big'
    }
    ...
}

Temos três propriedades que descrevem o tamanho de um elemento - borderBoxSize, contentBoxSize e contentRect. Eles representam o modelo de caixa do elemento, sobre o qual falaremos mais adiante. Agora, algumas palavras sobre suporte. A maioria dos navegadores suporta contentRect, no entanto, aparentemente, essa propriedade será preterida:
O contentRect apareceu no estágio de desenvolvimento preliminar do ResizeObserver e foi adicionado apenas no interesse da compatibilidade atual. Provavelmente no futuro será considerado obsoleto.


Portanto, eu recomendo usar o contentRect em conjunto com bordeBoxSize e contentBoxSize. ResizeObserverSize inclui duas propriedades: inlineSize e blockSize, que podem ser interpretadas como largura e altura (desde que trabalhemos na orientação horizontal do texto - modo de gravação: horizontal).

Observação de elementos


A última coisa a fazer é começar a rastrear o item. Para fazer isso, chamamos ResizeObserver.observe (). Este método adiciona um novo destino à lista de itens observados. Podemos adicionar a esta lista um ou vários elementos de uma vez:

// resizeObserver(target, options)
ro.observe(document.querySelector('h1'))
ro.observe(document.querySelector('h2'))

O segundo parâmetro é "opcional". Até o momento, a única opção disponível é a caixa, que define um modelo de bloco. Os valores possíveis são caixa de conteúdo (padrão), caixa de borda e caixa de conteúdo de pixel do dispositivo (somente Chrome). Somente um modelo de bloco pode ser definido em um ResizeObserver.

Para parar o monitoramento, use ResizeObserver.unobserve (target). Para parar de rastrear todos os elementos, use ResizeObserver.disconnect ().

Modelo de bloco


Caixa de conteúdo é o conteúdo do bloco sem preenchimento, borda e margem. A caixa Borda inclui preenchimento e borda (sem margem).



Caixa de conteúdo de pixels do dispositivo é o conteúdo do elemento em pixels físicos. Não vi exemplos do uso desse modelo, mas parece que isso pode ser útil ao trabalhar com canvas. Aqui está uma discussão interessante sobre esse tópico no Github.

Quando um observador descobre mudanças?


Um retorno de chamada é chamado sempre que o elemento de destino é redimensionado. Aqui está o que a especificação diz sobre isso:

  • A observação é acionada quando um item observado é adicionado / removido do DOM.
  • A observação é acionada quando a propriedade de exibição do elemento observado é definida como nenhuma.
  • A observação não funciona para elementos de linha "não substituídos".
  • A observação não funciona na transformação de CSS.
  • , , .. , 0,0.

De acordo com o primeiro parágrafo, podemos determinar a alteração no contêiner pai ao alterar seus filhos. Um ótimo exemplo desse uso da API ResizeObserver é rolar a janela de bate-papo ao adicionar uma nova mensagem. Um exemplo pode ser visto aqui .

Lembra das consultas de tamanho de contêiner que mencionei anteriormente? Sobre o seu problema de dependência circular? Portanto, a API ResizeObserver possui uma solução interna para impedir um loop infinito de "redimensionamento". Leia aqui .

Obrigado pela atenção.

Links úteis:

Especificação
MDN
CanIUse
Primeiro artigo da equipe de desenvolvimento
Polyfil mais popular

Source: https://habr.com/ru/post/undefined/


All Articles