动态插入DOM元素并执行脚本

在 HTML 中脚本以 <script> 来标记,通过设置其内容或src属性执行内联脚本或外部脚本。 本文讨论动态地插入脚本标签时浏览器对它的解析、下载和执行行为。 动态插入脚本的场景可能包括使用 AJAX 获取脚本并动态执行(多用于性能优化), 以及运行时决定执行页面模板中的某段脚本(多用于单页异步)。

动态执行脚本还有其他方式,比如evalnew Function,这些不在本文的讨论范围。

执行内联脚本

为了插入内联脚本,可以创建一个script元素并设置其内容,插入到 DOM 即可立即执行。 例如:

var script = document.createElement('script');
script.text = 'console.log("foo")';
document.body.appendChild(script);

以下写法是等价的

script.text = 'console.log("foo")';
script.innerText = 'console.log("foo")';
script.innerHTML = 'console.log("foo")';

需要注意的是内联脚本是否能够执行仍然受制于CSP策略指令, 该策略是由Content-Security-Policy响应头(rfc7762)控制的。 例如下列设置将会禁止执行harttle.com以外的任何内联脚本。

Content-Security-Policy: script-src harttle.com;

执行外部脚本

插入并执行外部脚本的方法与内联脚本类似,只需设置script.src属性并插入到 DOM。 例如:

var script = document.createElement('script');
script.src = 'foo.js';
document.body.appendChild(script);

与内联脚本不同的是,外部脚本的插入是异步的不会阻塞 DOM 解析。 详见异步渲染的下载和阻塞行为一文。

此外有一个细节可能需要注意:一旦设置了src属性,<script> 标签本身的所有内容就不会再被执行了。

innerHTML

innerHTML属性可用来设置 DOM 内容,但不可用来插入并执行<script>。 下面的内联脚本和外部脚本都不会被执行:

document.body.innerHTML = '<script src="foo.js"></script>'
document.body.innerHTML = '<script>console.log("foo")</script>'

在设置 innerHTML 时,浏览器会初始化一个新的 HTML Parser 来解析它。 只要与该 Parser 关联的 DOM 启用了 JavaScript(通常是启用的),脚本的 scripting flag 就为真, 但是即便如此,HTML 片段的解析过程中,脚本是不会执行的

Create a new HTML parser, and associate it with the just created Document node. – 12.4 Parsing HTML fragments, WHATWG

The scripting flag can be enabled even when the parser was originally created for the HTML fragment parsing algorithm, even though script elements don’t execute in that case. – 12.2.3.5 Other parsing state flags, WHATWG

事实上,设置innerHTMLouterHTML都不执行脚本,但document.write()是会同步执行的。

When inserted using the document.write() method, script elements execute (typically blocking further script execution or HTML parsing), but when inserted using innerHTML and outerHTML attributes, they do not execute at all. – 4.12.1 The script element WHATWG

jQuery DOM Eval

我们知道使用 jQuery html() 方法时插入的脚本总是执行的,jQuery 会检查传入的内容,并执行其中的每一个脚本。 源码在src/core/DOMEval.js

function DOMEval( code, doc ) {
    doc = doc || document;
    var script = doc.createElement( "script" );
    script.text = code;
    doc.head.appendChild( script ).parentNode.removeChild( script );
}

转载自:http://harttle.com/2017/01/16/dynamic-script-insertion.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值