ReedyBear's Blog

Force dynamically loaded Javascript scripts to execute in order

When you dynamically add multiple <script> tags to an HTML document, the scripts load asynchronously. So if your second script depends on an earlier script, things might just not work. I tried using defer on the script tags but this was entirely unsuccessful.

My approach adds the <script> tags one at a time, and uses the load event to add the next script url in the list. My solution also allows you to add data-{key}={value} attributes to the final script node, so you can use the currentScript property for settings.

Note: I am adding the script tags to the end of the <body>. You may wish to edit the first line of my function if you wish for scripts to be added elsewhere.

Usage:

const scripts = [  
    'https://example.org/dependency1.js',  
    'https://example.org/dependency2.js',  
    'https://example.org/generate-table-of-contents.js'  
];  
const data_attributes = {  
    title: "Page Contents:",  
    button_text: "Generate Table of Contents"  
};  
load_scripts_in_order(scripts, data_attributes)  

Load Scripts In Order:

/**  
 * Create a `<script>` node, setting an onload event to execute the next script in the list.  
 *  
 * @param scripts_array an array of javascript URLs with one or more entries  
 * @param final_script_settings key=>value array of `data-{key}={value}` settings to apply to only the last node in the array  
 */  
function load_scripts_in_order(scripts_array, final_script_settings){  
    const body = document.querySelector('body');  
    const script = document.createElement('script');  
    script.setAttribute('type', "text/javascript");  
    script.setAttribute('src', scripts_array[0]);  
    if (scripts_array.length > 1){  
        script.addEventListener('load',  
            load_scripts_in_order.bind(this, scripts_array.slice(1),  
                final_script_settings  
            )  
        );  

    }  
    if (scripts_array.length == 1){  
        for (const setting in final_script_settings){  
            script.setAttribute('data-'+setting, final_script_settings[setting]);  
        }  
    }  

    body.appendChild(script);  
}  

#code