首页 > 解决方案 > 如何使用事件监听器加载 javascript 文件

问题描述

这个问题不是重复的

有条件地加载 JavaScript 文件

也不是这个

如何有条件地包含外部 javascript 文件?

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

解决方案


更新:OP 更新了问题要求,以便在单击另一个 JS 文件时“卸载”一个 JS 文件。一旦加载了 JS 文件,就无法撤消所有运行时逻辑:唯一的方法是重新加载页面。删除<script>标签或更改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()则将其从当前位置移动到新位置

这意味着第二次点击只会改变src已经注入<script>元素的属性,而不是创建一个新的,这也意味着第二个脚本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');
}

替代独特的脚本注入策略和想法:

  • 使用ES6Map()跟踪被注入的独特脚本源
  • src脚本成功加载后,可能只存储到数组/dict/map

推荐阅读