初春的晚上,闲来无事,聊聊 document.write 方法。

document.write 使用方式非常简单,把 "字符串化"(不好意思,这可能是我自己创造的名词)的 html 代码当做参数传入就 ok 了,我并不打算讲它的基本用法,可以参考以下链接:

document.write 经常会被用来加载脚本,比如这样:

var url = 'http://ads.com/buyme?rand='+Math.random()
document.write('<script src="'+url+'"></scr'+'ipt>')

传统方式:

var script = document.createElement('script')
script.src = 'http://ads.com/buyme?rand='+Math.random()
// now append the script into HEAD, it will fetched and executed
document.documentElement.firstChild.appendChild(script)

对比 dom 插入的传统方法,的确能少几行代码。这样做还有个好处,它比 dom 插入的方式快,因为它是在一个输出流中,所以不用修改 dom 结构(It is very fast, because the browser doesn’t have to modify an existing DOM structure)。但是,如果这段脚本没有执行完,后续渲染都将挂起!

document.write 加载脚本也不是没有合适的场景,比如说后续的渲染都要依赖这段脚本,那么这样写就完全没有问题。比如这段代码:

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.6.3.min.js"><\/script>')</script>

或者:

<script>window.JSON || document.write('<script src="json2.js"><\/script>')</script>

非常的优雅。

还有个应用场景,加载第三方广告,百度联盟的广告就是用该方法输出的。我们假设百度联盟广告如下(另存为 cm.js):

document.write("<img src='ad.jpg /'>");

那么我们在页面任意部分同步加载这段代码,就能显现百度广告,事实上,体验是非常差的,因为是同步渲染,如果这段代码没有执行完,后续是不会执行下去的(UI 挂起)。尝试着将内含 document.write 的脚本文件异步执行,写个简单的 demo。

index.htm 文件:

<body>
Hello
<script>
  var s = document.createElement("script");
  s.src = "data.js";
  document.body.appendChild(s);
</script>
</body>

data.js 文件:

document.write('World');

页面只显示了 Hello 字样,控制台打印 notice 如下(详见 stackoverflow):

按照 notice 的提示将 document.open() 加入 data.js 文件,这时页面就只有 World 了。我去,异步加载个 js,替换这个页面,这样的操作应该几乎没有吧!所以,看起来百度的广告只能同步加载了,如果延迟加载(用个 setTimeout 方法)用到 document.write 的文件,那么理论上会覆盖整个页面吧,这是我们不希望看到的,也是我们要谨慎使用该方法的原因( Why is document.write considered a “bad practice”?)。

用 document.write 加载脚本文件,甚至还涉及到浏览器的兼容性,不同的浏览器会用不同的顺序加载,这点不展开了,有兴趣的可以参考如下链接:

最后总结下吧,如果用 document.write 来渲染页面,可以适当适时的使用,如果是加载脚本,尽量别用了,毕竟 stevesouders 建议别用(Don’t docwrite scripts),主要还是为了不影响后续的加载。


附以前写的草稿:

document.write 是 document 下的一个方法,很多入门书籍中经常见到该方法,实际生产中却很少用到。

document.write() 接收一个字符串作为参数,将该字符串写入文档流中。一旦文档流已经关闭(document.close()),那么 document.write 就会重新利用 document.open() 打开新的文档流并写入,此时原来的文档流会被清空,已渲染好的页面就会被清除,浏览器将重新构建 DOM 并渲染新的页面。

向文档流中写入 HTML 字符串:

<div>
  <script>
    document.write("<script src='cm.js'><\/script>");
    document.write("<div class='add'></div>")
  </script>
</div>

因为 document.write 方法作用时,文档流还没关闭,所以并不用先 document.open()。渲染完后页面 dom 结构( chrome下 需考虑浏览器兼容性):

<div>
  <script>
    document.write("<script src='cm.js'><\/script>");
    document.write("<div class='add'></div>")
  </script>
  <script src="cm.js"></script>
  <div class="add"></div>
</div>

这里还需要 注意一点,当 document.write 的字符串参数包含 script 标签时,注意要转义,或者将 </script> 割开(split),比如 document.write("<script src='cm.js'></" + "script>");,这是因为一旦遇到 </script>,会自动与包裹该段代码的 <script> 进行配对。详见 这里

再看个例子:

<div>
  <p>hello world</p>
</div>
<script>
  setTimeout(function() {
    document.write('<a href="http://www.cnblogs.com/zichi/">zichi\'s blog</a>');
  }, 0);
</script>

因为当 setTimeout 的回调执行时,文档流已经关闭(页面已经解析完),所以首先自动调用 document.open() 函数打开文档流,然后将文档流清空,渲染新的东西,即字符串中的 a 标签。

既然不加 document.open() 也会自动开启文档流,那么 document.open() 以及 document.close() 是否没用武之地了呢?思考如下代码。

代码一:

<div>
  <p>hello world</p>
</div>
<script>
  setTimeout(function() {
    document.write('a');
    document.write('b');
  }, 0);
</script>

代码二:

<div>
  <p>hello world</p>
</div>
<script>
  setTimeout(function() {
    document.open();
    document.write('a');
    document.close();

    document.open();
    document.write('b');
    document.close();
  }, 0);
</script>

前者页面显示 "ab",而后者显示 "b"。可以想象前者两个 document.write 在一个文档流中输出,而后者手动关闭文档流,所以相当于重写了两次。

继续看:

<div>
  <p>hello world</p>
</div>
<script>
  document.open();
  document.write('a');
  document.close();

  document.open();
  document.write('b');
  document.close();
