JavaScript - 控制 document.write 的插入点

17

我想创建一个页面,在DOM完全加载后运行一个包含document.write的第三方脚本。

我的页面不是XHTML。我的问题是document.write正在覆盖我的页面(一旦DOM被加载,它就会这样做)。

我尝试覆盖document.write函数(类似于http://ejohn.org/blog/xhtml-documentwrite-and-adsense/中所述的方法),但这并不能解决document.write包含部分标签的情况。

下面是一个将打破上述代码的示例:

document.write("<"+"div");
document.write(">"+"Done here<"+"/");
document.write("div>");

是否有一种方式可以通过JavaScript修改document.write的插入点?还有其他更好的方法吗?


第三方脚本已经下载,所以您可以编辑它吗?我认为这可能比hack document.write()更好的解决方案。 - peirix
我的示例已经通过了。我的意思是: document.write('<' + 'div'); document.write('>' + '文本内容' + '<'); document.write('\div>') - Yoav Weiss
我可以编辑它,但除了完全的JS解析之外,有没有一种方法来保证编辑后的代码能够正常运行? - Yoav Weiss
7个回答

11

如果你要处理第三方脚本,仅仅替换document.write以捕获输出并将其放置在正确位置是不够的,因为他们可能会更改脚本,那么你的网站就会出问题。

writeCapture.js 可以满足你的需求(完全透明:我是作者)。它基本上重新编写了脚本标签,使得每个标签都可以捕获其自己的document.write输出并将其放置在正确的位置。使用方法(使用jQuery)如下:

$(document.body).writeCapture().append('<script type="text/javascript" src="http://3rdparty.com/foo.js"></script>');

我假设你想将内容追加到页面的末尾。该插件可以使用所有jQuery选择器和操作方法,因此您可以根据需要将其注入到任何位置并进行操作。如果没有jQuery也可以使用该插件,如果这是一个问题的话。


当我使用这段代码时,Chrome对我发出警告并抛出一个错误:“Uncaught SyntaxError: Unexpected token ILLEGAL”。 - Eric K

4

可以覆盖document.write方法。因此,您可以缓冲发送到document.write的字符串,并在任何地方输出缓冲区。但是,如果不正确处理将脚本从同步更改为异步可能会导致错误。以下是一个示例:

简化的document.write替换

(function() {
    // WARNING: This is just a simplified example
    // to illustrate a problem.
    // Do NOT use this code!

    var buffer = [];
    document.write = function(str) {
        // Every time document.write is called push
        // the data into buffer. document.write can
        // be called from anywhere, so we also need
        // a mechanism for multiple positions if
        // that's needed.
        buffer.push(str);
    };

    function flushBuffer() {
        // Join everything in the buffer to one string and put
        // inside the element we want the output.
        var output = buffer.join('');
        document.getElementById("ad-position-1").innerHTML = output;
    }

    // Inject the thid-party script dynamically and
    // call flushBuffer when the script is loaded
    // (and executed).
    var script = document.createElement("script");
    script.onload = flushBuffer;
    script.src = "http://someadserver.com/example.js";

})();

http://someadserver.com/example.js的内容

var flashAdObject = "<object>...</object>";
document.write("<div id='example'></div>");

// Since we buffer the data the getElementById will fail
var example = document.getElementById("example");
example.innerHTML = flashAdObject; // ReferenceError: example is not defined

我已经记录了在编写和使用我的document.write替代方案时遇到的不同问题:https://github.com/gregersrygg/crapLoader/wiki/What-to-think-about-when-replacing-document.write 但是使用document.write替代方案的危险在于可能出现的所有未知问题,其中一些甚至无法避免。
document.write("<scr"+"ipt src='http://someadserver.com/adLib.js'></scr"+"ipt>");
adLib.doSomething(); // ReferenceError: adLib is not defined

幸运的是,我在实践中还没有遇到上述问题,但这并不能保证它不会发生 ;)
仍然想尝试吗?试试crapLoader(我的)或writeCapture
你也应该查看友好的iframes。基本上它创建了一个同域iframe,并在那里加载所有内容,而不是在你的文档中。不幸的是,我还没有找到任何处理这个问题的好库。

3

您可能会对我开发的JavaScript库感兴趣,它可以使用document.write在window.onload之后加载第三方脚本。该库内部覆盖了document.write,动态附加DOM元素,并运行任何包含使用document.write的脚本。

与John Resig的解决方案不同(这是我自己代码灵感的一部分),我开发的模块支持部分写入,例如您提供的div示例:

document.write("<"+"div");
document.write(">"+"Done here<"+"/");
document.write("div>");

我的库会在脚本结束之前等待解析和渲染标记。在上面的例子中,它将使用完整字符串"<div>完成</div>"一次运行,而不是部分标记3次运行。
我已经设置了一个演示,在该演示中我动态加载了3个Google广告、一个Amazon小部件以及Google Analytics

3

编辑前的原始答案:


document.write 的问题在于它不能在XHTML文档中使用。因此,最广泛的解决方案(尽管看起来有些严厉)是不要在页面上使用XHTML/xml。由于IE+XHTML和mimetype问题,Google Adsense会出现问题(也许这是件好事:),以及向HTML5的普遍转移,我认为情况并不像看起来那么糟糕。

然而,如果你真的想在页面上使用XHTML,那么你链接到的John的脚本是目前最好的选择。只需要在服务器端检测IE。如果请求来自IE,则不做任何操作(也不要提供application/xhtml+xml mimetype!)。否则将其放入页面的<head>中,就可以开始了。

重新阅读你的问题,你是否对John的脚本有具体的问题?它已知在Safari 2.0中失败,但仅此而已。


我的页面没有使用XHTML,但我在DOM加载后加载第三方脚本,它们会覆盖我的页面。 我的问题是以下代码将失败: document.write('<' + 'div'); <br/>document.write('>' + '文本内容' + '<'); <br/>document.write('\div>'); - Yoav Weiss
@yeeeev:哇,这与XHTML无关?你应该将代码作为代码片段放入你的问题中,这样我们可以更好地帮助你。它现在显示为错误的注释。 - Crescent Fresh
@yeeev:我看到你已经尝试过放置它了。让我编辑你的问题... - Crescent Fresh

1

FWIW,我发现postscribe是目前最好的选择 - 它能很好地处理烦人的广告渲染模块,使我们的页面在不被阻止的情况下加载。


0
为了在DOM渲染后更改页面内容,您需要使用JavaScript库在特定点附加HTML或文本(jQuery、mootools、prototype等),或者只需使用每个DOM元素的innerHTML属性来更改/附加文本。这适用于跨浏览器,并且不需要任何库。

-2
有更好的方法来做这个。 两种方式: 1)追加
<html><head>
<script>
  window.onload = function () {
    var el = document.createTextNode('hello world');
    document.body.appendChild(el);
  }
</script></head><body></body></html>

2) InnerHTML

<html><head><script>
  window.onload = function () {
    document.body.innerHTML = 'hello world';
  }
</script></head><body></body></html>

2
如果您无法更改第三方脚本,则此解决方案无效。另外,解决方案#2将用“hello world”替换整个正文。打字错误? - gregers
@gregers。这只是一种技术的示例。 - Robert Cabri

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