执行文档写入:除非显式地打开,否则无法从异步加载的外部脚本向文档中写入内容。

84

我正在尝试在页面加载后执行某个脚本,类似于这样:

function downloadJSAtOnload(){
            var element = document.createElement("script");
            element.src = "scriptSrc";
            document.body.appendChild(element);
        }

         if (window.addEventListener)
                  window.addEventListener("load", downloadJSAtOnload, false);
            else if (window.attachEvent)
                  window.attachEvent("onload", downloadJSAtOnload);
            else window.onload = downloadJSAtOnload;

虽然这个脚本似乎能够执行并下载“scriptSrc”,并将其附加到 body 标签结束之前,但在控制台(chrome)中会产生以下消息(不是错误):

未能在文档上执行“write”:除非显式打开文档,否则无法从异步加载的外部脚本中写入文档。

这是什么意思?我需要做一些不同的事情吗?尽管我得到了预期的行为?


2
这意味着你正在加载的脚本执行了 document.write,而当你通过 <script> 标签将其添加到页面时,它是不被支持的。你应该仔细查看脚本正在做什么,并更改脚本以不使用 document.write - univerio
1
明白了,但我无法更改那个脚本。这就是官僚主义。我能让脚本同步加载吗?不行,因为它发生在页面加载之后。 - Parijat Kalia
2
最近在 Github 上发现了一个非常棒的库,可以很好地解决这个“限制”问题:https://github.com/krux/postscribe。 - Randy L
@univerio,如果脚本同步运行,我很好。我也设置了async false属性,但没有成功。 - RockStar
@RockStar 不要使用 document.write。如果您无法更改使用它的脚本,请像上面建议的那样使用 postscribe。 - univerio
显示剩余6条评论
4个回答

102

异步加载的脚本很可能会在文档完全解析和关闭之后运行。 因此,您无法从这样的脚本中使用 document.write() (技术上可以,但它不会做您想要的事情)。

您需要将该脚本中任何 document.write() 语句替换为通过创建DOM元素并使用.appendChild().insertBefore()将它们插入到特定父级,或设置.innerHTML或类似于此类的直接DOM操作机制进行明确的DOM操作。

例如,您可以使用以下类型的代码代替内联脚本中的代码:

<div id="container">
<script>
document.write('<span style="color:red;">Hello</span>');
</script>
</div>
你可以使用这个代码来替换动态加载脚本中的内联脚本:
var container = document.getElementById("container");
var content = document.createElement("span");
content.style.color = "red";
content.innerHTML = "Hello";
container.appendChild(content);

或者,如果容器中没有其他需要附加的内容,您可以简单地执行以下操作:

var container = document.getElementById("container");
container.innerHTML = '<span style="color:red;">Hello</span>';

但是当我说“document.body.appendChild(element)”时,我不是在做类似的事情吗? - Parijat Kalia
@jfriend,关于“但它不会做你想要的事情”,如果它确实能做到呢?如果我想启用它,该怎么办?(在旧版浏览器上可以使用。) - Pacerier
2
@Pacerier - 在文档加载完成后使用document.write()会导致当前文档被清除,然后写入一个新的空白文档。如果您想要这种行为,那么只需在插入内容之前清除当前DOM即可。如果您有比这更详细的问题,请提出一个新问题,展示您拥有的代码并描述所需的结果。 - jfriend00

23

来晚了一点,但Krux为此创建了一个脚本,名为Postscribe。我们能够使用它来解决这个问题。


1
最佳方法是如何将此库与我们的JavaScript库一起传递?情况是,我们向客户提供JS标签,该标签在他们的网站上运行。 - RockStar
我不完全确定你在问什么,@RockStar,但是任何正常的包含JS的方法都应该可以工作。你可以打开https://cdnjs.com/libraries/postscribe并将代码复制/粘贴到现有的JS文件中,或者只需添加一个指向cdnjs托管版本文件的<script>标签即可。可能需要在其他脚本之前加载它,所以如果你无法让它工作,这可能是可能的原因。 - Randy L
1
我们向网站所有者提供了JavaScript CDN链接,以便使用此标签放置广告。我们无法要求每个所有者都放置这个postscribe.js,因此我们需要将其与我们的JS一起提供。 - RockStar
除非您明确使用“postscribe”函数,否则“postscribe”似乎根本不起作用 - 非常令人失望。 - Ray Nicholus
@RockStar,你解决了你的问题吗?我也遇到了同样的问题。 - Thilak Raj

3

如果有人需要,我来分享一下同样的问题。我通过jQuery将页脚引入到网页中,里面包含了一些用于广告和重新定位的Google脚本。我不得不将这些脚本从页脚移到页面中直接放置,这样就可以消除通知。


1
我遇到了同样的问题,但脚本位于由标签管理器创建的捆绑包中。问题是由于“async”属性引起的,唯一的解决方案是删除该属性,因为我不允许从我们的标签管理器移动脚本。 - Giorgio Tempesta

2

你也可以调用

document.open() before document.write()

调用

document.close() 

当你完成时。

这可能不是真实网页的最佳实践,但可以用于测试等。


非常适合多次写入的是document.writeln,并在末尾加上document.body.style.whiteSpace = "pre"; - cachius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接