</script>

页面上 "hello world" 和 "ab" 都在。可以想象,当页面初次载入,浏览器还没解析完时,就算手动关闭文档流,也是关不掉的。

闲扯 『 document.write 』的更多相关文章

  1. 似魔鬼的 『 document.write 』

    在平时的工作中,楼主很少用 document.write 方法,一直觉得 document.write 是个危险的方法.楼主不用,并不代表别人不用,最近给维护的项目添了一点代码,更加深了我对 &quo ...

  2. 拾遗:『Linux Capability』

    『Linux Capability』 For the purpose of performing permission checks, traditional UNIX implementations ...

  3. 『创意欣赏』20款精致的 iOS7 APP 图标设计

    这篇文章给大家分享20款精致的 iOS7 移动应用程序图标,遵循图形设计的现代潮流,所有图标都非常了不起,给人惊喜.通过学习这些移动应用程序图标,设计人员可以提高他们的创作,使移动用户界面看起来更有趣 ...

  4. 『设计前沿』14款精致的国外 iOS7 图标设计示例

    每天都有大量的应用程序发布到 iOS App Store 上,在数量巨大的应用中想要引起用户的主要,首要的就是独特的图标设计.这篇文章收集了14款精致的国外 iOS7 图标设计示例,希望能带给你设计灵 ...

  5. Github 恶搞教程(一起『玩坏』自己的 Github 吧)

    最近在伯乐在线读到一篇趣文,<如何在 Github『正确』做贡献>,里面各种能人恶搞 Github 的『Public contributions』,下面截取几个小伙伴的战绩: 顺藤摸瓜,发 ...

  6. 『创意欣赏』30幅逼真的 3D 虚拟现实环境呈现

    又到周末了,给大家分享30幅漂亮的 3D 虚拟现实环境呈现,放松一下.这些创造性的场景都是通过 3D 图形设计软件,结合三维现实环境渲染制作出来的.一起欣赏:) 您可能感兴趣的相关文章 20幅温馨浪漫 ...

  7. [TYVJ1827]『Citric II』一道防AK好题

    时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 第二届『Citric杯』NOIP提高组模拟赛第一题 描述 Lemon认为在第一届『Citric』杯模拟赛中出的 ...

  8. 办理滑铁卢大学(本科)学历认证『微信171922772』UW学位证成绩单使馆认证University of Waterloo

    办理滑铁卢大学(本科)学历认证『微信171922772』UW学位证成绩单使馆认证University of Waterloo QQ/微信171922772办理毕业证成绩单.真实使馆及教育部学历认证★诚 ...

  9. 办理渥太华大学(本科)学历认证『微信171922772』Ottawa U学位证成绩单使馆认证University of Ottawa

    办理渥太华大学(本科)学历认证『微信171922772』Ottawa U学位证成绩单使馆认证University of Ottawa QQ/微信171922772办理毕业证成绩单.真实使馆及教育部学历 ...

随机推荐

  1. Bootstrap弹出框(modal)垂直居中

    最近在做一个eit项目,由于此项目里面一些框架要遵循nttdata的一些规则,故用到了Bootstrap这个东东,第一次碰到这个东东,有很大抵触,觉得不好,但用起来我觉得和别的弹出框真没什么两样.废话 ...

  2. Mongodb Manual阅读笔记:CH9 Sharding

    9.分片(Sharding) Mongodb Manual阅读笔记:CH2 Mongodb CRUD 操作Mongodb Manual阅读笔记:CH3 数据模型(Data Models)Mongodb ...

  3. Spring中@Autowired注解、@Resource注解的区别

    Spring不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource.@PostConstruct以及@PreDestroy. @Resour ...

  4. Oracle索引梳理系列(一)- Oracle访问数据的方法

    版权声明:本文发布于http://www.cnblogs.com/yumiko/,版权由Yumiko_sunny所有,欢迎转载.转载时,请在文章明显位置注明原文链接.若在未经作者同意的情况下,将本文内 ...

  5. SQL Server调优系列基础篇(常用运算符总结——三种物理连接方式剖析)

    前言 上一篇我们介绍了如何查看查询计划,本篇将介绍在我们查看的查询计划时的分析技巧,以及几种我们常用的运算符优化技巧,同样侧重基础知识的掌握. 通过本篇可以了解我们平常所写的T-SQL语句,在SQL ...

  6. oracle存储过程、函数、序列、包

    一. 存储过程 1. 语法 create or replace procedure procedureName(seqName varchar2) is /*声明变量*/ n ); cursor cu ...

  7. java 重载、重写、构造函数详解

    方法重写 1.重写只能出现在继承关系之中.当一个类继承它的父类方法时,都有机会重写该父类的方法.一个特例是父类的方法被标识为final.重写的主要优点是能够定义某个子类型特有的行为. class An ...

  8. log4j 日志信息的引入(通用版)——解决项目运行过程中的日志信息

    定义 log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件,甚至是套接口服务器.NT的事件记录器.UNIX Syslog守护进程 ...

  9. 在win10中创建开机自动登陆的网络驱动器

    前提环境: win10系统电脑一台. centos系统电脑一台,在该系统中安装samba,并配置共享. 要解决的问题: 在win10做了网络映射,将centos共享的文件夹设置成本地硬盘.在映射时,也 ...

  10. android camera setMeteringArea详解

    摘要: 本文为作者原创,未经允许不得转载:原文由作者发表在博客园:http://www.cnblogs.com/panxiaochun/p/5802814.html setMeteringArea() ...