這段時間在做一個廣告系統,需要用js自動生成廣告代碼,其中遇到一些問題,在這記錄下。
第三方廣告代碼一般都是用 document.write()向頁面寫入,這次使用的時候遇到不少坑,經常會出現使用document.write()向頁面寫入的時候當前頁面被清空。這正是document.write()的特殊之處,頁面載入后瀏覽器輸出流自動關閉,此后對當前頁面進行document.write()操作將打開—個新的輸出流,它將清除當前頁面內容(包括源文檔的任何變量或值)。
那么有哪些瀏覽器輸出流關閉的標識呢?我們首先想到的是 window.onload,在window.onload后頁面加載完成,瀏覽器輸出流必然關閉。經測試$(document).ready()中的操 作也是在瀏覽器輸出流關閉之后執行。所以用於生成代碼的document.write()不能寫在window.onload 和$(document).ready()中。除了這兩個之外有沒有其他坑呢?js生成廣告代碼中需要ajax獲取服務端的數據,ajax請求成功之后再 document.write()寫入仍然會發生覆蓋。這是因為jquery的ajax默認是異步請求,並不阻塞文檔流,當ajax請求成功之后在執行操 作,瀏覽器輸出流很可能就關閉了。這種情況可以把ajax默認請求改成同步,阻塞文檔流,防止document.write()覆蓋。
由於第三方廣告代碼使用document.write()輸出,所有的廣告資源都在頁面載入時加載,如果頁面上第三方廣告比較多,必然會阻塞頁面加載。有 沒有辦法在使用document.write()的情況下不阻塞頁面加載呢?其實是可以的,這里就需要我們改造原生的document.write()方 法,在廣告加載完畢再把原生方法改回來。這里就不具體展開來寫,雨夜帶刀博客《讓document.write的廣告無阻塞的加載》有詳細分析,這里貼一下帶刀改造document.write()的代碼。
1 /** 2 * 重寫document.write實現無阻塞加載script 3 * @param { Dom Object } textarea元素 4 */ 5 var loadScript = function( elem ){ 6 var url = elem.value.match( /src="([\s\S]*?)"/i )[1], 7 parent = elem.parentNode, 8 // 緩存原生的document.write 9 docWrite = document.write, 10 // 創建一個新script來加載 11 script = document.createElement( 'script' ), 12 head = document.head || 13 document.getElementsByTagName( 'head' )[0] || 14 document.documentElement; 15 16 // 重寫document.write 17 document.write = function( text ){ 18 parent.innerHTML = text; 19 }; 20 21 script.type = 'text/javascript'; 22 script.src = url; 23 24 script.onerror = 25 script.onload = 26 script.onreadystatechange = function( e ){ 27 e = e || window.event; 28 if( !script.readyState || 29 /loaded|complete/.test(script.readyState) || 30 e === 'error' 31 ){ 32 33 // 恢復原生的document.write 34 document.write = docWrite; 35 head.removeChild( script ); 36 37 // 卸載事件和斷開DOM的引用 38 // 盡量避免內存泄漏 39 head = 40 parent = 41 elem = 42 script = 43 script.onerror = 44 script.onload = 45 script.onreadystatechange = null; 46 47 } 48 } 49 50 // 加載script 51 head.insertBefore( script, head.firstChild ); 52 };