Loading Script Files Dynamically

This is a companion discussion topic for the original entry at https://www.kirupa.com/html5/loading_script_files_dynamically.htm
1 Like

Thanks for creating this site! I’m not having good luck with getting answers on Stack Overflow, so I thought I’d try this site. User daniella on Stack Overflow included a link to your site in a post.

I’m trying to get an external js file to play at the same time as an audio file with a click on a simple inline play button that triggers the internal js listed below. The audio plays fine, but not the external script (which creates animated fireworks). I have the same external script playing on a different page but the code in the head is designed to play the animation when the page is opened. I’d like to delay it until the play button is clicked. The lines related to the var ‘script’, in the internal script below, I adapted from the script in the head of the other page. It seemed logical to append the external script to the canvas, but apparently not. I’m using Sigil to create an .epub file.

Thanks in advance for any help.

<img class="playArrow" id="fanfarePlay" alt="play audio" src="../Images/playArrow.png"/>    //custom inline button
<canvas id="canvasFireworks"></canvas>
<audio id="fanfareAudio" src="../Audio/garvey_finale.mp3" type="audio/mp3"></audio>

<script type="text/javascript">  
  var clip = document.getElementById("fanfareAudio");
  var playPause = document.getElementById("fanfarePlay");
  var script = document.createElement('script');
      script.src = '../Misc/fireScript.js';
      script.type = 'text/javascript';

  playPause.addEventListener("click", function() {
    if (clip.paused == true) {
        clip.play();
        playPause.src="../Images/playPause.png";
        document.getElementById('canvasFireworks').appendChild(script);
    } else {
        clip.pause();
        playPause.src="../Images/playArrow.png";
        // removeChild(script) probably goes here.
    }
  });
  clip.onended = function() {
    playPause.src="../Images/playArrow.png";
  };
</script>

Hi @Jgarvey - welcome to the forums :slight_smile:

Do you have a live link to this non-working example? I would like to take a look at the network traffic to see why the external script isn’t running. Also, I am wondering if the Live DOM has the script element properly filled out. The code you have posted looks good to me, so the error must be something subtle.

Thanks for responding so quickly! I threw together a pen (my first) at https://codepen.io/jgarvey/pen/JjYdjMY. I hope I included everything related. The fireworks script is by Andrew Powell-Morse at https://airbrake.io/blog/javascript/fourth-of-july-javascript-fireworks. You’ll see I commented out the parts I didn’t need for my project. I included the css through Chapter 1 in the pen, where the animation is located. That should cover everything in the html fragment, but I may have left something out. The image and audio files aren’t included but I can include whatever you need. Thanks again.

Ok - I think I figured out what is happening. Your code is fine. It is the external fireworks script that is throwing some errors. The first thing I noticed when checking in the console is that the fireworks script is looking for an element named canvas. Your element is named canvasFireworks. Fixing the reference in the external script seems to have solved that problem.

The last part has to do with when the fireworks actually play. Like you mention, the example provided runs on page load. You need it to play when the user interacts with the page. In the very last line of your external script, we have this:

window.onload = loop;

Replace it with just this:

loop();

This ensures the function responsible for kickstarting this animation is called when the script is loaded and not when when the window load event is overheard…which it will never hear since that event would have been fired a long time ago by the time a user will have clicked on your page.

Let me know if these changes solve it for you!

:slight_smile:

