WebReflection / uhtml

A micro HTML/SVG render
MIT License
909 stars 37 forks source link

Script tag execution #18

Closed anywhichway closed 4 years ago

anywhichway commented 4 years ago

With lighterhtml, if a script tag is included in the HTML template, it is inserted and executed when rendered. With uhtml it is inserted but not executed. Is this expected behavior?

To test, just include the below as part of a template.

<script>console.log("I was executed")</script>

It is OK if this is expected, I will just use lighterhtml.

WebReflection commented 4 years ago

uhtml is a subset, and it wouldn't bother with this. As lighterhtml lost recently weight, if you need this kind of alchemy, I suggest you to stick with lighterhtml, as uhtml just keeps it simple, no footguns allowed.

anywhichway commented 4 years ago

@WebReflection thanks, not a problem. This tiny hack does it:

const scripts = target.querySelectorAll("script");
for(const script of scripts) {
    const newscript = document.createElement("script");
    newscript.innerText = script.innerText;
    script.parentNode.replaceChild(newscript,script);
}

BTW, in case you are interested, this hack optionally shoves everything into a shadow-dom, supports passing data, does the script eval, supports targeting multiple output locations with targets that are generic CSS selectors, and optionally adds a render function to a target. We use the CURRENTELEMENT global in proxy objects passed in as data. These proxy objects can track every DOM node they are referenced in. A little like reactive custom elements but super light.

const render = (targets,source,{shadow,data,renderable}={}) => {
        const _data = data;
        for(let target of targets) {
            if(shadow) {
                CURRENTELEMENT = target = target.attachShadow({mode:shadow});
            } else {
                CURRENTELEMENT = target;
            }
            if(renderable) {
                target.render = ({data=_data}={}) => {
                    CURRENTELEMENT = target;
                    uhtml.render(target,html.with(source,data));
                    CURRENTELEMENT = null;
                }
            }
            uhtml.render(target,html.with(source,data));
            const scripts = target.querySelectorAll("script");
            for(const script of scripts) {
                const newscript = document.createElement("script");
                newscript.innerText = script.innerText;
                script.parentNode.replaceChild(newscript,script);
            }
        }
        CURRENTELEMENT = null;
    };

Will be looking at your custom element stuff next.

Have a great day!

WebReflection commented 4 years ago

wouldn't that re-force each time every script execution?

anywhichway commented 4 years ago

Only if a re-render is forced, in which case it might be appropriate. In many cases the data used in a template is static for the duration of a session. And, if not, the real trick is sizing the HTML template blocks and not making them too big. Additionally, much of the JavaScript does not do anything at the time the script block is executed other than attach functions to a variety of things. This keeps our scripts separate from the layout/style/content of the HTML and prevents having to define a bunch of components that developers have to be the ones managing. I love components, and although they can create a very stable piece of software, they actually create a pretty fragile development process highly dependent on software developers rather than designers and content people. This is one of the reasons we don't use React + JSX. One might say, then use a CMS ... trouble is most CMS systems are way too high a level.

Make sense?

WebReflection commented 4 years ago

as I have no idea what's the amount of code passing through that, and its complexity, I'd say it makes sense, if that works for you :-)