简体   繁体   English

使用CDN或外部域在内联脚本上执行脚本文件在HTML注入时

[英]Script File Executing After Inline Script With CDN or External Domain On HTML injection

I am having an issue with HTML injection into an already loaded DOM where the inline javascript is being loaded after the script file is downloaded. 我有一个HTML注入到已加载的DOM的问题,其中在下载脚本文件后加载内联javascript。 From what I know this should not be async and the inline script should execute after the script file. 据我所知,这不应该是异步的,并且内联脚本应该在脚本文件之后执行。 This works if the domain name is the same as the calling page, but using a CDN or even a subdomain does the same thing. 如果域名与调用页面相同,则此方法有效,但使用CDN甚至子域也可以执行相同的操作。 Is there something I should do to rework how I am calling these? 我是否应该做些什么来改变我如何称呼这些? I swear this worked before as I had the CDN on for over a week but maybe I never caught this issue. 我发誓这是有效的,因为我有一个多星期的CDN,但也许我从未发现过这个问题。

Console 安慰

Loading Inline Script
VM1400:3 Uncaught TypeError: Cannot read property 'init' of undefined(anonymous function) 
app.members.event.js?v=204&_=1453644424985:5 Loading Script File
app.members.event.js?v=204&_=1453644424985:71 Finished Script File

Javascript 使用Javascript

<script type="text/javascript" src="https://test.azureedge.net/Areas/Directors/scripts/app.members.event.js?v=204"></script>
<script type="text/javascript">
console.log('Loading Inline Script');
    app.viewModel.members.event.init();
console.log('Finished Inline Script');

One way is to use jquery's getScript() function. 一种方法是使用jquery的getScript()函数。

But preferably, you may use native javascript to load the script file and then run the inline script. 但最好是,您可以使用本机javascript加载脚本文件,然后运行内联脚本。

Maybe i have not understood the question clearly. 也许我没有清楚地理解这个问题。

Edit: This is a quote from the HTML5 spec regarding script elements. 编辑:这是关于脚本元素的HTML5规范的引用。

If the element has a src content attribute, run these substeps: 如果元素具有src内容属性,请运行以下子步骤:

Let src be the value of the element's src attribute. 设src为元素src属性的值。

If src is the empty string, queue a task to fire a simple event named error at the element, and abort these steps. 如果src是空字符串,则将任务排队以在元素处触发名为error的简单事件,并中止这些步骤。

Resolve src relative to the element. 相对于元素解析src。

If the previous step failed, queue a task to fire a simple event named error at the element, and abort these steps. 如果上一步失败,则将任务排队以在元素上触发名为error的简单事件,并中止这些步骤。

Do a potentially CORS-enabled fetch of the resulting absolute URL, with the mode being the current state of the element's crossorigin content attribute, the origin being the origin of the script element's Document, and the default origin behaviour set to taint. 对结果绝对URL进行潜在的CORS启用提取,模式是元素的crossorigin content属性的当前状态,origin是script元素Document的原点,默认的原始行为设置为污染。

The resource obtained in this fashion can be either CORS-same-origin or CORS-cross-origin. 以这种方式获得的资源可以是CORS-同源或CORS-交叉起源。 This only affects how error reporting happens. 这只会影响错误报告的发生方式。