Perfect. Thanks. The only problem I have now is stopping the script on pause and onended. I tried document.getElementById(‘canvas’).removeChild(script); under else {, but it doesn’t work. I looked at removeChild on W3schools.com and saw that node or parentNode is needed. If you could let me know how to stop the script it would save me time on this project, but that’s an area where I need to research more. Like at https://www.kirupa.com/html5/traversing_the_dom.htm. (You very funny guy: your wishlist on the snow-effect page.) Would disabling loop() be simpler? Thanks.

I am on my phone, so I don’t have an environment to test, but this should work if you just want to remove the script altogether:

let canvasElement = document.getElementById(“canvas”);
canvasElement.parentNode.removeChild(canvasElement);

I explain how this script works here: https://www.kirupa.com/html5/creating_dom_elements_and_other_stuff.htm

Great! It works. It even works with the onended function. Many thanks.

1 Like

Glad to hear! :partying_face:

A further question regarding the script below. The fireworks script is stopped with the line “canvasElement.parentNode.removeChild(canvasElement);” at the same time as the audio file is stopped, but when clicking on the inline play button again, only the audio file restarts. The canvas itself is removed by the above line, but it seems that it would need to be replaced for the fireworks script to be restarted. It’s not important that the user be able to replay the fireworks along with the audio-- nobody will feel the need to click to replay–but it would seem more logical if it replayed if someone did, and it would be educational for me to learn why “document.getElementById(‘canvas’).removeChild(script);” doesn’t work. I’m guessing that canvas is the child of document and script is the child of canvas, and so I tried “script.parentNode.removeChild(script);” but it didn’t work either. This is no biggie. I’d just like to fill in that gap in my knowledge. Thanks.

<script type="text/javascript">  
  var clip = document.getElementById("fanfareAudio");
  var playPause = document.getElementById("fanfarePlay");
  var script = document.createElement('script');
      script.src = '../Misc/fireScript.js';
      script.type = 'text/javascript';
  let canvasElement = document.getElementById("canvas");

  playPause.addEventListener("click", function() {
    if (clip.paused == true) {
        clip.play();
        playPause.src="../Images/playPause.png";
        document.getElementById('canvas').appendChild(script);
    } else {
        clip.pause();
        clip.currentTime = 0;
        playPause.src="../Images/playArrow.png";
        canvasElement.parentNode.removeChild(canvasElement);
     }
  });
  clip.onended = function() {
    playPause.src="../Images/playArrow.png";
    canvasElement.parentNode.removeChild(canvasElement);
  };
</script>

You are right the canvas needs to be re-added for the fireworks to play again. The solution of removing the script and the canvas is a very heavy-handed approach for stopping the script. For a one-time case, that is fine. For allowing someone to stop and restart, then it gets a bit messy :slightly_smiling_face:

The proper way would be detect what in the fireworks script causes the animation to both start as well as stop. The stopping needs to ensure that any code the fireworks script runs (like the animation loop) is stopped as well. Otherwise, the page may be doing unnecessary work. I haven’t looked at the script in detail to see what that would look like.

Does this help a bit?

:cowboy_hat_face:

It does, a bit. It looks to me that the function loop() at the end consolidates all of the other functions created above it. But I can’t tell what would be left running if that function and the call to the function on the last line of the script were removed, or disabled. It looks like the function launchAutomatedFirework() would continue looping and counting ticks. I’m just guessing but could everything from function Firework(...) down to function updateParticles() continue running in the absence of function loop()?

When I go to another chapter/file in Sigil and then return to that chapter, the script is reset and plays with the audio on click.

Also, because this is an .epub destined to become a .mobi to be viewed on the Kindle, I don’t think cross-browser issues are important like they are for online websites. You mentioned in your article on creating DOM elements that remove isn’t universally supported, but I’m guessing it’s a moot point with the Kindle. Thanks.

Hi Kiupa,
Thanks for your nice blog. It works for me.
But the problem that I’m facing is that when I want to use some object in my external Online Javascript library, NPM run build doesnt compile it.
for example: It works for me in html :

Upload document and then: i put: var uploadDocumentRequest = new DocumentManagement.UploadDocumentRequest(fileContent, documentRecord, overWriteValue, parentEntityReference, relativeLocation);

“new DocumentManagement.UploadDocumentRequest” is a function in my external JS library, but when i put “new DocumentManagement.UploadDocumentRequest” in my React app, it doesn’t compile and give me error:
‘DocumentManagement’ is not defined

Is there any solution that I can say to my react app that have to consult the JS library?

Thanks