【问题标题】:How to load javascript files using event listeners如何使用事件监听器加载 javascript 文件
【发布时间】:2021-01-11 13:57:37
【问题描述】:

这个问题不是重复的

Conditionally load JavaScript file

也不是这个

How to include an external javascript file conditionally?

我已经完成了它们,它们与我想要做的有点相似,但并不完全相同。以下是在上述问题中,用户只想根据条件加载脚本文件一次。但就我而言,我想根据点击事件加载不同的 js 文件。

所以在我的例子中,我有一个 HTML 文档:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Experiment</title>
    <link href="s.css" rel="stylesheet" type="text/css">
    
</head>
<body>
    <div class="navigation">
        <nav>
            <ul>
                <li id="home_btn"> Home</li>
                <li id="about_btn"> About </li>
            </ul>
        </nav>
    </div>

    <canvas id="myCanvas">

    </canvas>

    <div class="notePane">
        <p> This is just a bunch of text not explanation</p>
    </div>
   
</body>
<script src="./exp.js" type="text/javascript"></script>
</html>

这个 h.html 文件链接到一个 exp.js 文件。现在在 exp.js 文件中:

var h_btn = document.getElementById("home_btn");
var a_btn = document.getElementById("about_btn");
var head = document.getElementsByTagName("head")[0];
var js = document.createElement("script");
js.type="module";


h_btn.addEventListener("click", showHome );
a_btn.addEventListener("click", showAbout);

function showHome() {
   
    js.src="./j1.js";
    head.appendChild(js);
}

function showAbout() {
    js.src="./j2.js";
    head.appendChild(js);
}

所以当我点击网页上的 h_btn 时一切正常。它加载 j1.js。但是当我点击网页上的 a_btn 时,我希望看到 j2.js 链接,但我没有看到它。我必须刷新页面,然后单击 a_btn 以查看链接的 j2.js。如何链接 j1.js 和 j2.js,这样我就不必一次又一次地刷新页面来加载正确的脚本。

【问题讨论】:

  • 如果你克隆脚本元素而不是使用它,它有什么不同吗?重复使用相同的元素不是一个好主意。当您重用它时,它不会创建另一个元素,而是将它从文档中的一个位置移动到另一个位置。

标签: javascript


【解决方案1】:

更新:OP 已更新问题要求,以便在单击另一个 JS 文件时“卸载”另一个 JS 文件。一旦加载了 JS 文件,就无法撤消所有运行时逻辑:唯一的方法是重新加载页面。删除 &lt;script&gt; 标记或更改 src 属性不会神奇地取消绑定事件侦听器或“取消声明”变量。

因此,如果 OP 想要“重新开始”,唯一的方法是检查之前是否已加载自定义脚本:如果已加载,我们会强制重新加载页面。当然,有很多方法可以“通知”下一页要加载哪个源(如果可用):在下面的示例中,我们使用查询字符串:

var h_btn = document.getElementById("home_btn");
var a_btn = document.getElementById("about_btn");
var head = document.getElementsByTagName("head")[0];
var appendedScriptKey;
var scripts = {
    'home': './j1.js',
    'about': './j2.js'
}

h_btn.addEventListener("click", showHome);
a_btn.addEventListener("click", showAbout);

// Check query string if a specific script is set
var params = (new URL(document.location)).searchParams;
var scriptKey = params.get('scriptKey');
if (scriptKey && scriptKey in scripts) {
    appendScript(scriptKey);
}

function appendScript(key) {
    if (hasAppendedScript) {
        location.href = location.href + (location.search ? '?' : '&') + 'script=' + key;
        location.reload();
    }

    var js = document.createElement("script");
    js.type="module";

    js.src = scripts[key];
    head.appendChild(js);

    appendedScript = key;
}

function showHome() {
    appendedScriptKey('home');
}

function showAbout() {
    appendScript('about');
}

这是因为Node.appendChild() 的工作原理。第一次单击有效,因为您正在创建一个新元素并将其插入到您的文档中。但是,由于节点已经存在,第二次单击将无法正常工作:

Node.appendChild() 方法将一个节点添加到指定父节点的子节点列表的末尾。如果给定的子节点是对文档中现有节点的引用,appendChild() 会将其从当前位置移动到新位置

这意味着第二次点击只会改变已经注入的&lt;script&gt;元素的src属性,而不是创建一个新的,这也意味着第二个脚本src不会被加载。

