繁体   English   中英

如何知道动态创建的脚本标签被执行了?

[英]How to know that dynamically created script tag was executed?

我正在动态创建一个脚本标签:

var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.defer = true;
script.async = true;
script.text = 'some my javascript content here';
head.appendChild(script);

script.onload = function () {
    // this never get fired..
    debugger;
}

当脚本在其他代码块中执行时如何得到通知? 也许一些事件?

我能够通过向脚本添加 ID,然后在 JS 中手动触发该 DOM 元素上的加载事件来实现此目的。 仅在 Chrome 中测试,根据MDN在旧版 IE 中会出现问题)。

var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.id = 'testing';
script.defer = true;
script.async = true;
script.onload = function () {
    console.log('The script is loaded');
}
script.text = ["console.log('This is from the script');",
               "var script = document.getElementById('testing');",
               "var event = new UIEvent('load');",
               "script.dispatchEvent(event);"].join('');
head.appendChild(script);

小提琴

在现代浏览器中,你可以使用Mutation Observer来检测元素的变化——在这种情况下是head 像这样的东西:

observer = new MutationObserver(function (m) {
    // This will be fired
});
observer.observe(document.head, {childList: true});

不幸的是,这在 IE < 11 中不起作用,但似乎onload在 IE 中被触发,因此您可以将它用于 IE。

jsFiddle 的现场演示

代码每 100 毫秒检查一次脚本是否附加到 DOM。 您可以在整个应用程序中的任何地方使用它,而无需事件侦听器和调度事件。 同样,如果脚本没有附加到您设置的时间间隔内,您可以看到代码将抛出错误的时间间隔。

const waitForScriptToLoad = (scriptName, checkTimeMs, timeOutMs) => {
  let elapsedTime = 0;
  return new Promise((resolve, reject) => {
    setTimeout(x => reject('script: ' + scriptName + ' Timed out!')
      , timeOutMs)
    const time = setInterval(() => {
      elapsedTime += checkTimeMs;
      if (document.body.innerHTML.indexOf(scriptName) > -1) {
        resolve(
          {
            response: 'script: ' + scriptName + ' found!',
            time: (elapsedTime / 1000).toFixed(2) + 's'
          });
        clearInterval(time);
      }
    }, checkTimeMs)
  })
}

waitForScriptToLoad('script_name.js', 100, 20000)
  .then(res => console.log(res))
  .catch(err => console.log(err))

根据我的发现,您可以简单地执行以下操作:

var script = document.createElement("script");

script.innerHTML = "console.log('(1) hello from inline script');"

document.head.appendChild(script);

setTimeout(
  function() {
    console.log("(2) we're done!");
  },
  0 // 👈 zero timeout
);

为什么会这样:零超时基本上意味着“下一个事件周期” - 因为它是一个内联<script>节点,所以当你 append 它到<head>时,任何网络活动都没有延迟,这意味着浏览器将执行在下一个事件周期之后立即内联脚本。

只是不要使用load事件,你对 go 很好。

在 IE 10 + 11 和当前版本的 Chrome、Firefox、Edge、Opera 和 Safari 中测试并工作。

JsFiddle

如果您的脚本需要同时支持内联脚本和外部脚本,只需使用if (script.src)之类的条件来测试脚本是否为外部脚本 - 然后有条件地使用load事件侦听器或零超时。

在设置脚本源之前,您需要定义onload函数。 但是,正如 Teemu 告诉我的那样,由于您是通过text属性编写 javascript,因此onload事件永远不会触发。 您最好的选择是拥有一个外部 js 文件并通过src属性加载它。

顺序应该是:

  • 将脚本元素附加到 DOM
  • 定义加载函数
  • 定义src
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.defer = true;
script.async = true;
head.appendChild(script);
script.onload = function () {
    // this never get fired..
    debugger;
}
script.src = 'scriptName.js';

然后你的 onload 事件应该被触发,你可以插入一个console.log("script has loaded!"); 函数中的语句。

将代码提取到 .js 文件中。

添加script.src = 'yourjs.js'; 到脚本。

当脚本被添加到 DOM 时,.js 文件会被沉浸式执行。

yourjs.js添加一个if(console){console.debug('helloworld.');} ,您将看到该消息。

