Mengapa membagi tag <script> saat menulisnya dengan document.write ()?

268

Mengapa beberapa situs (atau pengiklan yang memberikan kode javascript klien) menggunakan teknik pemisahan <script>dan / atau </script>tag dalam document.write()panggilan?

Saya perhatikan bahwa Amazon juga melakukan ini, misalnya:

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

Jawaban:

373

</script>harus dipecah karena jika tidak maka akan mengakhiri <script></script>blok penutup terlalu dini. Benar-benar harus dibagi antara <dan /, karena blok skrip seharusnya (menurut SGML) diakhiri oleh setiap urutan tag-end terbuka (ETAGO) (yaitu </) :

Meskipun elemen STYLE dan SCRIPT menggunakan CDATA untuk model datanya, untuk elemen-elemen ini, CDATA harus ditangani secara berbeda oleh agen pengguna. Markup dan entitas harus diperlakukan sebagai teks mentah dan diteruskan ke aplikasi apa adanya. Kemunculan pertama dari urutan karakter " </" (pembatas tag terbuka-tag) diperlakukan sebagai pengakhiran akhir konten elemen. Dalam dokumen yang valid, ini akan menjadi tag akhir untuk elemen.

Namun dalam praktiknya browser hanya mengakhiri penguraian blok skrip CDATA pada </script>tag- sebenarnya .

Di XHTML tidak ada penanganan khusus untuk blok skrip, sehingga setiap <(atau &) karakter di dalamnya harus &escaped;seperti di elemen lain. Namun, browser yang mem-parsing XHTML sebagai HTML lama akan bingung. Ada beberapa solusi yang melibatkan blok CDATA, tetapi paling mudah untuk menghindari penggunaan karakter ini tanpa hambatan. Cara yang lebih baik untuk menulis elemen skrip dari skrip yang bekerja pada kedua jenis parser adalah:

<script type="text/javascript">
    document.write('\x3Cscript type="text/javascript" src="foo.js">\x3C/script>');
</script>
bobince
sumber
30
\/adalah urutan pelarian yang valid untuk /, jadi mengapa tidak hanya menggunakan itu alih-alih string literal itu lolos <? Misalnya document.write('<script src=foo.js><\/script>');. Juga, </script>bukan satu-satunya urutan karakter yang dapat menutup <script>elemen. Beberapa info lebih lanjut di sini: mathiasbynens.be/notes/etago
Mathias Bynens
11
@Mathias: <\/script>tidak masalah dalam hal ini, tetapi hanya akan berfungsi dalam HTML; di XHTML tanpa pembungkus bagian CDATA tambahan, itu masih merupakan kesalahan yang terjadi dengan baik. Anda juga dapat menggunakan \x3Catribut inline event handler di mana <juga tidak valid baik dalam HTML maupun XHTML, sehingga memiliki penerapan yang lebih luas: jika saya memilih satu, cara otomatis otomatis untuk menghindari karakter sensitif dalam literal string JS untuk semua konteks, itu yang saya pilih.
bobince
3
Dalam HTML, <dapat digunakan dalam atribut event handler inline. html5.validator.nu/... Dan Anda benar tentang kompatibilitas XHTML dari \x3Csebuah sich, tetapi karena XHTML tidak mendukung document.write(atau innerHTML), saya tidak melihat bagaimana itu relevan.
Mathias Bynens
2
@ MathiasBynens— document.writetidak relevan, itu hanya contohnya. OP bisa menggunakan innerHTML, ini tentang menyembunyikan menyembunyikan </urutan karakter dari parser markup, di mana pun itu terjadi. Hanya saja sebagian besar parser membiarkannya di dalam elemen skrip padahal sebenarnya tidak seharusnya (tetapi parser HTML sangat toleran). Anda benar bahwa <\/cukup dalam semua kasus untuk HTML.
RobG
3
Saya pikir melarikan diri dari pembukaan <diperlukan ... document.write('<script src="foo.js">\x3C/script>')tampaknya sudah cukup di semua browser kembali ke IE6. (Saya meninggalkan atribut type karena itu tidak diperlukan dalam HTML5, juga tidak ditegakkan seperti yang disyaratkan oleh browser apa pun.)
Matt Browne
34