For performance reasons, user agents may start fetching the script (as defined above) as soon as the src attribute is set, instead, in the hope that the element will be inserted into the document (and that the crossorigin attribute won't change value in the meantime). 出于性能原因,用户代理可以在设置src属性后立即开始获取脚本(如上所述),而希望元素将插入到文档中(并且crossorigin属性不会更改值)同时)。 Either way, once the element is inserted into the document, the load must have started as described in this step. 无论哪种方式,一旦元素插入到文档中,负载必须已按照此步骤中的描述启动。 If the UA performs such prefetching, but the element is never inserted in the document, or the src attribute is dynamically changed, or the crossorigin attribute is dynamically changed, then the user agent will not execute the script so obtained, and the fetching process will have been effectively wasted. 如果UA执行这样的预取,但是元素从未插入文档中,或者动态更改了src属性,或者动态更改了crossorigin属性,则用户代理将不会执行如此获取的脚本,并且获取过程将已经被有效地浪费了。

Then, the first of the following options that describes the situation must be followed: 然后,必须遵循描述该情况的以下第一个选项:

If the element has a src attribute, and the element has a defer attribute, and the element has been flagged as "parser-inserted", and the element does not have an async attribute The element must be added to the end of the list of scripts that will execute when the document has finished parsing associated with the Document of the parser that created the element. 如果元素具有src属性,并且该元素具有defer属性,并且该元素已被标记为“解析器插入”,并且该元素没有async属性该元素必须添加到列表的末尾当文档完成与创建元素的解析器的Document相关联的解析时将执行的脚本。

The task that the networking task source places on the task queue once the fetching algorithm has completed must set the element's "ready to be parser-executed" flag. 一旦获取算法完成,网络任务源在任务队列上放置的任务必须设置元素的“准备好被解析器执行”标志。 The parser will handle executing the script. 解析器将处理执行脚本。

If the element has a src attribute, and the element has been flagged as "parser-inserted", and the element does not have an async attribute The element is the pending parsing-blocking script of the Document of the parser that created the element. 如果元素具有src属性,并且该元素已标记为“parser-inserted”,并且该元素没有async属性该元素是创建该元素的解析器Document的挂起解析阻止脚本。 (There can only be one such script per Document at a time.) (每个文档一次只能有一个这样的脚本。)

The task that the networking task source places on the task queue once the fetching algorithm has completed must set the element's "ready to be parser-executed" flag. 一旦获取算法完成,网络任务源在任务队列上放置的任务必须设置元素的“准备好被解析器执行”标志。 The parser will handle executing the script. 解析器将处理执行脚本。

If the element does not have a src attribute, and the element has been flagged as "parser-inserted", and either the parser that created the script is an XML parser or it's an HTML parser whose script nesting level is not greater than one, and the Document of the HTML parser or XML parser that created the script element has a style sheet that is blocking scripts The element is the pending parsing-blocking script of the Document of the parser that created the element. 如果元素没有src属性,并且元素已被标记为“解析器插入”,则创建脚本的解析器是XML解析器,或者是脚本嵌套级别不大于1的HTML解析器,创建脚本元素的HTML解析器或XML解析器的Document包含一个阻塞脚本的样式表元素是创建该元素的解析器Document的挂起解析阻塞脚本。 (There can only be one such script per Document at a time.) (每个文档一次只能有一个这样的脚本。)

Set the element's "ready to be parser-executed" flag. 设置元素的“准备好解析器执行”标志。 The parser will handle executing the script. 解析器将处理执行脚本。

If the element has a src attribute, does not have an async attribute, and does not have the "force-async" flag set The element must be added to the end of the list of scripts that will execute in order as soon as possible associated with the Document of the script element at the time the prepare a script algorithm started. 如果元素具有src属性,没有async属性,并且没有设置“force-async”标志元素必须添加到脚本列表的末尾,这些脚本将按顺序执行,并尽快关联在准备脚本算法开始时使用脚本元素的Document。

The task that the networking task source places on the task queue once the fetching algorithm has completed must run the following steps: 完成提取算法后,网络任务源在任务队列上放置的任务必须执行以下步骤:

If the element is not now the first element in the list of scripts that will execute in order as soon as possible to which it was added above, then mark the element as ready but abort these steps without executing the script yet. 如果元素现在不是脚本列表中的第一个元素,它将尽快按顺序执行,那么将元素标记为就绪,但是在不执行脚本的情况下中止这些步骤。

Execution: Execute the script block corresponding to the first script element in this list of scripts that will execute in order as soon as possible. 执行:执行与此脚本列表中第一个脚本元素对应的脚本块,该脚本将尽快按顺序执行。

Remove the first element from this list of scripts that will execute in order as soon as possible. 从此脚本列表中删除第一个元素,这些脚本将尽快按顺序执行。

If this list of scripts that will execute in order as soon as possible is still not empty and the first entry has already been marked as ready, then jump back to the step labeled execution. 如果这个将尽快按顺序执行的脚本列表仍然不为空且第一个条目已被标记为就绪,则跳回标记为执行的步骤。

If the element has a src attribute The element must be added to the set of scripts that will execute as soon as possible of the Document of the script element at the time the prepare a script algorithm started. 如果元素具有src属性必须将元素添加到脚本集中,这些脚本将在准备脚本算法开始时尽快执行脚本元素的Document。

The task that the networking task source places on the task queue once the fetching algorithm has completed must execute the script block and then remove the element from the set of scripts that will execute as soon as possible. 一旦提取算法完成,网络任务源放置在任务队列上的任务必须执行脚本块,然后从将尽快执行的脚本集中删除该元素。

Otherwise The user agent must immediately execute the script block, even if other scripts are already executing. 否则,即使其他脚本已在执行,用户代理也必须立即执行脚本块。 Fetching an external script must delay the load event of the element's document until the task that is queued by the networking task source once the resource has been fetched (defined above) has been run. 获取外部脚本必须延迟元素文档的加载事件,直到已经运行了获取资源(上面定义)后由网络任务源排队的任务。

From this I think that your "external" file is loaded after the inline script block. 从这里我认为你的“外部”文件是在内联脚本块之后加载的。 I would therefore use the "getScript()" function from jquery to make sure that the script is loaded before the inline script block. 因此,我将使用jquery中的“getScript()”函数来确保在内联脚本块之前加载脚本。

I have two theories: 我有两个理论:

  1. Might be that there's something in the external script that's delaying the creation of the app.viewModel.members object (either a timeout or an event handler that takes a while to fulfil). 可能是外部脚本中有一些东西延迟了app.viewModel.members对象的创建(超时或需要一段时间才能完成的事件处理程序)。 This can be easily tested by setting a long timeout in your inline script (fi 5000+ms) and then checking if the models object exists. 这可以通过在内联脚本中设置长超时(fi 5000 + ms)然后检查模型对象是否存在来轻松测试。

  2. There's something funky going on when loading a same origin script. 加载相同的原始脚本时会发生一些时髦的事情。

In this case you could try is delay the execution of your inline script by doing the following: 在这种情况下,您可以尝试通过执行以下操作来延迟内联脚本的执行:

<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
    app.viewModel.members.event.init();
});
</script>