另一种解决方案(不使用onloadsrc

window.onload = function(){

    var script = document.createElement('SCRIPT'),
        content = 'console.debug("Hello Kitty");'; // or console.log

    script.type = 'text/javascript';

    try {
      script.appendChild(document.createTextNode(content));
    } catch (e) {
      script.text = content;
    } finally {
      document.getElementsByTagName("HEAD")[0].appendChild(script);
    }
};

工作演示(在 IE、FF、Chrome 和 Opera 上测试)

出于测试目的,您始终可以添加对 alert() 函数的调用。 但是,在查看您的代码后,我不确定我在其中看到了任何实际执行函数调用的内容。 在执行 appendChild() 之前,可能应该将“onload”事件处理程序添加到动态创建部分。

不久前我写了一些代码来做一些动态脚本创建,它工作正常。 我的代码有两个主要功能,一个创建脚本(一堆),从“.js”文件加载数据,另一个调用这些脚本中的函数,按名称确定的顺序(使用“eval ()“ 功能)。 你的函数没有任何我能看到的名字......我知道很多人不喜欢使用 eval() 函数,但只要它唯一调用的东西是你完整写的东西,应该没问题。

这段代码让浏览器,而不是 Web 服务器,动态创建一个可点击链接项目的菜单(每个可点击链接看起来像一个普通的超链接,但实际上是一个 JavaScript 结构,浏览器必须启用 JavaScript 才能使链接工作--但是它必须启用 JavaScript 才能创建菜单,所以没问题!):

 var F00, F01, F02, F03, F04, F05, F06, F07, F08, F09,
     F10, F11, F12, F13, F14, F15, F16, F17, F18, F19;
 var dat = new Array();
 var form, script, str, st2, tmp, tmp2, dtno, indx, unde;

 function initialize()
 { window.name="MyMenu";
   form = document.getElementById('MENU');
   for(indx=0; indx<20; indx++)
   { str = "0" + indx;
     tmp = str.length - 2;
     str = str.substr(tmp);
     script = document.createElement('script');
     script.type = 'text/javascript';
     script.src = str + ".js";
     form.appendChild(script);
   }
   window.setTimeout("BuildMenu();", 1000); //delay is necessary;
      // scripts are actually only loaded after the function ends,
      // and you need to allow time for it to finish
      // before calling the functions in those scripts.
   return;
 }

请注意,此代码已准备好处理 20 个菜单项,即使您当前只有 5 个菜单项准备就绪。 如果 20 个最大的“.js”文件中的某些文件不存在,则上述函数不会崩溃。

 function BuildMenu()
 { dtno = 0;
   for(indx=0; indx<20; indx++)
   { str = "0" + indx;
     tmp = str.length - 2;
     str = "F" + str.substr(tmp);
     tmp = eval(str);
     if(tmp != unde)  //no value is assigned to 'unde'; it is undefined;
                      //this is a valid way to find out
                      //whether or not a ".js" script existed/was-loaded.
       dat[dtno++] = eval(str + "()");
   }
   dat.sort();
   for(indx=0; indx<dtno; indx++)
   { str = "0" + indx;
     tmp = str.length - 2;
     str = "W" + str.substr(tmp);
     tmp = document.getElementById(str);
     tmp.innerHTML = "<a onclick=\"window.open('" + dat[indx][1] + "', 'MyMenu');\" style=\"color:#0000ff;text-decoration:underline;cursor:pointer;\">" + dat[indx][0] + "</a> " + dat[indx][2] + "<br />";
   }
   return;
 }

在网页的 HTML 代码中,有这样的:

 <body onload="initialize();>
 <br />
 <form id="MENU" action="" onsubmit="return false;">
 <span id="W00">&nbsp;</span>
 <span id="W01">&nbsp;</span>
 <span id="W02">&nbsp;</span>
 <span id="W03">&nbsp;</span>
 <span id="W04">&nbsp;</span>
 <span id="W05">&nbsp;</span>
 <span id="W06">&nbsp;</span>
 <span id="W07">&nbsp;</span>
 <span id="W08">&nbsp;</span>
 <span id="W09">&nbsp;</span>
 <span id="W10">&nbsp;</span>
 <span id="W11">&nbsp;</span>
 <span id="W12">&nbsp;</span>
 <span id="W13">&nbsp;</span>
 <span id="W14">&nbsp;</span>
 <span id="W15">&nbsp;</span>
 <span id="W16">&nbsp;</span>
 <span id="W17">&nbsp;</span>
 <span id="W18">&nbsp;</span>
 <span id="W19">&nbsp;</span>
 </form>

以下是“00.js”文件的一些示例代码:

 <!--
 function F00()
 { return ["Menu Item Alpha", "./Alpha.htm", "Select Web Page Alpha"];
 }
 -->

请注意,该函数仅返回 3 个数组元素(字符串)。 BuildMenu() 函数使用这些字符串构建网页上的菜单,部分是通过修改 span 元素的 innerHTML。

暂无
暂无

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

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