Por que dividir a tag <script> ao escrevê-la com document.write ()?


268

Por que alguns sites (ou anunciantes que fornecem código javascript aos clientes) empregam uma técnica de dividir as tags <script>e / ou </script>tags nas document.write()chamadas?

Notei que a Amazon também faz isso, por exemplo:

<script type='text/javascript'>
  if (typeof window['jQuery'] == 'undefined') document.write('<scr'+'ipt type="text/javascript" src="http://z-ecx.images-amazon.com/images/G/01/javascripts/lib/jquery/jquery-1.2.6.pack._V265113567_.js"></sc'+'ript>');
</script>

Respostas:


373

</script>precisa ser interrompido porque, caso contrário, encerraria o <script></script>bloco anexo muito cedo. Realmente, ele deve ser dividido entre the <e the /, porque um bloco de script deve (de acordo com SGML) terminar com qualquer sequência de fim de tag aberta (ETAGO) (ou seja </) :

Embora os elementos STYLE e SCRIPT usem CDATA para seu modelo de dados, para esses elementos, o CDATA deve ser tratado de maneira diferente pelos agentes do usuário. A marcação e as entidades devem ser tratadas como texto bruto e passadas para o aplicativo como estão. A primeira ocorrência da sequência de caracteres " </" (delimitador aberto da tag final) é tratada como finalização do final do conteúdo do elemento. Em documentos válidos, essa seria a tag final do elemento.

No entanto, na prática, os navegadores apenas finalizam a análise de um bloco de script CDATA em uma </script>marca de fechamento real .

No XHTML não existe um tratamento especial para blocos de script, portanto, qualquer <(ou &) caractere dentro deles deve ser &escaped;semelhante a qualquer outro elemento. No entanto, os navegadores que estão analisando XHTML como HTML da velha escola ficarão confusos. Existem soluções alternativas que envolvem blocos CDATA, mas é mais fácil simplesmente evitar o uso desses caracteres sem escape. Uma maneira melhor de escrever um elemento de script a partir de um script que funcione em qualquer tipo de analisador seria:

<script type="text/javascript">
    document.write('\x3Cscript type="text/javascript" src="foo.js">\x3C/script>');
</script>

30
\/é uma sequência de escape válida para /, então, por que não apenas usá-la em vez dessas sequências literais de escape <? Por exemplo document.write('<script src=foo.js><\/script>');. Além disso, </script>não é a única sequência de caracteres que pode fechar um <script>elemento. Mais informações aqui: mathiasbynens.be/notes/etago
— Mathias Bynens

11
@ Matias: <\/script>está bem neste caso, mas só funcionaria em HTML; no XHTML sem quebra de seção CDATA extra, ainda é um erro de formação. Além disso, você pode usar \x3Catributos de manipulador de eventos em linha, onde <também seria inválido tanto em HTML quanto em XHTML, para ter uma aplicabilidade mais ampla: se eu estivesse escolhendo uma maneira fácil e automatizada de escapar de caracteres sensíveis em literais de string JS para todos os contextos, isso é o que eu escolheria.
— bobince

3
Em HTML, <pode ser usado em atributos de manipulador de eventos embutidos. html5.validator.nu/… E você está certo sobre a compatibilidade XHTML de \x3Cum sich, mas como o XHTML não suporta document.write(ou innerHTML) de qualquer maneira, não vejo como isso é relevante.
— Mathias Bynens

2
@ MathiasBynens - document.writeé irrelevante, apenas é o exemplo. O OP poderia ter usado innerHTML, é sobre como ocultar a </sequência de caracteres do analisador de marcação, onde quer que ocorra. É que a maioria dos analisadores o tolera dentro de um elemento de script quando estritamente não deveriam (mas os analisadores de HTML são muito tolerantes). Você está correto, porém, <\/em todos os casos, basta para HTML.
— RobG

3
Eu não acho que escapar da abertura <é necessário .... document.write('<script src="foo.js">\x3C/script>')parece ser suficiente em todos os navegadores do IE6. (Eu deixei de fora o atributo tipo porque não é exigido em HTML5, nem é imposta como exigido por qualquer browser.)
— Matt Browne

34

