Perché dividere il tag <script> quando lo si scrive con document.write ()?


268

Perché alcuni siti (o inserzionisti che forniscono ai clienti codice javascript) utilizzano una tecnica per suddividere il <script>e / o il </script>tag all'interno delle document.write()chiamate?

Ho notato che anche Amazon fa questo, ad esempio:

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

Risposte:


373

</script>deve essere rotto perché altrimenti finirebbe il <script></script>blocco di chiusura troppo presto. In realtà dovrebbe essere diviso tra il <e il /, perché si suppone che un blocco di script (secondo SGML) sia terminato da qualsiasi sequenza aperta (ETAGO) end-tag (cioè </) :

Sebbene gli elementi STYLE e SCRIPT utilizzino CDATA per il loro modello di dati, per questi elementi, CDATA deve essere gestito in modo diverso dagli agenti utente. Markup ed entità devono essere trattati come testo non elaborato e passati all'applicazione così come sono. La prima occorrenza della sequenza di caratteri " </" (delimitatore aperto end-tag) viene considerata come la fine del contenuto dell'elemento. In documenti validi, questo sarebbe il tag di fine per l'elemento.

Tuttavia, in pratica, i browser terminano solo l'analisi di un blocco di script CDATA su un </script>tag di chiusura effettivo .

In XHTML non esiste una gestione così speciale per i blocchi di script, quindi qualsiasi <(o &) carattere al loro interno deve essere &escaped;come in qualsiasi altro elemento. Tuttavia, i browser che analizzano XHTML come HTML della vecchia scuola verranno confusi. Esistono soluzioni alternative che coinvolgono blocchi CDATA, ma è più semplice evitare di usare questi caratteri senza caratteri di escape. Un modo migliore di scrivere un elemento di script dallo script che funziona su entrambi i tipi di parser sarebbe:

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

30
\/è una sequenza di escape valida per /, quindi perché non usare quella invece di quelle stringhe letterali di escape per <? Es document.write('<script src=foo.js><\/script>');. Inoltre, </script>non è l'unica sequenza di caratteri che può chiudere un <script>elemento. Altre informazioni qui: mathiasbynens.be/notes/etago
Mathias Bynens,

11
@Mathias: <\/script>va bene in questo caso, ma funzionerebbe solo in HTML; in XHTML senza il wrapping di una sezione CDATA aggiuntiva, è ancora un errore ben strutturato. Inoltre puoi usare gli \x3Cattributi del gestore di eventi inline dove non <sarebbero validi sia in HTML che in XHTML, quindi ha un'applicabilità più ampia: se dovessi scegliere un modo facilmente automatizzato per sfuggire ai caratteri sensibili nei letterali delle stringhe JS per tutti i contesti, questo è quello per cui vorrei andare.
bobince,

3
In HTML, <può essere utilizzato negli attributi del gestore eventi inline. html5.validator.nu/… E hai ragione sulla compatibilità XHTML di \x3Cun sich, ma poiché XHTML non supporta document.write(o innerHTML) comunque non vedo quanto sia rilevante.
Mathias Bynens,

2
@ MathiasBynens— document.writeè irrilevante, è solo l'esempio. L'OP avrebbe potuto usare innerHTML, si tratta di nascondere la </sequenza di caratteri dal parser di markup, ovunque si verifichi. È solo che la maggior parte dei parser lo tollerano all'interno di un elemento di script quando rigorosamente non dovrebbero (ma i parser HTML sono molto tolleranti). Hai ragione, tuttavia, è <\/sufficiente in tutti i casi per HTML.
RobG,

3
Non penso che sfuggire all'apertura <sia necessario .... document.write('<script src="foo.js">\x3C/script>')sembra essere sufficiente in tutti i browser a IE6. (Ho lasciato fuori l'attributo type perché non è richiesto in HTML5, né è imposto come richiesto da nessun browser.)
Matt Browne

34

Ecco un'altra variante che ho usato quando volevo generare un tag di script in linea (quindi si esegue immediatamente) senza bisogno di alcuna forma di escape:

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

(Nota: contrariamente alla maggior parte degli esempi in rete, non sto impostando type="text/javascript"né sul tag allegato, né su quello generato: non esiste un browser che non lo abbia come predefinito, quindi è ridondante, ma non farà male, se non sei d'accordo).


Buono a proposito re: digitare. L'impostazione predefinita è "text / javascript" a partire da HTML5, quindi è un attributo inutile. w3.org/html/wg/drafts/html/master/…
Luca

8
Questa è migliore della risposta accettata perché questa variazione può essere effettivamente minimizzata. Questo 'x3C / script>' diventerà '</script>' dopo la minificazione.
Zoltan Kochan,

20

Penso che sia per impedire al parser HTML del browser di interpretare <script> e principalmente </script> come tag di chiusura dello script reale, tuttavia non penso che l'utilizzo di document.write sia un'ottima idea per valutare lo script blocchi, perché non usare il DOM ...

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

4
È necessario impedire al parser di chiudere prematuramente il blocco di script ...
roenving

9

La soluzione pubblicata da Bobince funziona perfettamente per me. Volevo offrire un metodo alternativo anche per i futuri visitatori:

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

In questo esempio, ho incluso un carico condizionale per jQuery per dimostrare il caso d'uso. Spero che sia utile per qualcuno!


3
nessuna necessità di rilevare il protocollo - l'URI senza protocollo funziona bene ('//foo.com/bar.js' ecc.)
dmp

3
non è nemmeno necessario impostare l'asincronizzazione. è impostato per tutti i tag di script creati dinamicamente.
Noishe,

Mi ha salvato! Quando usavo document.write (<script>) il mio sito mostrava una schermata vuota. Questo ha funzionato.
Tomas Gonzalez,

@dmp tranne quando in esecuzione da un file system in cui il file sarà il protocollo: //
mplungjan

9

L' </script>interno della stringa di Javascript litteral è interpretato dal parser HTML come tag di chiusura, causando comportamenti imprevisti ( vedi esempio su JSFiddle ).

Per evitare ciò, puoi inserire il tuo javascript tra i commenti (questo stile di programmazione era una pratica comune, quando JavaScript era scarsamente supportato tra i browser). Funzionerebbe ( vedi esempio in 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>

... ma ad essere sincero, l'utilizzo document.writenon è qualcosa che considererei la migliore pratica. Perché non manipolare direttamente il DOM?

<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 vuoi usare JS puro, vorrai comunque usare document.write;)
Nirav Zaveri

Scusa, volevo scrivere - questo richiede la libreria jQuery, giusto? Quando ho usato document.body.append, ha generato un errore che document.body.append non è una funzione.
Nirav Zaveri,

1
Scusa, mio ​​errore: ho scritto appendinvece di appendChild. Corretta la risposta Grazie per averlo notato!
Mathieu Rodic,

1
Sembra molto più generale della divisione manuale della stringa. Ad esempio, la divisione non è possibile se la stringa viene inserita da un motore modello.
w1th0utnam3,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.