Posso executar o javascript antes de toda a página ser carregada?

Respostas:

186

Não só você pode , mas você deve fazer um esforço especial para não fazê-lo se não quiser. :-)

Quando o navegador encontra uma scripttag clássica ao analisar o HTML, ele para de analisar e passa para o interpretador JavaScript, que executa o script. O analisador não continua até que a execução do script seja concluída (porque o script pode fazer document.writechamadas para a marcação de saída que o analisador deve manipular).

Esse é o comportamento padrão, mas você tem algumas opções para atrasar a execução do script:

  1. Use módulos JavaScript. Um type="module"script é adiado até que o HTML tenha sido totalmente analisado e o DOM inicial criado. Este não é o principal motivo para usar módulos, mas é um dos motivos:

    <script type="module" src="./my-code.js"></script>
    <!-- Or -->
    <script type="module">
    // Your code here
    </script>

    O código será obtido (se for separado) e analisado em paralelo com a análise HTML, mas não será executado até que a análise HTML seja concluída. (Se o código do módulo estiver embutido em vez de em seu próprio arquivo, ele também será adiado até que a análise de HTML seja concluída.)

    Isso não estava disponível quando escrevi esta resposta pela primeira vez em 2010, mas aqui em 2020, todos os principais navegadores modernos suportam módulos nativamente, e se você precisar suportar navegadores mais antigos, pode usar bundlers como Webpack e Rollup.js.

  2. Use o deferatributo em uma tag de script clássico:

    <script defer src="./my-code.js"></script>

    Tal como acontece com o módulo, o código my-code.jsserá obtido e analisado em paralelo com a análise HTML, mas não será executado até que a análise HTML seja concluída. Mas , defernão funciona com conteúdo de script embutido, apenas com arquivos externos referenciados via src.

  3. Não acho que seja o que você quer, mas você pode usar o asyncatributo para dizer ao navegador para buscar o código JavaScript em paralelo com a análise de HTML, mas depois executá-lo o mais rápido possível, mesmo se a análise de HTML não estiver completa . Você pode colocá-lo em uma type="module"tag ou usá-lo em vez de deferem uma scripttag clássica .

  4. Coloque a scripttag no final do documento, antes da </body>tag de fechamento :

    <!doctype html>
    <html>
    <!-- ... -->
    <body>
    <!-- The document's HTML goes here -->
    <script type="module" src="./my-code.js"></script><!-- Or inline script -->
    </body>
    </html>

    Dessa forma, mesmo que o código seja executado assim que for encontrado, todos os elementos definidos pelo HTML acima dele existem e estão prontos para serem usados.

    Antigamente, isso causava um atraso adicional em alguns navegadores porque eles não começavam a buscar o código até que a scripttag fosse encontrada, mas os navegadores modernos fazem a varredura adiante e começam a pré-busca. Ainda assim, esta é a terceira escolha neste ponto, ambos os módulos e defersão opções melhores.

A especificação tem um diagrama útil mostrando uma matéria- scripttag, defer, async, type="module", e type="module" asynce o momento em que o código JavaScript é buscado e corrida:

insira a descrição da imagem aqui

Aqui está um exemplo do comportamento padrão, uma scripttag bruta :

.found {
    color: green;
}
<p>Paragraph 1</p>
<script>
    if (typeof NodeList !== "undefined" && !NodeList.prototype.forEach) {
        NodeList.prototype.forEach = Array.prototype.forEach;
    }
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

(Veja minha resposta aqui para detalhes sobre esse NodeListcódigo.)

Ao executá-lo, você verá "Parágrafo 1" em verde, mas "Parágrafo 2" em preto, porque o script foi executado em sincronia com a análise de HTML e, portanto, encontrou apenas o primeiro parágrafo, não o segundo.

Em contraste, aqui está um type="module"script:

.found {
    color: green;
}
<p>Paragraph 1</p>
<script type="module">
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

Observe como eles estão verdes agora; o código não foi executado até que a análise de HTML foi concluída. Isso também seria verdadeiro com um defer scriptconteúdo externo (mas não com conteúdo embutido).

(Não houve necessidade de NodeListverificar porque qualquer navegador moderno com suporte a módulos já está forEachativado NodeList.)

Neste mundo moderno, não há valor real para o DOMContentLoadedevento do recurso "pronto" que PrototypeJS, jQuery, ExtJS, Dojo e muitos outros forneciam (e ainda fornecem); apenas use módulos ou defer. Mesmo no passado, não havia muitos motivos para usá-los (e muitas vezes eram usados ​​incorretamente, segurando a apresentação da página enquanto toda a biblioteca jQuery era carregada porque o scriptestava no em headvez de depois do documento), algo que alguns desenvolvedores em O Google sinalizou no início. Isso também foi parte da razão para a recomendação da YUI de colocar scripts no final do dia body, novamente.

TJ Crowder
fonte
1
@FeRD - Obrigado pela edição. Os revisores rejeitaram, mas você estava certo. :-)
TJ Crowder
Ou coloque tudo em uma função e insira-o <body onload="doIt()>".
Justin Liu
@JustinLiu - O loadevento acontece muito tarde no ciclo de carregamento da página, quase nunca é uma prática recomendada esperar para executar seu JavaScript até então. Mas sim, é uma opção. Eu revisei a resposta para 2020, aliás.
TJ Crowder
Ou você pode usardocument.addEventListener('DOMContentLoaded', function(){ //doyour stuff })
Justin Liu
@JustinLiu - Sim, veja o último parágrafo. :-) Nunca vi necessidade, mas você pode.
TJ Crowder
6

Você pode executar o código javascript a qualquer momento. O AFAIK é executado no momento em que o navegador atinge a tag <script> onde está. Mas você não pode acessar elementos que ainda não foram carregados.

Portanto, se você precisa de acesso aos elementos, deve esperar até que o DOM seja carregado (isso não significa que toda a página seja carregada, incluindo imagens e outras coisas. É apenas a estrutura do documento, que é carregada muito antes, então você geralmente ganha perceber um atraso), usando o DOMContentLoadedevento ou funções como $.readyem jQuery.

Mariana
fonte
2
O evento DOMContentLoaded será acionado assim que a hierarquia DOM for totalmente construída, o evento load fará isso quando todas as imagens e subframes terminarem de carregar.
BeauCielBleu