Aqui está outra variação que eu usei ao querer gerar uma tag de script embutida (para que seja executada imediatamente) sem precisar de nenhuma forma de escape:

<script>
    var script = document.createElement('script');
    script.src = '/path/to/script.js';
    document.write(script.outerHTML);
</script>

(Observação: ao contrário da maioria dos exemplos na rede, não estou definindo type="text/javascript"nem a tag anexa nem a gerada: não há navegador que não a tenha como padrão e, portanto, é redundante, mas também não prejudicará, se você discordar).


Bom ponto re: type. O padrão é definido como "text / javascript" a partir do HTML5, portanto, é um atributo inútil. w3.org/html/wg/drafts/html/master/…
— Lucas

8
Essa é melhor que a resposta aceita, porque essa variação pode ser realmente minificada. Este 'x3C / script>' se tornará '</script>' após a minificação.
— Zoltan Kochan

20

Eu acho que é para impedir que o analisador HTML do navegador interprete o <script> e, principalmente, o </script> como a tag de fechamento do script real, no entanto, não acho que usar document.write seja uma excelente idéia para avaliar o script blocos, por que não usar o DOM ...

var newScript = document.createElement("script");
...

4
É necessário evitar que o analisador de fechar o script do bloco prematuramente ...
— roenving

9

A solução que Bobince postou funciona perfeitamente para mim. Eu também queria oferecer um método alternativo para futuros visitantes:

if (typeof(jQuery) == 'undefined') {
    (function() {
        var sct = document.createElement('script');
        sct.src = ('https:' == document.location.protocol ? 'https' : 'http') +
          '://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js';
        sct.type = 'text/javascript';
        sct.async = 'true';
        var domel = document.getElementsByTagName('script')[0];
        domel.parentNode.insertBefore(sct, domel);
    })();
}

Neste exemplo, incluí uma carga condicional para o jQuery demonstrar caso de uso. Espero que seja útil para alguém!


3
não há necessidade de detectar protocolo - o URI sem protocolo funciona muito bem ('//foo.com/bar.js' etc)
— dmp

3
não há necessidade de definir assíncrono também. está ativado para todas as tags de script criadas dinamicamente.
— Noishe

Me salvou! Ao usar document.write (<script>), meu site estava mostrando uma tela em branco. Isso funcionou.
— Tomas Gonzalez

@dmp exceto quando correndo de sistema de arquivos em que o protocolo será file: //
— mplungjan

9

A parte </script>interna da string Javascript é interpretada pelo analisador HTML como uma tag de fechamento, causando comportamento inesperado ( veja o exemplo no JSFiddle ).

Para evitar isso, você pode colocar seu javascript entre os comentários (esse estilo de codificação era uma prática comum, quando o Javascript era pouco suportado pelos navegadores). Isso funcionaria ( veja o exemplo no JSFiddle ):

<script type="text/javascript">
    <!--
    if (jQuery === undefined) {
        document.write('<script type="text/javascript" src="http://z-ecx.images-amazon.com/images/G/01/javascripts/lib/jquery/jquery-1.2.6.pack._V265113567_.js"></script>');
    }
    // -->
</script>

... mas, para ser sincero, usar document.writenão é algo que eu consideraria uma prática recomendada. Por que não manipular o DOM diretamente?

<script type="text/javascript">
    <!--
    if (jQuery === undefined) {
        var script = document.createElement('script');
        script.setAttribute('type', 'text/javascript');
        script.setAttribute('src', 'http://z-ecx.images-amazon.com/images/G/01/javascripts/lib/jquery/jquery-1.2.6.pack._V265113567_.js');
        document.body.appendChild(script);
    }
    // -->
</script>

1
Se você quiser usar JS puro, você vai querer ainda usam document.write;)
— Nirav Zaveri

Desculpe, eu pretendia escrever - isso requer a biblioteca jQuery, certo? Quando usei, document.body.append, gerou um erro que document.body.append não é uma função.
— Nirav Zaveri

1
Desculpe, meu erro: escrevi em appendvez de appendChild. Corrigida a resposta. Obrigado por perceber!
— Mathieu Rodic

1
Isso parece muito mais geral do que dividir a string manualmente. Por exemplo, a divisão não é possível se a string for inserida por um mecanismo de modelo.
— precisa saber é o seguinte
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.