Como adicionar interatividade com o JavaScript

Com o JavaScript, é possível modificar quase todos os aspectos da página: conteúdo, estilo e resposta à interação do usuário. No entanto, o JavaScript também pode bloquear a construção do DOM e atrasar a renderização da página. Para oferecer o desempenho ideal, torne seu JavaScript assíncrono e elimine qualquer JavaScript desnecessário do caminho crítico de renderização.

Resumo

  • O JavaScript pode consultar e modificar o DOM e o CSSOM.
  • A execução do JavaScript bloqueia o CSSOM.
  • O JavaScript bloqueia a construção do DOM, a menos que ela seja explicitamente declarada como assíncrona.

O JavaScript é uma linguagem dinâmica executada em um navegador que permite alterar praticamente todos os aspectos do comportamento da página. É possível modificar o conteúdo adicionando e removendo elementos da árvore do DOM, modificar as propriedades do CSSOM de cada elemento, lidar com entradas do usuário e muito mais. Para ilustrar isso, vamos ampliar nosso exemplo anterior de "Hello World" com um script inline simples:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script>
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline'; // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>

Faça um teste

  • O JavaScript nos permite acessar o DOM e extrair a referência ao nó de período oculto. O nó pode não estar visível na árvore de renderização, mas ainda está no DOM. Então, quando temos a referência, podemos alterar seu texto (via .textContent) e até mesmo substituir sua propriedade de estilo de exibição calculada de "none" para "inline". Agora a página vai mostrar a mensagem "Hello Interactive students!".

  • O JavaScript também permite criar, estilizar, anexar e remover novos elementos no DOM. Tecnicamente, nossa página inteira pode ser apenas um grande arquivo JavaScript que cria e estiliza os elementos um por um. Embora isso funcionasse, na prática, usar HTML e CSS é muito mais fácil. Na segunda parte da nossa função JavaScript, criamos um novo elemento div, definimos seu conteúdo de texto, definimos seu estilo e o anexamos ao corpo.

visualização da página

Com isso, modificamos o conteúdo e o estilo CSS de um nó DOM existente e adicionamos um nó totalmente novo ao documento. Nossa página não vai ganhar nenhum prêmio de design, mas ilustra o poder e a flexibilidade que o JavaScript nos oferece.

No entanto, embora o JavaScript nos ofereça muito poder, ele cria muitas limitações adicionais sobre como e quando a página é renderizada.

Primeiro, observe que no exemplo acima nosso script in-line está próximo à parte inferior da página. Por quê? Você deve tentar por conta própria, mas se movermos o script para acima do elemento span, você notará que o script falha e informa que não consegue encontrar uma referência a nenhum elemento span no documento. Ou seja, getElementsByTagName(‘span') retorna null. Isso demonstra uma propriedade importante: nosso script é executado no ponto exato em que é inserido no documento. Quando o analisador HTML encontra uma tag de script, pausa seu processo de construção do DOM e passa o controle para o mecanismo JavaScript. Depois que o mecanismo JavaScript termina de ser executado, o navegador retoma de onde parou e retoma a construção do DOM.

Em outras palavras, nosso bloco de script não consegue encontrar nenhum elemento mais tarde na página porque eles ainda não foram processados. Ou, de forma um pouco diferente: a execução do nosso script in-line bloqueia a construção do DOM, o que também atrasa a renderização inicial.

Outra propriedade sutil da introdução de scripts na nossa página é que eles podem ler e modificar não apenas o DOM, mas também as propriedades do CSSOM. Na verdade, é exatamente isso que estamos fazendo no nosso exemplo quando mudamos a propriedade de exibição do elemento span de none para inline. O resultado final? Agora temos uma disputa.

E se o navegador não tiver concluído o download e a criação do CSSOM quando quisermos executar o script? A resposta é simples, mas não muito boa para o desempenho: o navegador atrasa a execução do script e a construção do DOM até concluir o download e a criação do CSSOM.

Resumindo, o JavaScript introduz muitas novas dependências entre o DOM, o CSSOM e a execução do JavaScript. Isso pode causar atrasos significativos no navegador no processamento e na renderização da página na tela:

  • o local do script no documento é significativo.
  • Quando o navegador encontra uma tag de script, a construção do DOM é interrompida até que a execução do script seja concluída.
  • O JavaScript pode consultar e modificar o DOM e o CSSOM.
  • A execução do JavaScript é pausada até que o CSSOM esteja pronto.

De modo geral, "otimizar o caminho crítico de renderização" refere-se à compreensão e à otimização do gráfico de dependência entre HTML, CSS e JavaScript.

Bloqueio de analisador versus JavaScript assíncrono

Por padrão, a execução do JavaScript bloqueia o analisador: quando o navegador encontra um script no documento, ele deve pausar a construção do DOM, passar o controle ao tempo de execução do JavaScript e deixar o script ser executado antes de prosseguir com a construção do DOM. Vimos como isso funciona com um script em linha no exemplo anterior. Na verdade, os scripts in-line sempre bloqueiam o analisador, a menos que você crie código adicional para adiar a execução deles.

E os scripts incluídos por meio de uma tag script? Vamos usar nosso exemplo anterior e extrair o código para um arquivo separado:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script External</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js"></script>
  </body>
</html>

app.js

var span = document.getElementsByTagName('span')[0];
span.textContent = 'interactive'; // change DOM text content
span.style.display = 'inline'; // change CSSOM property
// create a new element, style it, and append it to the DOM
var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);

Faça um teste

Independentemente de usar uma tag <script> ou um snippet de JavaScript in-line, é esperado que ambos se comportem da mesma maneira. Em ambos os casos, o navegador faz uma pausa e executa o script antes de processar o restante do documento. No entanto, no caso de um arquivo JavaScript externo, o navegador precisa pausar para aguardar a busca do script no disco, no cache ou em um servidor remoto, o que pode adicionar dezenas a milhares de milissegundos de atraso ao caminho crítico de renderização.

Por padrão, todo JavaScript bloqueia o analisador. Como o navegador não sabe o que o script está planejando fazer na página, ele assume o pior cenário e bloqueia o analisador. Um sinal para o navegador de que o script não precisa ser executado no ponto exato em que é referenciado permite que o navegador continue construindo o DOM e deixe o script ser executado quando estiver pronto, por exemplo, depois que o arquivo for recuperado do cache ou de um servidor remoto.

Para isso, marcamos nosso script como async:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script Async</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

Faça um teste

Adicionar a palavra-chave "async" à tag do script instrui o navegador a não bloquear a construção do DOM enquanto aguarda a disponibilização do script, o que pode melhorar significativamente o desempenho.

Feedback