Dynamically Injecting and Executing Inline Scripts with window.onload in JavaScript


I faced an issue where dynamically injected inline scripts with window.onload functions were not executing. Here's how I solved it.

I needed to dynamically inject and execute JavaScript scripts in Head Tag with window.onload functions from an API response. The challenge was to ensure that the window.onload functions were captured and executed correctly.

Step 1: Fetch the scripts from Database using API

$.ajax({
    url: 'your-api-endpoint',
    type: 'GET',
    success: function(data) {
        // logic goes here
    },
    error: function(xhr, status, error) {
        console.error('AJAX error:', status, error);
    }
});

Step 2: Parse the Scripts, Links, and Styles

I created a temporary container to parse the HTML content and extract script, link, and style elements.

const tempContainer = document.createElement('div');
tempContainer.innerHTML = data.project.scripts;
const scriptsLinksAndStyles = tempContainer.querySelectorAll('script, link, style');

Step 3: Capture window.onload Functions

I temporarily overrode window.onload to capture any assigned functions.

let capturedOnLoad = null;
const originalOnLoad = window.onload;

Object.defineProperty(window, 'onload', {
    set: function(func) {
        capturedOnLoad = func;
    },
    configurable: true
});

Step 4: Append the Elements to the Document Head

I appended the script, link, and style elements to the document head. For inline scripts, I captured the window.onload function.

scriptsLinksAndStyles.forEach(element => {
    // Check if the element is a <script> tag
    if (element.tagName === 'SCRIPT') {
        const scriptElement = document.createElement('script');

        // If the <script> tag has a 'src' attribute (external script)
        if (element.src) {
            scriptElement.src = element.src;
            scriptElement.async = false; // Ensure scripts load in the correct order
            document.head.appendChild(scriptElement); // Append the script to the <head>
        } else {
            // If the <script> tag is an inline script
            scriptElement.textContent = element.textContent;
            document.head.appendChild(scriptElement); // Append the script to the <head>
        }
    } 
    // Check if the element is a <link> or <style> tag
    else if (element.tagName === 'LINK' || element.tagName === 'STYLE') {
        document.head.appendChild(element.cloneNode(true)); // Clone and append the element to the <head>
    }
});

scriptsLinksAndStyles.forEach(element => { ... }); loops through each element in the scriptsLinksAndStyles collection.

const scriptElement = document.createElement('script'); creates a new <script> element.

document.head.appendChild(scriptElement); appends the new <script> element to the <head>.

Step 5: Restore and Execute window.onload

I restored the original window.onload and executed the captured function with a delay to ensure proper execution timing.

Object.defineProperty(window, 'onload', {
    value: originalOnLoad,
    writable: true,
    configurable: true
});

if (capturedOnLoad) {
    capturedOnLoad();
}

By following these steps, I successfully injected and executed inline JavaScript scripts with window.onload functions from an API response, ensuring proper script execution.

This approach can be used to handle similar dynamic script injection scenarios effectively.