一个解决方案是使用一个每次都会创建一个脚本标签的函数:

var h_btn = document.getElementById("home_btn");
var a_btn = document.getElementById("about_btn");
var head = document.getElementsByTagName("head")[0];
h_btn.addEventListener("click", showHome);
a_btn.addEventListener("click", showAbout);

function insertScript(src) {
  var js = document.createElement("script");
  js.type = "module";
  js.src = src;
  head.appendChild(js);
}

function showHome() {
  insertScript('./j1.js');
}

function showAbout() {
  insertScript('./j2.js');
}

但这也意味着多次点击同一个按钮会导致脚本被多次注入。这不会对浏览器性能产生太大影响,因为浏览器将加载的脚本缓存在某处,但为了防止这种情况,最好为每个脚本实现某种唯一标识符,并在注入之前检查它。有很多方法可以做到这一点,这只是一种方法:

var h_btn = document.getElementById("home_btn");
var a_btn = document.getElementById("about_btn");
var head = document.getElementsByTagName("head")[0];
h_btn.addEventListener("click", showHome);
a_btn.addEventListener("click", showAbout);

// Store scripts that you've injected
var scripts = [];

function insertScript(src) {
  // If we have previously attempted injection, then do nothing
  if (scripts.indexOf(src) !== -1) {
    return;
  }
  
    var js = document.createElement("script");
    js.type = "module";
    js.src = src;
    head.appendChild(js);
  
  // Push script to array
  scripts.push(src);
}

function showHome() {
    insertScript('./j1.js');
}

function showAbout() {
    insertScript('./j2.js');
}

另类独特的脚本注入策略和想法:

  • 使用ES6 Map() 跟踪被注入的独特脚本源
  • 可能只在脚本成功加载后将src 存储到array/dict/map

【讨论】:

  • 您提供的解决方案有效但不完全。因为当我单击主页按钮然后单击关于按钮时它工作正常,但是当我再次单击主页按钮时它不起作用。我面临的另一个问题是 j1.js 和 j2.js 混淆了。当我在它们之间切换时。所以我认为在检查 src 之前是否存在之后,在条件内的 insert 函数中,我会从 dom 中删除 script[0] 元素,然后 append 。但这不起作用。你能帮我解决这个问题吗
  • 其实我想要的是能够在这些j1和j2文件之间切换。一旦我点击 j1 并且不希望以前的 js 文件存在。
  • 你可以切换,但你知道你不能“撤消”第一个加载的脚本所做的事情,对吧?它不会仅仅因为您删除脚本标签而神奇地删除所有事件侦听器等。
  • 那么你知道有什么方法可以删除之前的所有内容,以便新加载的js文件可以从干净的网页开始
  • 唯一的办法就是重新加载页面。
【解决方案2】:

您必须创建两次元素,因为只有一个元素具有 1 个 src。

var h_btn = document.getElementById("home_btn");
var a_btn = document.getElementById("about_btn");
var js1 = document.createElement("script");
var js2 = document.createElement("script");


h_btn.addEventListener("click", showHome);
a_btn.addEventListener("click", showAbout);

function showHome() {

  js1.src = "j1.js";
  document.body.appendChild(js1);
}

function showAbout() {
  js2.src = "j2.js";
  document.body.appendChild(js2);
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Experiment</title>
  <link href="s.css" rel="stylesheet" type="text/css">

</head>

<body>
  <div class="navigation">
    <nav>
      <ul>
        <li id="home_btn"> Home</li>
        <li id="about_btn"> About </li>
      </ul>
    </nav>
  </div>

  <canvas id="myCanvas">

    </canvas>

  <div class="notePane">
    <p> This is just a bunch of text not explanation</p>
  </div>

</body>
<script src="exp.js" type="text/javascript"></script>

</html>

【讨论】:

  • 代码不起作用,因为堆栈溢出时文件不存在,但它应该在您的代码中工作。
  • 当我单击主页按钮然后单击关于按钮时,您的代码有效。但是当我想切换回家时它不起作用。
  • 我稍微编辑了代码。现在试试看。如果还是不行,可能是js文件有问题。
  • 还要确保这个脚本放置在正文的末尾,或者添加一个加载到脚本中的 dom 内容,以确保它在整个文档加载时正确触发。
猜你喜欢
  • 2020-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多