ShadyCSS polyfill not properly handling CSS in Edge
Asked Answered
C

1

8

I am building a widget for third-party websites, using shadow DOM to prevent their CSS from interfering with ours. I am using the ShadyDOM and ShadyCSS polyfills to make it work in Edge and IE, but it is not transforming the CSS for the shadow DOM as I would expect.

Example:

<!DOCTYPE html>
<html>
	<head>
		<title>Shadow DOM test</title>
	</head>
	<body>
		<div id="container">container is here</div>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/2.3.0/webcomponents-bundle.js"></script>
		<script>
			const shadow = document.getElementById("container").attachShadow({ mode: "open" });
			const style = document.createElement("style");
			style.innerHTML = `
				:host .stuff {
					background: #ff00ff;
				}
			`;
			shadow.appendChild(style);
			const div = document.createElement("div");
			div.classList.add("stuff");
			div.innerHTML = "stuff inside shadow dom";
			shadow.appendChild(div);
		</script>
	</body>
</html>

In Chrome (which supports shadow DOM natively), the stuff div has a pink background, as I would expect. But in Edge (which does not support shadow DOM natively), I see the "stuff inside shadow dom" text (meaning my script ran and the ShadyDOM functions worked), but I don't see the pink background.

Why is this happening? I am attaching a shadow root to a plain old div, instead of using custom elements as the example in the ShadyCSS README does, but does that matter? If so, how can I make this work? I am working on a big, existing app, and not wanting to make too many changes at once, so I would strongly prefer to use the standard HTML elements I am already using (divs, buttons, etc.) instead of coming up with my own elements or templates, although I would be willing to consider templates and/or custom elements if it can be done easily, without having to make a lot of big changes.

Calumny answered 30/9, 2019 at 20:0 Comment(8)
I have tried several things, like appending the style element directly to the inner div instead of the shadow root, appending the style element after appending the inner div, and setting the innerHTML of the style element after appending it. But none of those things helped.Calumny
If I remove the :host from the CSS selector, then the styles get applied. But they also get applied to things outside the shadow root, which I don't want.Calumny
I also tried stuff like ShadyCSS.styleElement(element) and ShadyCSS. styleSubtree(element) and they didn't work.Calumny
I was able to get the polyfill to style a custom element made with a template, but I would like to avoid doing that because doing that in my app would require changing and rearchitecting a bunch of things.Calumny
In case this is useful to anyone: my app uses Preact. I am using a higher-order component to isolate our CSS from third-party CSS (which would otherwise interfere with our CSS in many ways). It works by putting our stuff in iframes, and doing some tricks to put style tags in the right place and adjust the dimensions appropriately. It is slow at rendering and cumbersome to deal with, so I changed it to use shadow DOM. Now, it works beautifully in all non-Microsoft browsers. I am trying to make it work in Edge and IE using the polyfill, but it is not working.Calumny
I tried using only the @webcomponents/shadydom polyfill, and also @webcomponents/shadydom with @webcomponents/shadycss, and that didn't seem to do anything helpful.Calumny
GitHub issue for my problem: github.com/webcomponents/polyfills/issues/213Calumny
I decided to use my own solution to just override the offending CSS styles and hope it is good enough for my purposes. I (and the other people I work with) don't think it is worth spending more time and adding a lot of complexity just to support a few users. To be fair to @Supersharp and anyone else who may answer this question, I will try the answers I get on my toy example, in my own time, upvote whichever ones seem helpful, and offer the bounty to whatever I think is the best answer.Calumny
H
7

With ShadyCSS

:host CSS pseudo-element is not known in Edge.

To make it work, you should use ShadyCSS.prepareTemplate() that will replace :host by the name of the custom element and define the style as a global style that will apply to all the page.

Remember that there's no Shadow DOM in Edge: there's no boundaries/scope for CSS with a fake/polyfilled Shadow DOM.

In your case you could use ShadyCSS.prepareTemplate( yourTemplate, 'div' ) as in the example below:

ShadyCSS.prepareTemplate( tpl, 'div' )
container.attachShadow( { mode: "open" } )
         .appendChild( tpl.content.cloneNode(true) )
<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/2.3.0/webcomponents-bundle.js"></script>

<template id=tpl>
    <style>
    :host .stuff {
       background: #ff00ff;
     }
     </style>
    <div class=stuff>stuff inside shadow dom</div>
</template>

<div id=container>container is here</div>

Note: since the polyfill will replace :host by div and add it as a global style, you could observe some side effects if you have another HTML code part that matches div .stuff.


Without ShadyCSS

ShadyCSS was designed for Custom Elements, but not really for standard elements. However, you should get inspiration from the polyfill and create explicitely the styles properties for fake (polyfilled) Shadow DOM. In your case replace :host with div#containter:

container.attachShadow( { mode: "open" } )
         .appendChild( tpl.content.cloneNode(true) )
<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/2.3.0/webcomponents-bundle.js"></script>

<template id=tpl>
    <style>
    div#container .stuff { 
       background: #ff00ff;
     }    
    :host .stuff {
       background: #ff00ff;
     }
     </style>
    <div class=stuff>stuff inside shadow dom</div>
</template>

<div id=container>container is here</div>
Hustle answered 2/10, 2019 at 22:49 Comment(6)
Thanks for the response. I am busy trying my own ideas. I will try yours, and consider accepting or upvoting your answer, when I get a chance, which will probably be some time tomorrow.Calumny
That looks good, but can any of that template stuff be (easily and realistically) made to work in a Preact (or React) app that is constantly trying to change the contents of the divs (at various times, for various reasons)? I would need to make it somehow mutate the shadow DOM at the right time, right?Calumny
@EliasZamaria sorry I'm not a specialist of React but I know some use it with Shadow DOM (ie github.com/Wildhoney/ReactShadow). Depending on your use case maybe you'll need to use MutationObserver to detect changes on the DOM.Hustle
@Hustle thanks for your answer. Mutation observers look cool, but I don't think it is worth the effort to use them for my use case (emulating shadow DOM well enough for the 8% or so of our users using Edge and IE).Calumny
Here's a really thorough article on the Web Components polyfills for anyone that's interested. dev.to/bennypowers/…Phenix
I tried your ideas and they seemed to work (although they may not be practical for my use case). Thank you.Calumny

© 2022 - 2024 — McMap. All rights reserved.