jquery / jquery

jQuery JavaScript Library
https://jquery.com
MIT License
58.93k stars 20.62k forks source link

Scripts in dynamically set html always asynchronously loaded #5453

Closed PetesBreenCoding closed 3 weeks ago

PetesBreenCoding commented 1 month ago

Description

If you dynamically load HTML to an element using the html method, and that HTML has script tags pointing to JavaScript files on another domain, those scripts get dynamically added to the header by jQuery through the ajaxTransport method. The problem for me is that this dynamically loaded script tag will have async set to true by default. I would like the script to be loaded synchronously. Is there a way to force async to be false? Thanks

dmethvin commented 1 month ago

Can you provide a code example, as the template asks? That helps us understand exactly what you are asking about, and whether this is about the current supported version.

PetesBreenCoding commented 1 month ago

I couldn't see an easy way to do this on CodePen or similar, so pasting the code here. Below I have a HTML document where a HTML "widget" is loaded from elsewhere. In this case, that widget comes from the <template>, but in real life it would probably come from a remote location.

HTML

<!DOCTYPE html>
<html>
    <head>
        <title>jQuery Async test</title>
        <script src="js/jquery-3.7.1.js"></script>
    </head>
    <template id="dynamically-loaded-html">
        <script src="https://dev2.local/js/global.js"></script>
        <script src="https://dev2.local/js/app.js"></script>

        <h2 id="app-header"></h2>
    </template>
    <body>
        <h1>jQuery Async test</h1>

        <div id="widget-container"></div>

        <script>
            const templateHtml = $('#dynamically-loaded-html').html();
            $('#widget-container').html(templateHtml);
        </script>
    </body>
</html>

The remote "widget" HTML contains script tags that load scripts (global.js and app.js) from another domain (in my test I loaded this webpage using https://dev.local, the JS files are on dev2.local). app.js needs global.js to run first.

global.js

const global = { appHeader: "My app header" };

app.js

$('#app-header').text(global.appHeader);

Using the current version of jQuery, this will fail as both scripts in the widget HTML will be loaded asynchronously and global.appHeader probably won't be set by the time app.js runs. They are not marked as async in the HTML, but jQuery will move them to the document header and dynamically load them, which uses async by default.

If I modify jQuery line 10006 (or /src/ajax/script.js line 53) to add async: s.async to the prop method, this works for me as it will load both files synchronously.

I'm not sure if this is a bug or intentional, but I would say there is a case for the dynamically loaded scripts to set the async property based on how it's defined in the HTML (an absence of an async attribute meaning async = false)

timmywil commented 3 weeks ago

Thanks for opening an issue. The real issue here is execution order, which should be fixed by https://github.com/jquery/jquery/issues/5453, so we can let that ticket track this.