Berikut variasi lain yang telah saya gunakan ketika ingin membuat inline tag skrip (jadi langsung dieksekusi) tanpa perlu bentuk apa pun:

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

(Catatan: bertentangan dengan sebagian besar contoh di internet, saya tidak mengatur type="text/javascript"tag terlampir, maupun yang dibuat: tidak ada browser yang tidak memiliki itu sebagai default, dan karena itu berlebihan, tetapi tidak ada salahnya juga, jika Anda tidak setuju).

Stoffe
sumber
Poin bagus ulang: ketik. Defaultnya didefinisikan sebagai "teks / javascript" pada HTML5, jadi itu atribut yang tidak berguna. w3.org/html/wg/drafts/html/master/…
Luke
8
Jawaban ini lebih baik daripada jawaban yang diterima karena variasi ini sebenarnya dapat diperkecil. 'X3C / script>' ini akan menjadi '</script>' setelah minification.
Zoltan Kochan
20

Saya pikir ini untuk mencegah parser HTML peramban dari menafsirkan <script>, dan terutama </script> sebagai tag penutup skrip aktual, namun saya tidak berpikir bahwa menggunakan document.write adalah ide bagus untuk mengevaluasi skrip blok, mengapa tidak menggunakan DOM ...

var newScript = document.createElement("script");
...
CMS
sumber
4
Hal ini diperlukan untuk mencegah pengurai menutup blok-script sebelum waktunya ...
roenving
9

Solusi yang diposting Bobince bekerja dengan sempurna untuk saya. Saya ingin menawarkan metode alternatif juga untuk pengunjung masa depan:

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

Dalam contoh ini, saya telah menyertakan pemuatan bersyarat untuk jQuery untuk menunjukkan use case. Semoga bermanfaat bagi seseorang!

Jongosi
sumber
3
tidak perlu mendeteksi protokol - protokol-kurang URI berfungsi dengan baik ('//foo.com/bar.js' dll)
dmp
3
tidak perlu mengatur async juga. diaktifkan untuk semua tag skrip yang dibuat secara dinamis.
Noishe
Menyelamatkan saya! Saat menggunakan document.write (<script>) situs saya menunjukkan layar kosong. Ini berhasil.
Tomas Gonzalez
@dmp kecuali saat menjalankan dari sistem file di mana protokol akan menjadi file: //
mplungjan
9

Bagian </script>dalam string Javascript diinterpretasi oleh parser HTML sebagai tag penutup, menyebabkan perilaku yang tidak terduga ( lihat contoh di JSFiddle ).

Untuk menghindari ini, Anda dapat menempatkan javascript di antara komentar (gaya pengkodean ini adalah praktik umum, ketika Javascript kurang didukung di antara browser). Ini akan berhasil ( lihat contoh di 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>

... tapi jujur ​​saja, menggunakan document.writebukanlah sesuatu yang saya anggap sebagai praktik terbaik. Mengapa tidak memanipulasi DOM secara langsung?

<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>
Mathieu Rodic
sumber
1
Jika Anda ingin menggunakan JS murni, Anda masih ingin menggunakan document.write;)
Nirav Zaveri
Maaf, saya bermaksud menulis - ini membutuhkan jQuery Library, kan? Ketika saya menggunakan, document.body.append, itu melemparkan kesalahan yang document.body.append bukan fungsi.
Nirav Zaveri
1
Maaf, kesalahan saya: saya menulis appendbukan appendChild. Memperbaiki jawabannya. Terima kasih telah memperhatikan!
Mathieu Rodic
1
Ini terlihat jauh lebih umum daripada memisahkan string secara manual. Misalnya pemisahan tidak layak jika string dimasukkan oleh mesin template.
w1th0utnam3