or just put your inline code in an external .js file and call it with the 'deferred' flag: 或者只是将内联代码放在外部.js文件中,并使用'deferred'标志调用它:

<script type="text/javascript" src="https://test.azureedge.net/Areas/Directors/scripts/app.members.event.js?v=204"></script>
<script type="text/javascript" src="{link-to-external-js-file}" defer></script>

This is a common problem in the injection scenerio. 这是注射场景中的常见问题。 It occurs due to the variable delays in script availability, as well as due to the parallel and differing implementations on various browsers. 它是由于脚本可用性的可变延迟以及各种浏览器上的并行和不同实现而发生的。

There are 3 options, depending on whether the source code is available for editing or not, and if more than 2 dependencies exist between the script files. 有3个选项,具体取决于源代码是否可用于编辑,以及脚本文件之间是否存在2个以上的依赖项。

Option 1. Using defer attribute in the script tag 选项1.在脚本标记中使用defer属性

This option can be used, if both scripts are remote (ie, not inline) 如果两个脚本都是远程的(即不是内联的),则可以使用此选项

"defer" indicates to the browser that the script has to execute after the document has been parsed (quoted from MDN). “defer”向浏览器指示在解析文档(从MDN引用)之后脚本必须执行。 This is applicable only for remote (not inline) scripts, that have the "src" attribute. 这仅适用于具有“src”属性的远程(非内联)脚本。

https://html.spec.whatwg.org/multipage/scripting.html#attr-script-defer https://html.spec.whatwg.org/multipage/scripting.html#attr-script-defer

You can use it like below. 您可以像下面一样使用它。

Defer is supported on major browsers, and I validated on Chrome, Firefox, Webkit on Tizen, and Safari: 主要浏览器支持Defer,我在Chrome,Firefox,Webkit on Tizen和Safari上进行了验证:

https://developer.mozilla.org/en/docs/Web/HTML/Element/script#Browser_compatibility https://developer.mozilla.org/en/docs/Web/HTML/Element/script#Browser_compatibility

To provide concrete example of the above cases, refer below. 提供上述案例的具体例子,请参阅下文。 Note that the below have been validated on Firefox, Chrome, IE11, Safari on iPhone, and Webkit on Tizen. 请注意,以下内容已在Firefox,Chrome,IE11,iPhone上的Safari和Tizen上的Webkit上进行了验证。

Case 1: 情况1:

Many Javascript files - All independent: 许多Javascript文件 - 全部独立:

If there is no dependency, the "defer" attribute allows the HTML to be loaded quickly. 如果没有依赖关系,则“defer”属性允许快速加载HTML。 The script takes over after downloading, without issues (assuming onload etc are taken care). 下载后脚本接管,没有问题(假设onload等被照顾)。

Case 2: 案例2:

Two javascript files test1.js and test2.js - One dependent on the other: 两个javascript文件test1.js和test2.js - 一个依赖于另一个:

If test2.js is dependent on the loading of test1.js, then the script tag for test2.js "only" should have the defer attribute. 如果test2.js依赖于test1.js的加载,那么test2.js“only”的脚本标记应该具有defer属性。

This usage is shown in 此用法显示在

