1

I need to dynamically load a JS file which exports a variable to the window object (e.g. React or jQuery) and get the exported value without changing the page window object. How can I do it using JavaScript?

It should work like this:

(function () {
    var React = someMagic();
    assertNotEmpty(React);
    assertEmpty(window.React);
})();

What is my end goal: make a script for embedding to other websites. The script performs some actions with the page where it is installed, requires some dependencies, but the dependencies must not interfere with the page dependencies.


Using AMD or Require.js is not suitable because it changes the page scripts behaviour (they stop exporting variables to window) which can break the page.

Using a solution like this:

<script src="jquery.js"></script>
<script>
    (function () {
        var jQuery = jQuery.noConflict();
    })();
</script>

Is also not suitable because it requires changing HTML while I can use only JavaScript.

Joining the script with the dependencies and wrapping it all with (function(){ ... })() is not suitable too.

4
  • I'm confused about what is actually suitable. AMD? no. Require.js? no. IIFE? no. ES6 Imports? idk Commented Mar 30, 2018 at 5:06
  • @JayHarris IIFE is ok, but the dependencies must be loaded from other files Commented Mar 30, 2018 at 5:07
  • stackoverflow.com/a/14521482/2284613 Commented Mar 30, 2018 at 5:09
  • @JayHarris When a script is loaded this way, it exports variables to the window object. If a variable exists (e.g. loaded by the page), the script will overwrite it which can break the page. Commented Mar 30, 2018 at 5:13

1 Answer 1

1

Use Iframe to separate the namespaces. When a script is loaded inside an Iframe, it doesn't change the page namespace. A variable from an Iframe namespace can be exported to the page namespace when required.

// Imagine this is a content of the embedded script

const onMyJqueryLoad = jQuery => {
    console.log('Page jQuery:', window.jQuery.fn.jquery, window.jQuery.ui);
    console.log('My jQuery:', jQuery.fn.jquery, jQuery.ui);
};

const loadScript = (src, callback = () => {}, document = window.document) => {
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.async = true;
    script.onload = script.onerror = () => {
        script.parentNode.removeChild(script);
        callback();
    };
    script.src = src;
    document.body.appendChild(script);
}

// Create an Iframe and do the dirty job inside it without affecting the page
let iframe = document.createElement('iframe');
iframe.style.position = 'absolute';
iframe.style.top = '-1000px';
iframe.style.width = 0;
iframe.style.height = 0;
iframe.onload = () => {
    loadScript(
        'https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js',
        () => loadScript(
            'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.8.24/jquery-ui.min.js',
            () => {
                const jQuery = iframe.contentWindow.jQuery;
                iframe.parentNode.removeChild(iframe);
                iframe = null;
                onMyJqueryLoad(jQuery);
            },
            iframe.contentWindow.document
        ),
        iframe.contentWindow.document
    );
	
    iframe.onload = null;
};
document.body.appendChild(iframe);
<!-- The page -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>

Unfortunately the code doesn't work inside a Stack Overflow snippet because of the snippet sandboxing. You can view a demo on CodePen.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.