使用JavaScript来防止后来的`<script>`标记被评估?

Tre*_*ham 28 html javascript javascript-events

这是一个奇怪的用例,但我有我的理由:

我希望能够写作

<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
Run Code Online (Sandbox Code Playgroud)

在我的标记中,并使用代码first.js,防止或延迟执行second.js.这可能在任何浏览器中都可以吗?如果first.js内联的内容怎么办?(如果有帮助,则假设第二个脚本标记具有id属性.)

由于我得到了几个错过我所得到的答案,我应该澄清一下:

  1. 解决方案必须完全在内first.js.任何需要更改页面原始HTML的内容second.js都是不可接受的.
  2. 可以接受的加载second.js通过Ajax以及使用执行它eval.这很容易.困难的部分是阻止立即执行second.js.
  3. 假设你不知道里面有什么second.js.因此,您不能仅second.js使用no-op函数替换每个调用的全局函数.(另外,这几乎肯定会导致错误.)

如果你知道一个解决方案适用于某些浏览器但不适用于其他浏览器,我很乐意听到它.

示例:为了使这更具体,让我们说代码

<script type="text/javascript">
  function func() {
    window.meaningOfLife = 42;
    window.loadSecond();
  };
  setTimeout(func, 10);
</script>
Run Code Online (Sandbox Code Playgroud)

在两个script包含之前,second.js包含该行

if (window.meaningOfLife !== 42) {throw new Error();}
Run Code Online (Sandbox Code Playgroud)

first.js应该能够通过延迟second.js执行直到window.loadSecond运行来防止此错误.(假设执行的window.loadSecond也是first.js.)它是不是不准碰window.meaningOfLife.

更新:Alohci的答案符合这些要求,但仅限于第二个脚本标记在第一个脚本标记之后立即出现的情况,其间只有空格.如果有人可以扩展他的黑客以避免这种要求,而不会引入其他不必要的后果,那将是惊人的......

Alo*_*hci 30

根据您的具体要求设置,这实际上非常简单,应该完全跨浏览器工作.但是,它确实需要first.js紧跟在second.js之前,除了空格之外没有任何东西.

首先,我们假设HTML看起来像这样:

<!DOCTYPE html>
<html>
  <head>
    <title>Test Case</title>
    <meta charset="UTF-8" />
    <script type="text/javascript">
      function func() {
        window.meaningOfLife = 42;
        window.loadSecond();
      };
    </script>
    <script type="text/javascript" src="first.js"></script>
    <script type="text/javascript" src="second.js"></script>
  </head>
  <body>
    <p>Lorem ipsum dolor sit amet ...</p>
    <a href="javascript:func()">Run Func()</a>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

我删除了setTimeout,因为这会导致func()在start.js运行之前运行导致"loadSecond未定义"错误.相反,我提供了一个锚点来点击运行func().

其次,让我们假设second.js看起来像这样:

document.body.appendChild(document.createTextNode("second.js has run. "));
if (window.meaningOfLife !== 42) {throw new Error();}
Run Code Online (Sandbox Code Playgroud)

在这里,我刚刚添加了一行来将一些文本追加到文档正文中,以便在second.js实际运行时更容易看到.

然后first.js的解决方案是这样的:

function loadSecond()
{
    var runSecond = document.createElement("script");
    runSecond.setAttribute("src", "second.js"); 
    document.body.appendChild(runSecond);
}

document.write("<script type='application/x-suppress'>");
Run Code Online (Sandbox Code Playgroud)

当func()运行时,loadSecond函数就是运行second.js.

解决方案的关键是document.write线.它将<script type='application/x-suppress'>在first.js的close脚本标记和second.js的打开脚本标记之间注入HTML .

解析器将看到此并启动一个新的脚本元素.由于type属性的值不是浏览器识别为JavaScript的值,因此不会尝试运行其内容.(因此,您可以在此处使用无限数量的类型属性值,但必须包含类型属性,因为在缺少的情况下,浏览器将假定脚本的内容是JavaScript.)

然后,second.js脚本的开始标记将被解析为新脚本元素的文本内容,而不会被执行.最后,第二个.js脚本的结束标记将被重新用于关闭新的脚本元素,这意味着HTML的其余部分被正确解析.

您可以在http://www.alohci.net/static/jsprevent/jsprevent.htm查看工作版本

  • @ Ms2ger,这与问题完全无关...... Alohci,很好的答案! (5认同)
  • 你是个天才。@阿洛奇 (2认同)

san*_*eus 7

以下文章介绍了如何阻止(第 3 方)脚本从脚本加载/执行(包括页面标题中的标签和动态添加的标签)。

\n\n

要处理页面上的现有标签:

\n\n
    \n
  1. 使用 MutationObserver 观察脚本元素插入,并在 MutationObserver 回调中备份脚本(以便稍后启用/插入)并将脚本类型更改为“javascript/blocked”(不适用于 IE、Edge、Firefox)。此外,您还可以在 Firefox 中处理已弃用(但有效)的beforescriptexecute事件,以防止脚本加载。
  2. \n
  3. 手动设置类型“javascript/blocked”(适用于所有地方,包括 IE 和 Edge),例如 \n <script type="text/javascript" type="javascript/blocked" src="second.js"></script>,然后在 MutationObserver 回调中备份并稍后重新添加。
  4. \n
\n\n

处理动态添加的标签

\n\n
    \n
  1. 对document.createElement进行猴子修补。
  2. \n
  3. 覆盖 HTMLScriptElement 原型上的 \xe2\x80\x98src\xe2\x80\x99 和 \xe2\x80\x98type\xe2\x80\x99 描述符。
  4. \n
\n\n

这些人还提供了一个Yett库,其中包含文章中描述的方法。

\n


Kyl*_*ild 6

first.js,设置var shouldILoad = true

然后,以second.js这种方式加载:

<script>
    if (shouldILoad) {
        (function() {
            var myscript = document.createElement('script');
            myscript.type = 'text/javascript';
            myscript.src = ('second.js');
            var s = document.getElementById('myscript');
            s.parentNode.insertBefore(myscript, s);
        })();
    }
</script>
Run Code Online (Sandbox Code Playgroud)

(其中'myscript'是您要插入新Script元素之前的某个元素的ID)

  • 对不起,我的原始措辞可能不清楚:假设我无法控制页面的原始HTML; 我只控制first.js的内容. (2认同)

Mar*_*pel 6

据我所知,你不能。如果标记看起来像

<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
Run Code Online (Sandbox Code Playgroud)

您无法从内访问第二个脚本元素first.js,因为在第一个脚本运行时它尚未添加到 DOM(即使您将 分配id给第二个元素也不会)。代码second.js是放在内联还是放在外部文件中都没有关系。

我唯一不明白的是你的第二点。首先你说你不能控制文档的标记,但是你说可以second.js动态加载(使用 AJAX)。