http://www.gpupowered.org/loadtest/2_defer.html http://www.gpupowered.org/loadtest/2_defer.html

Incorrect usage is shown in 使用不正确显示在

http://www.gpupowered.org/loadtest/no_defer.html (Both scripts do not have defer tag - this fails) http://www.gpupowered.org/loadtest/all_defer.html (Both scripts having defer tag - this also fails) http://www.gpupowered.org/loadtest/no_defer.html (两个脚本没有延迟标记 - 这都失败了) http://www.gpupowered.org/loadtest/all_defer.html (两个脚本都有延迟标记 - 这个也失败了)

Async usage that does not work is at, 不起作用的异步使用是,

http://gpupowered.org/loadtest/2_async.html (this fails) http://gpupowered.org/loadtest/2_async.html (此操作失败)

Where does "defer" not meet the needs ? “推迟”哪里不符合需求?

If the functionality is split across several JS files (say n), and all "n-1" need to be downloaded before "n"th file can start processing some variables, even though the "defer" attribute might be present on all the script source tags, it is rendered irrelevant because the order in which they are received are indeterminate. 如果功能被分成几个JS文件(比如n),并且所有“n-1”需要在“n”文件开始处理一些变量之前下载,即使“defer”属性可能存在于所有脚本源标记,它呈现无关紧要,因为它们的接收顺序是不确定的。

More background on defer and the various options for deferred loading (doesnot cover the multi defer case) http://www.html5rocks.com/en/tutorials/speed/script-loading/ 关于延迟的更多背景和延迟加载的各种选项(不包括多延迟案例) http://www.html5rocks.com/en/tutorials/speed/script-loading/

https://developer.mozilla.org/en/docs/Web/HTML/Element/script https://developer.mozilla.org/en/docs/Web/HTML/Element/script

Option 2: Using state variables 选项2:使用状态变量

This option can be used if some additional state variables can be added to both javascript source files. 如果可以将两个其他状态变量添加到两个javascript源文件,则可以使用此选项。

The approach relies on a named variable in the dependent js file, and a named function in the js file that uses the dependent file. 该方法依赖于依赖js文件中的命名变量,以及使用依赖文件的js文件中的命名函数。 If the dependent file is not loaded at the time the user file is trying to access its functionality, it exits and will be called back when it is really loaded. 如果在用户文件尝试访问其功能时未加载依赖文件,则它将退出并在真正加载时将被回调。

This is demonstrated in the below html file. 这在以下html文件中进行了演示。

http://gpupowered.org/loadtest/variable.html (works correctly) http://gpupowered.org/loadtest/variable.html (正常工作)

This option does not work if the loading needs to happen repetitively (ie, loading of multiple files of same name etc). 如果加载需要重复发生(即加载多个同名文件等),则此选项不起作用。

Option 3: Native script loader 选项3:本机脚本加载器

In this case, there are multiple javascript files having dependencies between each other. 在这种情况下,存在多个彼此之间具有依赖性的javascript文件。

There is no solution for this case using defer or async or other specification provided tags. 对于使用延迟或异步或其他规范提供的标记的情况,没有解决方案。 For the use-case I had in the remote labs in gpupowered.org, I had to implement my own native script loader using XMLHttpRequest, and the source for this is provided in the link below. 对于我在gpupowered.org的远程实验室中使用的用例,我必须使用XMLHttpRequest实现我自己的本机脚本加载器,其源代码在下面的链接中提供。 This uses worker threads as some of the textures I have are fairly big. 这使用工作线程,因为我拥有的一些纹理相当大。 The callback function can be used to implement the dependency logic as per the application needs. 回调函数可用于根据应用程序需要实现依赖关系逻辑。 For example, keep count of all loaded scripts and then trigger full execution etc. 例如,保持所有已加载脚本的计数,然后触发完全执行等。

https://github.com/prabindh/gpupowered.gl/blob/master/worker/worker_object_loader.js https://github.com/prabindh/gpupowered.gl/blob/master/worker/worker_object_loader.js

The jquery script loader uses the HTTP request as well, though I have not checked if it uses a worker for loading. jquery脚本加载器也使用HTTP请求,但我还没有检查它是否使用worker进行加载。 https://api.jquery.com/jquery.getscript/ https://api.jquery.com/jquery.getscript/

Use this: 用这个:

<script type="text/javascript" src="https://test.azureedge.net/Areas/Directors/scripts/app.members.event.js?v=204&onload=onloadCallback"></script

<script type="text/javascript">function onloadCallback(){
app.viewModel.members.event.init();}</script>
function onloadCallback(){
    app.viewModel.members.event.init();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM