当前位置: 首页 > news >正文

闲扯 『 document.write 』

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

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

  • HTML DOM write() 方法
  • HTML DOM write() Method
  • Document.write()
  • Using document.write

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):

675542-20160320195132037-1567160280.png

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

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

  • 知乎 中张立理的回答
  • document.write 方式引入外部 JS 文件导致脚本程序执行顺序不同以及 DOM 树更新延迟问题
  • Javascript 装载和执行

最后总结下吧,如果用 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" 都在。可以想象,当页面初次载入,浏览器还没解析完时,就算手动关闭文档流,也是关不掉的。

相关文章:

  • 【vSphere故障案例】案例九:ESXi主机HA未配置错误
  • 在Mac上关于tomcat服务器的安装、配置、启动、部署web详细流程
  • git分享(二)git checkout
  • Android源码大放送之material design类型
  • 国内收益最高的聚合平台--KeyMob移动广告聚合平台
  • SQL 存储过程返回值
  • 利用excel办公软件快速拼凑sql语句
  • 如何利用又拍云玩转live photo,没有6S也能玩
  • PHP异常处理
  • Java之IO流总结
  • Java NIO系列教程(三) Buffer
  • 解决HP打印机错误:Couldn't open fifo
  • 第二章 策略模式
  • 又拍云,音视频CDN加速利器
  • HTTP真的很简单(转)
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • ComponentOne 2017 V2版本正式发布
  • Java新版本的开发已正式进入轨道,版本号18.3
  • Webpack4 学习笔记 - 01:webpack的安装和简单配置
  • 分享几个不错的工具
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 配置 PM2 实现代码自动发布
  • 前嗅ForeSpider采集配置界面介绍
  • 算法系列——算法入门之递归分而治之思想的实现
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 一天一个设计模式之JS实现——适配器模式
  • 栈实现走出迷宫(C++)
  • kubernetes资源对象--ingress
  • Redis4.x新特性 -- 萌萌的MEMORY DOCTOR
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • 机器人开始自主学习,是人类福祉,还是定时炸弹? ...
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • ​力扣解法汇总946-验证栈序列
  • # 透过事物看本质的能力怎么培养?
  • (3)(3.5) 遥测无线电区域条例
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (利用IDEA+Maven)定制属于自己的jar包
  • (七)Knockout 创建自定义绑定
  • (四)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (译)计算距离、方位和更多经纬度之间的点
  • (转)菜鸟学数据库(三)——存储过程
  • (转)我也是一只IT小小鸟
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • ./configure、make、make install 命令
  • .[hudsonL@cock.li].mkp勒索加密数据库完美恢复---惜分飞
  • .bat文件调用java类的main方法
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .NET Core 和 .NET Framework 中的 MEF2
  • .NET MVC第五章、模型绑定获取表单数据
  • .NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例
  • .NET框架
  • .Net语言中的StringBuilder:入门到精通
  • /usr/local/nginx/logs/nginx.pid failed (2: No such file or directory)