document.write()的一些坑
这段时间在做一个广告系统,需要用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 };