Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switching from Structure to Content mode loads scripts in wrong order #6671

Open
wkleinheerenbrink opened this issue Apr 18, 2019 · 2 comments

Comments

Projects
None yet
2 participants
@wkleinheerenbrink
Copy link

commented Apr 18, 2019

Summary

If you open a page in Structure mode, Django CMS will load the 'structure HTML' first and use an XHR call to fetch the actual contents of the page. When you close the Structure mode the page source is replaced with the actual contents of the paged, fetched earlier with the XHR call. All scripts on the page are loaded, but in sync instead of in order of appearance. This will cause libraries not being loaded in time.

Expected behaviour

The script-tags should be loaded / executed in order of appearance. E.g. If you have a jQuery script-tag and a jQuery plugin tag, its important to load jQuery before the plugin otherwise you'll get a jQuery is not defined.

Actual behaviour

The script-tags are loaded synchronously and executed once loaded, but not in order of appearance. E.g. the jQuery plugin can't find jQuery.

Environment

  • Python version: All (Javascript issue)
  • Django version: All (Javascript issue)
  • django CMS version: 3.5.x and 3.6.x
@wkleinheerenbrink

This comment has been minimized.

Copy link
Author

commented Apr 18, 2019

The issue starts here: https://github.com/divio/django-cms/blob/develop/cms/static/cms/js/modules/cms.structureboard.js#L525

The contents of the body is appended by the _loadContent() method, using bodyElement.append(body);. This will call the [append()](https://api.jquery.com/append/) method from jQuery. This method will load all scripts at once and execute them directly after loading them, so not in order of apearence.

Short description: https://stackoverflow.com/a/21487979/522248
Long description: https://www.w3.org/TR/html5/semantics-scripting.html#script-processing-model

I'm not sure what would be the correct fix for this issue. The jQuery append() behaviour is correct (following W3C), but the behaviour in Django CMS is not (the result starting in 'content' mode is different from the result from starting in 'structure' mode.).

Once option would be to dynamically replace all script tags with a new tag that add a async = true attribute, like suggested here: https://stackoverflow.com/a/8578840/522248 (the Google/Facebook method).

Pro: it works like charm
Con: it might be slower due to 'async' loading which is not the same as executing in order (not sure) and you might alter behaviour of some other scripts

Another option would be to roll back to the old behaviour where the original page is simply loaded and the StructureBoard is loaded via XHR.

@SachaMPS

This comment has been minimized.

Copy link
Contributor

commented May 13, 2019

Maybe this might be a help: Personally I think it might be a plan to use a bundler for all your js code and init them with a single function. In our case we lost all references because of the DOM manipulations done after saving a plugin.

This pattern works for us:

A simplified example of that from our code... Since we write everything in React and don't use jQuery it wont work out of the box but maybe its a help?!:

import domready from 'domready';

const req = require.context('./', true, /\.jsx$/);

const components =[{
    name: 'react--calculator',
    module: 'modules/calculator/calculatorPreview.jsx'
}]

const loadComponents = () => {
    components.forEach(component => {
        let ReactElement;
        const nodes = document.querySelectorAll(`.${component.name}`);
        if (nodes.length > 0) {
            ReactElement = req('./' + component.module);
        }
        ReactDOM.render(React.createElement(ReactElement, {}), node);
    }
}
domready(loadComponents);

//
// Listen to DOM Mutations which happen on every cms plugin save...
domready(() => {
        ((CMS || {}).$ || {})(window).on('cms-content-refresh', function() {
                loadComponents();
        });
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.