Closed nxtwrld closed 1 year ago
Hi @nxtwrld , yes this would be really useful and important for me too. My use-case is am trying to use custom component for encapsulating some complex components, but the styles and class names need to use external CSS classes (for theming across different apps, like Bootstrap). I can't embed styles in the component. Use of shadow DOM means I can't use svelte compiler for this.
I am trying to tweak the svelte compiler and add a new option
customElement : true,
shadowDom: false // defaults to true
I will post more info when I am done and it will pass or the tests.
Hey @nxtwrld did you finish your implementation of the shadowDom option? I can pick this up if needed. I can't move forward with Svelte without this, for the reasons stated and also because we have to support IE 11 which has no support for Shadow DOM
I am trying to tweak the svelte compiler and add a new option
customElement : true, shadowDom: false // defaults to true
By the way, perhaps the API should instead be:
shadowDom: 'open' | 'closed' | 'none'
I understand for backward compatibility it might be best to make "open" the default, but really I think a more natural default would be "none". Since this would go into a new major version, perhaps we could consider adding this breaking change also?
Just wanna chime in, this is a good idea and there are numerous reasons to want it, especially if you can still use Svelte's own support for the <slot>
functionality that is (I presume) syntactical rather than relying on real Shadow DOM.
For my use cases, this functionality is needed to work around existing limitations in and issues with Shadow DOM so that Web Components can be effectively used throughout the transitional period while incompatibilities decrease, the platform fills in the gaps, etc.
The issues I've encountered or heard of that need workarounds include:
In any case where a component's interface would require slots, but Shadow DOM in its current incarnation has issues that make it not viable to use, you're between a rock and hard place. Either you get no slots, or you get no form participation, accessibility, or styles (perhaps at all, perhaps only with major expense). This can lead to no Web Components.
In the long term, my hope and assumption is that these issues will be fixed by increased support, new specs, or evolutions to the current specs and that eventually using Shadow DOM will have none of these significant downsides.
I ran into this same issue with a component I was building for one of our experimental projects. I went ahead and started a PR to add the the option to disable shadow DOM. I'm not super familiar with Svelte internals at this point (or WCs generally), but we'll see where we get.
Is there any progress?
Are you still working on this?
Hi there, I've opened this PR https://github.com/sveltejs/svelte/pull/4073. I was wondering if any of the maintainers can give me some feedback on the approach/whether this is something Svelte wants?
Hi there, problem is, when you need to create classic html form (not submited via ajax) with form element created custom element, then you need light dom. Custom element cannot contain shadowed html forms.
I think that this is most important complexity feature comparing to Stencil. StencilJS is perfect for large projects. But svelte is svelte...
Please merge...
Use of the Light DOM is a legit use case and I want to add my voice to the number of people for whom this blocker eliminates svelte from consideration. I hope it can be resolved/merged soon.
Wanted to make a small authentication component with the help of svelte. Got stuck after finding out that google reCaptcha cannot work in shadow dom. Is there any other way out? When can we expect making custom components without having shadow dom?
I don't think we need something very complex here. We just have to consider each component as a new Svelte app. I guess the store is available too, I didn't test so far.
This is the "connect" utility I use to render a Svelte component into a CustomElement (without shadowDom):
/**
* Connect Web Component attributes to Svelte Component properties
* @param {string} name Name of the Web Component
* @param {*} Component Svelte Component
* @param {string[]} attributes Which attributes will be passed as properties
*/
export default function connect(name, Component, attributes = []) {
return customElements.define(name, class extends HTMLElement {
constructor() {
super();
this.component = undefined;
}
static get observedAttributes() {
return attributes;
}
attributeChangedCallback(name, oldValue, newValue) {
if (this.component && oldValue !== newValue) {
this.component.$set({ [name]: newValue });
}
}
connectedCallback() {
let props = {};
for (const attr of attributes) {
props[attr] = this.getAttribute(attr) || undefined;
}
this.component = new Component({
target: this,
props,
});
}
});
}
I also cobbled together something similar in the meantime, though in addition I pass in an option of shadow true/false and if true also embed a link to the svelte's compiled stylesheet.
I think the svelte maintainers should decide if they really want to compile to web-components with all their complexity, or just promote using wrappers like these. What would help here, and with integration with other frameworks would be to make top level slots accessible outside of shadow dom. I realise they'd need to be "simulated", but grabbing the content of the mounted dom node and parsing it for named slots should be feasible, though admittedly not ideal.
I have two pull requests dealing with these two issues, was hoping for some feedback, even if just to say no thanks. The slots pull request is still a WIP.
Hey @cedeber, can you provide a small example on how to integrate your connector?
Hi @rac0316 Here is my Svelte boileplate: https://github.com/cedeber/eukolia/tree/master/boilerplates/rollup-svelte I modified it to integrate the connector. In this commit, you can see the needed changes: https://github.com/cedeber/eukolia/commit/1e1742ddca7fac58f02760aa11b4b3c61a560283
This may be a little "crude" but it seems to work for light dom slots:
customElements.define('element-details',
class extends HTMLElement {
constructor() {
super();
const template = document.getElementById('element-details-template').content;
// render into light dom
this.appendChild(template.cloneNode(true));
// grab all slots
const slots = this.querySelectorAll('slot');
slots.forEach(slot => {
// replace slots with their element counterpart
const el = this.querySelector(`[slot="${slot.name}"]`)
if (!el) slot.parentNode.removeChild(slot)
slot.parentNode.replaceChild(el, slot)
});
}
}
);
Adapted from the demo at https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots
I got this which also supports slots. The only restriction is that the main slot must be wrapped in a tag (eg. <custom-button><span>{label}</span></custom-button>
) if something may change within the slot.. because it changes the context of the main slot elements which svelte uses when updating.
export default class KinElement extends HTMLElement {
constructor() {
super();
this.elTpl; // elementTemplate
this.initialized = false;
this.initTimeoutId = null;
}
connectedCallback() {
this.initTimeoutId = window.setTimeout(this.init.bind(this), 0);
}
disconnectedCallback() {
this.initTimeoutId = window.clearTimeout(this.initTimeoutId);
}
attributeChangedCallback() {
if (this.initialized) {
// may be defined by a child class
this.updateElem && this.updateElem();
}
}
init() {
if (this.initialized) {
return;
}
// must be defined by a child class (e.g. Button) and create this.elTpl
this.initElem();
this.initSlots();
this.appendChild(this.elTpl);
this.initialized = true;
}
initSlots() {
const mainSlot = this.elTpl.querySelector('slot:not([name])');
const namedSlots = this.elTpl.querySelectorAll('slot[name]');
const namedSlotsMap = {};
const mainSlotNodes = [];
const namedSlotNodes = [];
namedSlots.forEach(slot => {
namedSlotsMap[slot.name] = slot;
});
this.childNodes.forEach(child => {
if (child.slot) {
namedSlotNodes.push(child);
} else {
mainSlotNodes.push(child);
}
});
if (mainSlot) {
mainSlotNodes.forEach((node, index)=>{
node = this.removeChild(node);
if (index) {
mainSlotNodes[index - 1].after(node);
} else {
mainSlot.replaceWith(node);
}
});
}
namedSlotNodes.forEach(node => {
const slot = namedSlotsMap[node.slot];
node = this.removeChild(node);
if (slot) {
slot.replaceWith(node);
}
});
}
}
Wanted to make a small authentication component with the help of svelte. Got stuck after finding out that google reCaptcha cannot work in shadow dom. Is there any other way out? When can we expect making custom components without having shadow dom?
I am also stuck with same issue trying develop a custom element with google reCaptcha
Wanted to make a small authentication component with the help of svelte. Got stuck after finding out that google reCaptcha cannot work in shadow dom. Is there any other way out? When can we expect making custom components without having shadow dom?
Did you got any solution for this i am also stuck with this..
@varun-etc I didn't find any way with having it as a custom component. But I solved my problem by not making it as a custom component, in that way there is no shadow dom and it's as good as any other app (I have to be carefull with my class names though, so as to not override styles and also not leak them to the host app).
any news on this ?
Finally i was able to achieve this in svelte you can refer build file here..
build file here..
please share your source files
source files github link
I created a wrapper to solve this issue a while ago. Been using it internally for a few months and just quickly uploaded something to npm / github.
It supports default and named slots, attributes, shadow dom, light dom, and embedding css.
Hopefully someone else finds it useful.
S
Lots of accessibility tools like Jaws do have very very very poor support for shadow dom. Like a combobox
with haspopup=listbox
doesn't work at all in shadow dom with Jaws. @Rich-Harris to be able to use svelte to built UI component libraries we desperately need custom elements without shadow dom. This topic hasn't been addressed for a long time. It stopped quite a few enterprises I work for to adopt svelte because of WAI compliance.
Please have another look into this. Thanks
would love to see this. are there are other technologies out there that do something similar?
Another use case I just encountered: rendering a custom element SVG filter. That filter can't be addressed by the rest of the page while in shadow (even if it's open). It must be rendered into light dom.
Just getting started with Svelte and SvelteKit and really liking it so far! One of few attractions to Svelte was support for web components. In one of our use cases, all worked well, and the ShadowDom worked as expected. In another case.. well, not so much. We have now probably spent more time fighting this then it would have taken to just create a plan web component and just NOT added the shadow dom. My issue resembles others discussed in this thread.
Anyway, an idea occurred to me. If the
Hi,
I would like to get your thoughts on having a switch for customElement behaviour.
Current custom elements are based on shadow DOM which can be really handy in some cases.
But in our project we heavily depend on cascading - components style depends on its context. (e.g. Button colors are different for different types of containers)
It would be handy, if we could choose between using shadow DOM and a simpler version without it. In the similar way we attach slots in connectedCallback, we could import the component itself into the the main document.
Here is a very crude example of the custom element without shadowDOM:
Would someone else also find this useful?