Search code examples
javascripthtmlxmlhttprequesteval

Load external JavaScript synchronously


I have to add a script tag via some JavaScript and have it execute in full as the later statements rely on it.

var logIt = (function() {
  var i = 0,
    logging = document.getElementById('logging');
  return function(s) {
    var heading = document.createElement('p');
    heading.innerText = `${++i}:  ${s}`;
    logging.appendChild(heading);
  }
}());

logIt('starting');
var newScriptTag = document.createElement('script');
newScriptTag.src = 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.js';
newScriptTag.type = 'text/javascript';
document.head.appendChild(newScriptTag);
try {
  var now = moment().format('HH:mm');
  logIt(`moment loaded.  The time is ${now}`);
} catch (e) {
  logIt(`Moment not loaded: ${e}`);
}
<html>

<head>
  <title>Injecting Script Tags</title>
</head>

<body>
  <h1>Injecting Script Tags</h1>
  <div id="logging"></div>
</body>

</html>

As the snippet above shows, the moment() isn't available on the statement after the tag insertion.

I think it could be done with eval(...), but that option isn't popular.


Solution

  • Use the onload event listener on the <script> tag:

    const URL = 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.js';
    
    const onMomentReady = () => {
      console.log('Moment.js is loaded!');
      console.log(typeof moment);
    }
    
    var newScriptTag = document.createElement('script');
    newScriptTag.onload = onMomentReady;
    newScriptTag.src = URL;
    // newScriptTag.type = 'text/javascript'; // no need for this
    
    // optional
    newScriptTag.async = true;
    document.head.appendChild(newScriptTag);
    

    Note that I added the onload handler before setting the src attribute. If you set the src first, the handler function might not fire.