6

I'm a bit confused on what's required to dynamically load a JS file into the DOM.

When I include in my HTML file, example.js will run normally.

When I include it will add to the DOM but not run it.

I previously believed that I had to recreate , then append() it to the tag. I feel as if I am missing a crucial step, I just don't know what that step is.

example.html

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <script src="example.js"></script><!-- working -->
    <script src="add-example-dynamically.js"></script><!-- not working -->
</head>
<body>
    <script>
        execute( anyScriptElement ); // not working
    </script>
</body>
</html>
</body>
</html>

add-example-dynamically.js

function toExecutable( tagElement ){
    // Duplicate the provided tag as a new element in order for all tags to run the 'src' attribute after adding it to the DOM
    // Required to run: <script src=""></script>
    var newTag = document.createElement( tagElement.tagName );

    if( tagElement.hasAttributes() ){
        // Check if the tag has attributes
        for( var countAttributes = 0; countAttributes < tagElement.attributes.length; ++countAttributes ){
            var name = tagElement.attributes[ countAttributes ].name;
            var value = tagElement.attributes[ countAttributes ].value;
            newTag.setAttribute( name, value );
        }
    }
    if( tagElement.textContent ){
        // Check if the tag has content within it
        newTag.textContent = tagElement.textContent;
    }
    return newTag;
}
function execute( anyScriptElement ){
    var tag = toExecutable( anyScriptElement );
    document.getElementsByTagName( 'head' )[ 0 ].append( tag );
}
var theScript = document.createElement( 'script' );
theScript.src = 'example.js';
execute( theScript ); // not working

Things I've tried (or a variation of)

error loading javascript files dynamically

I've also been adding .onload and .onreadystatechange to various objects without success.

Things I don't quite yet understand

Dynamically load a JavaScript file

How do you import multiple javascript files in HTML index file without the bloat?

Dynamically load a JavaScript file

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

https://cleverbeagle.com/blog/articles/tutorial-how-to-load-third-party-scripts-dynamically-in-javascript

https://humanwhocodes.com/blog/2009/07/28/the-best-way-to-load-external-javascript/

Things I don't think solve my problem

http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml

https://gomakethings.com/a-better-way-to-load-scripts-with-javascript-or-why-document-write-sucks/

Thoughts

I have a feeling that the right solution doesn't involve XMLHttpRequest or Promises but I'm not certain.

My repository in question: Widgets

If someone could point me in the right direction, that would help me figure out what I need to look into.

FYI

Native JS ideal, not interested in JQuery.

I only care about support for Chrome, Firefox, Opera, Safari (desktop / mobile)

Josh Balsillie
  • 119
  • 1
  • 10
  • How does the dynamic script tag get inserted into the DOM? If you do it via `appendChild`, there shouldn't be any need for anything else, it should execute – CertainPerformance Dec 28 '19 at 05:43
  • So i get the script tags from an HTML file using XMLHttpRequest. once the request status === 200 and readyState === 4 I use my toExecutable function on each tag before appending them to the tag in example.html. – Josh Balsillie Dec 28 '19 at 05:49
  • This is the reason I don't declare anyScriptElement in my example, as that part seems to be working for the most part. I just cannot figure out why it adds the script containing the JS source to the DOM but does not execute the JS file. – Josh Balsillie Dec 28 '19 at 05:55
  • Once you have the response text you want to execute as `responseText`, just do `document.body.appendChild(document.createElement('script')).textContent = responseText`, I'd think that should work – CertainPerformance Dec 28 '19 at 05:58
  • Tried the following (htmlCollection is a variable containing the head object as an html collection): (1) domHead.textContent = theRequest.responseText; // adds html as quoted "string" (2) domHead.textContent = htmlCollection.innerHTML; // adds html as quoted "string" (3) domHead.textContent = htmlCollection.innerHTML.replace('"',''); // adds html as quoted "string" – Josh Balsillie Dec 28 '19 at 06:30
  • I added a short script to the **add-example-dynamically.js** example that adds `` to the DOM tag, but does not run. Modified execute() as the head reference needed to change to work in this scenario. – Josh Balsillie Dec 28 '19 at 18:13
  • so I partially solved the issue by identifying that **example.html** > `execute( anyScriptElement );` was trying to execute before `` loaded. Because I'm using `XMLHttpRequest();` to read external files, I tried setting `xhr.open("GET", "content.html", true);` to **false** which solved the issue with the order that content loaded into **example.html**. Synchronous XMLHttpRequest is deprecated however in various browsers (at least the way I'm using it). – Josh Balsillie Dec 29 '19 at 14:57

1 Answers1

0

So I found the issue was with the order that I was resolving code. It took forever to find because there was nothing inherently wrong with my code, but the sequence was wrong.

I was calling everything in the correct order, but the order that things were resolving in my network panel were incorrect.

Once I fixed the sequence that things were being loaded into the DOM, everything worked as expected.

Fix #1

Because my XMLHttpReqests should be asynchronous, I put all the calls into a single Javascript file so they would run synchronously.

I needed Javascript files to be loaded in the tag before loading function calls that reference those files.

The function calls I wrapped in window.onload = function(){}.

Basically my final solution was for any <script>code</script> that I was dynamically placing in example.html I would wrap in window.onload = function(){}.

i.e. <script>window.onload = function(){ code }</script>

Fix #2

I was using the onload wrapper window.onload = function(){} in a location that did not make sense. Also it may have been nested within another window.onload function at one point while debugging, which probably didn't help.

Josh Balsillie
  • 119
  • 1
  • 10