为什么脚本不执行?

问题描述 投票:2回答:2

我正在尝试创建一个使用AJAX刷新其内容,样式和脚本的Web应用程序,因此该站点无需重新加载页面即可更新所有内容。

所以会发生的事情是我首先得到页面,当它加载时我使用ajax向服务器发出请求并获取html内容然后另一个请求获取样式,最后另一个请求获取脚本并将其放入脚本标记

<script> //The script goes here </script>

然后,我将脚本标记放在html内容的末尾,并使用内容完全更新正文

<body>
  The HTML content goes here
  <script> //and here is the script </script>
</body>

请求成功,内容加载,因此样式和我使用浏览器检查器工具时,我可以看到脚本标记内加载的脚本,但脚本不执行。

这是对问题的概述,你可以在这里看到代码https://codepen.io/Yousef-Essam/project/editor/ZJGxea

app.js提供index.html文件,然后索引文件获取script.js文件,该文件使用ajax发送两个请求以获取content.html文件和content.js文件,并将链接标记的href属性设置为样式文件。然后将content.js文件放在带有新内容的脚本标记内。虽然加载了content.html和样式文件,并且脚本标记中加载了脚本,但脚本不会执行。

为什么会发生这种情况,我该如何解决?

--Update--

问题可能在于使用innerHTML,但为什么它不能真正起作用。

此外,我认为eval可能是一个解决方案,但我想要一个更好的解决方案,因为不鼓励使用eval

javascript html ajax single-page-application
2个回答
2
投票

这太长了,不能发表评论,所以我发布作为答案。

你正确地注意到:

我认为eval可能是一个解决方案,但我想要一个更好的解决方案因为不鼓励使用eval

事实上,eval在任何语言中都存在问题,因为它实际上允许正在运行的程序将字符串视为代码。这个问题的一个大问题就是打开了代码注入的大门(在外行的术语:黑客攻击)。但这个建议更多的是理论建议,而不是建议使用什么功能。

那么eval的建议是什么意思?

Eval是允许将字符串解释为代码的任何函数或机制。不同的语言具有不同的功能或特征,允许eval发生。例如,某些语言允许递归字符串插值。有些语言有一个名为interp()的函数,它产生一个子解释器。有些语言字面上有一个名为eval()的函数。

Javascript有四种评估机制:

  1. 使用eval()函数。 传递给eval()的任何字符串都被视为代码。
  2. 设置脚本标记的src属性 假设src属性的值是要下载的javascript文件 这适用于文字代码(HTML本身的实际代码)或动态生成的脚本标记(在javascript中创建脚本元素并设置它的src属性)
  3. 脚本标签的主体 脚本标记内的任何文本都被视为代码 这仅适用于文字代码。正如您所发现的那样,使用innerHTML设置脚本体是被禁止的(并且自从Netscape 4以来 - 第一个使用javascript的浏览器)
  4. javascript: URI协议 :之后的所有内容都被视为代码 这仅在用户单击链接时有效

所以,你要做的是执行eval。你使用eval函数天气并不重要,你仍然试图使用evalinnerHTML(虽然它不起作用)

那么,如果一切都是eval,有没有一种安全的方式在页面上运行javascript?

有 - 至少对于现代浏览器。

现代浏览器实现了一个名为subresource integrity的功能。不幸的是,在写这个答案时,Edge不支持它。基本上你可以计算js文件的哈希值(例如sha1)并在脚本标记中声明它,这样浏览器就可以确认js文件没有被篡改过。以下是一个例子:

<script src="https://cdn.example.com/script.js"
 integrity="sha384-+/M6kredJcxdsqkczBUjMLvqyHb1K/JThDXWsBVxMEeZHEaMKEOEct339VItX1zB"></script>

你是这个偏执狂吗?

这取决于。 eval问题可能对你很重要。如果是这样,从设置了子资源完整性的外部源加载javascript是执行javascript的唯一安全方法。

但并不是每个人都是那种偏执狂。到目前为止,我们一直没有这个功能。无论您使用脚本标记还是调用eval(),以下是一些可以减轻评估代码的经验法则:

  1. 始终确保您只执行静态JavaScript代码。不要尝试从字符串构造代码。
  2. 如果必须从字符串构造代码(例如使用像Handlebars这样的模板语言或使用像Webpack或Browserify这样的捆绑器),那么永远不要在代码中包含任何用户生成的内容。您仍然可以通过发出ajax请求而不是将其包含在代码中来加载内容。
  3. 如果您的代码中必须包含用户生成的内容,请确保清理内容。有几种普遍接受的策略,例如禁止使用<>以及"或逃避特殊字符等特殊字符。甚至还有一些库可以帮到你。

基本上我们想要避免的是像用户输入他们的名字John"; console.log("gotcha");"并以某种方式能够执行代码的情况。


1
投票

HTML规范禁止使用innerHTML解析在页面加载后动态添加为HTML标记的SCRIPT元素(根据生活标准中的note under the text property description)。

如果没有使用eval来解析脚本的类似问题的答案在简单的搜索中并不明显 - 虽然它们可能存在,但我只看到异地提供的替代技术。

这是一个不使用eval的动态加载器。因为加载脚本是异步的,所以它使用(err, data)类型的回调函数来指示何时可以调用脚本。

function loadScript( url, callback) {
    var el = document.createElement("SCRIPT");
    el.type = "text/javascript";
    function finish( err) {
        callback( err, err ? false : true);
    }
    el.onerror = finish;
    if( el.readyState){  // <= IE 10
        el.onreadystatechange = function(){
            if( this.readyState == "complete"){
                finish( null);
            }
        };
    }
    else { 
        el.onload = function() { finish(null) };
    }
    el.setAttribute("async", true);
    el.src = url;
    document.getElementsByTagName("HEAD")[0].appendChild( el);
} 

// example call to load jQuery 3.2.1         

loadScript( "https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js",
   function (err, ok) {
       if( err) {
           console.log( "loading failed: " + err);
        }
        else {
         console.log( "loading success");
        }
    }
);
© www.soinside.com 2019 - 2024. All rights reserved.