document中的write⽤法
⼀、document.write()运⾏原理
⾸先我们先了解⼀下这条语句运⾏的原理:
document.write()是Javascript中对document.open()所开启的⽂档流操作的API⽅法。
它能够直接砸⽂档流中写⼊字符串,⼀旦⽂档流已经关闭,那么document.write()就会重新运⽤document.open()打开新的⽂档流并写⼊,此时原来的⽂档流会被清空,已经渲染好的页⾯就会被覆盖,浏览器将重新构建DOM并渲染新的页⾯。
--(这也就是所谓的页⾯重写问题!!只发⽣在页⾯已经加载完,⽤它就清空以前的document)
⼆、写⼊⽂本(页⾯加载中可以写⼊,⽽不会出现重写页⾯问题)
下⾯来看看如何在利⽤ document.write()来写⼊⽂本。先考虑如下代码:
<body>
<h1>Head</h1>
<script>
document.write('<p>hello document</p>');
</script>
<h2>Tail</h2>
</body>
这是在h1和h2之间内嵌⼀个脚本,使⽤document.write()来写⼊⼀个p标签。
刷新页⾯,可以看到最终的结果是
Head
hello document
Tail
即⽂本在脚本执⾏的位置被插⼊。这是因为,浏览器就解析HTML构建DOM的时候,如果遇到script就会暂停下来,解析script中的代码并执⾏,然后再继续解析剩余HTML。(阻塞进⾏的)
此时再去浏览器中检测DOM的结构,会发现script与h2之间多了⼀个p,浏览器在解析完h1之后,碰到script并执⾏之,此
时document.write将⼀段HTML代码写⼊到⽂档流中,script执⾏完毕后,浏览器会解析⽂档流中的字符串,对新添加的p标签进⾏解析。
如果将渲染好的页⾯保存下来,不同的浏览器会有不同的结果。如Chrome和Firefox的做保存下来的页⾯⽂件中,script后⾯会增加p标签,⽽IE中则是维持原状。(这⾥指的是原有的HTML结构,不同浏览器将页⾯保存会作不同的处理,有些会增加⼀些不影响原有结构的标签或注释。这意味着,如果浏览器重新加载Chrome或Firefox中保存下来的页⾯⽂件,就会多出⼀个p标签。⽽IE浏览器则不会(可以⾃⼰动⼿试试!毕竟实践出真知)
三、写⼊脚本(注意加转义符号)
既然document.write可以写⼊p并被浏览器解析,那么⾃然地也可以写⼊script标签。
<script>
document.write('<script>alert("oops")</script>');
</script>
我们将代码作出上⾯的改动,意图在利⽤document.write在页⾯中插⼊⼀段脚本。这段代码的本意是弹出⼀个窗⼝,阻塞浏览器对HTML的解析。
浏览器下刷新页⾯,发现并不管⽤,取⽽代之的是显⽰出⼀个没有意料到的页⾯。
Head
');
Tail
去检查DOM树,就会发现,这段脚本被拦腰截断了!浏览器将它解析成以下代码:
<script>
document.write('<script>alert("oops")
</script>
');
插⼊⽂本中的</script>被当成了第⼀个script的闭合标签,因此这个段代码成了⾮法代码,因为document.write的调⽤书写不正确,缺少右边的括号)。此时,你可以在console中看到相关的错误信息。(控制台)
为了解决这个问题,我们可以对插⼊⽂本中闭合的的标签进⾏轻微修改,对最后⼀个</script>进⾏转义,变成<script>alert("oops")<\ /script>或者<script>alert("oops")</script\>都可;
此时再刷新⼀下页⾯,就可以看到预想中的结果。即页⾯中仅显⽰h1,弹窗阻塞了浏览器对HTML的解析,关闭弹窗后,浏览器继续对HTML的解析并完成对页⾯的渲染。
再去看看DOM的结构,会发现在原有的script元素后⾯⼜多了⼀个新的<script>元素,其中所执⾏的代码就是我们的alert("opps")。
四、异步引⽤外部javascript⽂件
此时,必须注意:必须先运⾏document.open()清空⽂档,然后才能运⾏document.write()。
如果不先运⾏document.open(),直接运⾏document.write(),则会⽆效。chrome浏览器会有如下提⽰:
参数写在body内容的开头。如下⽂:
// test.js内容
document.open();
document.write('<p>test</p>');
document.close();
//html⽂件内容
<body>
<script src='test.js' ansyc></script>
</body>
运⾏后的⽂件是:
<body>
<p>test</p>
</body>
也就是会清空先前的页⾯,重新渲染,页⾯显⽰test字符串。
五、document.write使⽤的时机很重要
这样看来,利⽤document.write来在HTML中插⼊标签⾮常⽅便,就如同让浏览器在解析HTML的时候动态添加标签,⽽且只需要⼀⾏代码即可,不需要使⽤ateElement再将其插⼊到DOM中。
但为什么⼤家都不建议使⽤document.write呢?这跟document.write的实现机制有关。在讨论之前,先看看下⾯的代码:
<body>
<h1>Head</h1>
<script>
setTimeout(function(){
document.write('<p>5 secs later</p>');
}, 5000);
</script>
<h2>Tail</h2>
</body>
对之前的代码作简单修改,这段代码同样是想插⼊⼀个p元素,但它是在5秒以后才执⾏。
刷新页⾯后,我们先看到了这样的显⽰效果:
Head
Tail
但是5秒以后,却变成了这样:(页⾯被重写了!!)
5 secs later
原来的h1、h2甚⾄是script,DOCTYPE还有head(当然,之前并没有往head添加任何标签,但如果添加了,这些标签也会有同样的下场),它们全部都不见了,取⽽代之的是⼀个基本的html结构,它是这样的:
<html>
<head></head>
<body>
<p>5 secs later</p>
</body>
</html>
这是⼀个全新的页⾯,document.write将之前的页⾯全部清除了,重新打开⼀页⾯并在这个页⾯上写
⼊了新的标签。为什么会这样呢?(5s后页⾯加载完了,所以重写页⾯了!)
这是因为,5秒以后,浏览器早已完成了HTML的解析,并将⽂档流给关闭了。5秒后,timeout事件触发,document.write在执⾏的时候发现⽂档流已经关闭了,就会重新调⽤document.open打开⼀个新的⽂档流,⽽document.open的调⽤则会清除已有的⽂档。所以,最终看到的显⽰结果就是向上⾯那样,之前存在的页⾯都被清除掉了。
如果我们把document.write调⽤放到DOMCOntentLoaded或load的事件处理中,也会得到同样的结果。
这样看到,除⾮是在浏览器关闭⽂档之前调⽤document.write,否则当前页⾯都会被清除。
(说通俗点,如果能保证能在onload前执⾏,那么可以实现载⼊,⽽不是重写)
六、应⽤场景
(1)加载需要配合JS脚本使⽤的外部CSS⽂件
利⽤下⾯的语句加载外部样式⽂件:
<script>
document.write('<link rel="stylesheet" href="style_neads_js.css">');
</script>
将所有需要⽤到JS的样式都放到这个外部样式表中,如果浏览器不禁⽤JS,那么该样式表就会被顺利加载,否则页⾯就不会使⽤该样式。()
(2)在新的窗⼝中写⼊新的页⾯数据时(新建⼀个页⾯就不会重写之前的)
既然在⼀个已加载完成的页⾯中调⽤document.write会重写整个页⾯,那么在⼀个新的窗⼝的空⽩页⾯中调⽤这个⽅法,就不存在这样的的问题了。
另外,在调⽤document.write,最好不要把document.open和document.close漏掉,尽管多数时候浏览器会帮忙完成这些操作。即,⼀个标准的document.write应该是这样的:
document.open();
document.write('anthing')
document.close();
七、弊端
从某个⾓度说,document.write的实际功能确实很强,能够直接修改⽂档流,但它有很多弊端:
html document是什么在⾮loading阶段调⽤document.write会清除已加载的页⾯;
document.write不能够在XHTML中使⽤;
嵌⼊script中的document.write不能给任意节点添加⼦节点,因为它是随着DOM的构建执⾏的;
利⽤document.write写⼊HTML字符串流并不是⼀个好⽅法,它有违DOM操作的概念;
利⽤document.write添加script加载外部脚本时,浏览器的HTML解析会被script的加载所阻塞;
总结
综合上⾯所描述的关于document.write的种种特点,个⼈感觉还是不到迫不得已的时候,不要去使⽤document.write,使⽤不当document.write不仅会影响页⾯的性能,还容易造成各种bug。
要对DOM进⾏操作时,还是应当使⽤安全且对DOM的友好的API⽅法,以避免不必要的问题出现。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论