Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
104 views
in Technique[技术] by (71.8m points)

How to load javascript files using event listeners

This question is not duplicate of

Conditionally load JavaScript file

and nor this

How to include an external javascript file conditionally?

I have gone through them, they are kind of similar to what I want to do but not exactly same. Here is how, in the above question the user just wants to load the script file once based on a condition. But in my case I want to load different js files based on click events.

So here in my case I have an HTML document:

<!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>

and this h.html file is linked to an exp.js file. Now in the exp.js file :

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);
}

So things work fine when I click the h_btn on the web page. It loads j1.js. But then when I click on the a_btn on the web page I expect to see j2.js linked but I don't see it. I have to refresh the page and then click on a_btn to see j2.js linked. How do I link j1.js and j2.js such that I don't have to refresh the page again and again to load the correct script.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Update: OP has updated the question requirements such that he wants to "unload" a JS file when another is clicked. There is no way to undo all the runtime logic once a JS file is loaded: the only way is to reload the page. Removing the <script> tag or changing the src attribute will not magically unbind event listeners or "undeclare" variables.

Therefore, if OP wants to "start anew", the only way is to check if a custom script has been loaded before: if it has, we force reload the page. There are of course many ways to "inform" the next page which source to load, if available: in the example below, we use query strings:

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');
}

This is because of how Node.appendChild() works. The first click works because you're creating a new element and inserting it into your document. However, the second click will not work as you've expected because the node already exists:

The Node.appendChild() method adds a node to the end of the list of children of a specified parent node. If the given child is a reference to an existing node in the document, appendChild() moves it from its current position to the new position

This means that the second click will only mutate the src attribute of the already-injected <script> element instead of creating a new one, and that also means that the second script src will not be loaded.

A solution will be to use a function that will create a script tag every single time:

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');
}

But this will also mean that multiple clicks on the same button will cause the script to be injected multiple times. This does not affect browser performance much since the browser has the loaded script cached somewhere, but to guard against this, it might be a good idea to implement some kind of unique identifier per script, and check against that before injection. There are many ways to do this, and this is just one way:

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');
}

Alternative unique script injection strategies and ideas:

  • Use ES6 Map() to track unique script sources being injected
  • Perhaps only store src to array/dict/map when the script has successfully loaded

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...