你知道为什么下面这段代码没有将脚本元素添加到DOM中吗?

var code = "<script></script>";
$("#someElement").append(code);

当前回答

你不需要jQuery来创建一个Script DOM元素。它可以用香草ES6这样做:

const script = "console.log('Did it work?')"
new Promise((resolve, reject) => {
  (function(i,s,o,g,r,a,m){
      a=s.createElement(o),m=s.getElementsByTagName(o)[0];
      a.innerText=g;
      a.onload=r;m.parentNode.insertBefore(a,m)}
  )(window,document,'script',script, resolve())
}).then(() => console.log('Sure did!'))

它不需要包装在Promise中,但是这样做允许您在脚本加载时解析Promise,有助于防止长时间运行的脚本出现竞争条件。

其他回答

可以使用jQuery函数getScript动态加载JavaScript文件

$.getScript('http://www.whatever.com/shareprice/shareprice.js', function() {
  Display.sharePrice();
});

现在外部脚本将被调用,如果不能加载它,它将优雅地降级。

好消息是:

100%有效。

只需在脚本标记中添加一些内容,例如alert('voila!');。您可能想问的正确问题是,“为什么我在DOM中没有看到它?”

Karl Swedberg在jQuery API网站上对访问者的评论做了一个很好的解释。我不想重复他所有的话,你可以直接在这里阅读(我发现很难浏览那里的评论)。

All of jQuery's insertion methods use a domManip function internally to clean/process elements before and after they are inserted into the DOM. One of the things the domManip function does is pull out any script elements about to be inserted and run them through an "evalScript routine" rather than inject them with the rest of the DOM fragment. It inserts the scripts separately, evaluates them, and then removes them from the DOM. I believe that one of the reasons jQuery does this is to avoid "Permission Denied" errors that can occur in Internet Explorer when inserting scripts under certain circumstances. It also avoids repeatedly inserting/evaluating the same script (which could potentially cause problems) if it is within a containing element that you are inserting and then moving around the DOM.

接下来,我将通过使用.append()函数添加一个脚本来总结坏消息。


坏消息是…

你不能调试代码。

我不是在开玩笑,即使你添加了调试器;关键字,你将最终只得到对象的调用堆栈,而看不到源代码上的断点,(更不用说这个关键字只适用于webkit浏览器,所有其他主流浏览器似乎都省略了这个关键字)。

如果您完全理解您的代码是做什么的,那么这将是一个小缺点。但如果你不这样做,你最终会添加一个调试器;关键字到处都是,只是为了找出你(或我)的代码有什么问题。不管怎样,还有另一种选择,不要忘记javascript可以原生操作HTML DOM。


解决方案。

使用javascript(而不是jQuery)来操作HTML DOM

如果您不想失去调试功能,那么您可以使用javascript原生HTML DOM操作。想想这个例子:

var script   = document.createElement("script");
script.type  = "text/javascript";
script.src   = "path/to/your/javascript.js";    // use this for linked script
script.text  = "alert('voila!');"               // use this for inline script
document.body.appendChild(script);

就在那里,就像过去一样,不是吗。不要忘记清理DOM或内存中所有被引用且不再需要的对象,以防止内存泄漏。你可以考虑下面的代码来清理:

document.body.removechild(document.body.lastChild);
delete UnusedReferencedObjects; // replace UnusedReferencedObject with any object you created in the script you load.

这种解决方法的缺点是可能会不小心添加重复的脚本,这很糟糕。从这里开始,您可以稍微模仿.append()函数,在添加之前添加对象验证,并在添加脚本后立即从DOM中删除脚本。想想这个例子:

function AddScript(url, object){
    if (object != null){
        // add script
        var script   = document.createElement("script");
        script.type  = "text/javascript";
        script.src   = "path/to/your/javascript.js";
        document.body.appendChild(script);

        // remove from the dom
        document.body.removeChild(document.body.lastChild);
        return true;
    } else {
        return false;
    };
};

function DeleteObject(UnusedReferencedObjects) {
    delete UnusedReferencedObjects;
}

这样,您可以添加带有调试功能的脚本,同时避免脚本重复。这只是一个原型,你可以把它扩展成任何你想要的样子。我一直在使用这种方法,并对此非常满意。果然,我永远不会使用jQuery .append()来添加脚本。

试试这个可能会有帮助:

var fileref=document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src","scriptAnalytics.js");
document.getElementsByTagName("head")[0].appendChild(fileref);

我曾见过一些浏览器在你直接执行某些更改时不尊重它们的问题(我指的是从文本创建HTML,就像你尝试使用script标记一样),但当你使用内置命令执行这些更改时,事情会变得更好。试试这个:

var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = url;
$("#someElement").append( script );

来自:JSON for jQuery

这是我认为最好的解决办法。谷歌分析就是这样注入的。

var (function(){
    var p="https:" == document.location.protocol ? "https://" : "http://";
        d=document,
        g=d.createElement('script'),
        s=d.getElementsByTagName('script')[0];
        g.type='text/javascript';
        g.src=p+'url-to-your-script.js';
        s.parentNode.insertBefore(g,s); })();