-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Proposal: Top-Level <:Body> Injections #1133
Comments
This would be useful for me too, I've just been getting the ref and appending it to the body manually. |
I think a special
As for injected tags that persist after the injector is destroyed - my feeling is that should be done manually in javascript in |
Hmm I was just thinking about the sorts of complications this would cause with SSR. Not sure how to best handle that. |
What are the implications on SSR that are different than |
When adding However, while writing this, it did just occur to me that we could have an extra internal argument or option to the render function that indicates that 'you are not the top-level component' (and so the component knows to return separate 'main body' and 'injected body'), which would not be passed by the consumer to the top-level component (and so that component knows to return the injected body and main body in one string). It's a couple more moving parts, and I guess I don't have strong opinions about whether these complications are worth it for this new feature. |
Oh, I just didn't think adding an extra string would complicate things further than they already are due to But that's a nice idea, although we don't even know where the top-level component would end up so the extra separate body markup would still be needed at least as an option. But, yeah, not particularly a deal-breaker for me either. I wonder if I could use this to include and deduplicate svg icon symbol definitions automatically within Svelte.. |
@Conduitry Concerning injections which persist beyond the destruction of their parent, the use case I had in mind for this was 'closure' animations and handling. For instance, imagine that a modal is injected by a parent, and then that parent is destroyed. From a UX perspective, it would be preferable for the modal to gracefully handle its orphanage, either by displaying one or more closure animations, or by changing the state of the orphan to display a notification indicating that it has been orphaned, prompting the user to close the modal themselves. Of course, if this kind of functionality is desirable at all, it's not limited to only In both cases, the core problem is that you want to retain your nested component structure without involving external JavaScript, while at the same time gracefully handling orphanage. |
I think it's a good idea and it's really needed for my cases but looks like it possible only for Svelte Framework and not for Svelte Render Engine... Currently, we have no Svelte Framework. |
To merge in my comment from #1872... Maybe there can be a placeholder (e.g. in the template On that note, would it make sense to have arbitrarily named placeholder buffers for the template? This would allow something like: <body>
%sapper.html_prefix%
<div id='sapper'>%sapper.html%</div>
%sapper.html_postfix%
</body> |
I had a message here about how React has portals to do this and I need something similar to make modals & popups work in Svelte. Then I went on Discord and @Rich-Harris reminded me that I can just use |
The problem still exists for flay-out menus that should be position absolutely on the page and not the viewport. |
Hi, I really need this too. E.g. for a lightbox or a modal. Only to use position fixed/absolute for this is not a nice solution because you can't guarantee that there is no parent node somewhere which is positioned somehow. The ability to inject e.g. my modal at the end of the body would be great. |
I think syntax like |
I think I've need for this. I need to open popup from from svelte widget, which can be placed inside scrollable table. Thus table is wrapped into div with "overflow" settings. This causes that if table is narrow and popup for widget is opened, then it will be cut-off by by container. So far, I know that only solution likely would be to open popup outside of this container (i.e. within closest modal dialog or document.body context). However, so far I didn't see how to do it with svelte. Could I allow svelte to render popup, and then manually detach DOM node of popup and attach it into document.body? EDIT: I think that I figured out possible solution to my problem which should work with existing svelte logic. |
@kikonen may I know why you can't use position fixed? It is not affected by an overflow parent, contrary to position absolute. |
Yes, actually I went with position fixed, and thus my specific case was seemingly handled |
For the record, "position: fixed" does not work if any ancestor has the CSS Regarding |
Indeed @ahfarmer. This is cancer... |
Can we rename this ticket from <svelte:body>
<!-- html content -->
</svelte:body> we should have: <svelte:portal inject="#someIdOrCssSelector">
<!-- html content -->
</svelte:portal> where the user can specify some ID or CSS selector in an |
I would much prefer an HTML element to be the expected attribute for |
|
Not extensively tested yet, but this approach seems to have promise: https://svelte.dev/repl/dd6088388b564a70b710e43504f2c193?version=3.24.0 Similar technique to the previous but without using a target node, it just appends the component to to the document body, effectively de-nesting it. |
In general, parameters that accept something like CSS selectors should also accept <svelte:portal inject={document.body}>
<!-- html content -->
</svelte:portal> Actually, this functionality should probably come first and the CSS selectors are optional. You can always do a query manually, but you cannot get from a query to every element (e.g. elements currently not attached to the DOM). <svelte:portal inject={document.querySelector('#selector')}>
<!-- html content -->
</svelte:portal> |
@afaur That leaks memory and the The simplest version of the action probably looks like this: export function portal(node, targetNode)
{
const portalChildren = [...node.children];
targetNode.append(...portalChildren);
return {
destroy()
{
for (const portalChild of portalChildren)
portalChild.remove();
}
}
} (This uses DOM nodes and keeps the original content at the target node as it is. REPL that accepts CSS and nodes) |
@brunnerh I like your example. It seems to work well.
Update: I was having an issue with a template tag. Made an update that seems to fix it. |
I needed this for the same reason as @fabian-michael and svelte-portal helped me out until an official solution is available. |
I had a more complex and dynamic component (paginated modal w/ sub components) for which I was able to build upon this technique, the one as-is did not work.
Utilized the afterUpdate lifecycle hook instead, technically only ran it once, added a conditional that would make sure it ran only once, but reset said conditional whenever the modal may be hidden, as it would need to be re-appended in cases where it was hidden, then re-displayed, or else it would be nested again. |
Are you considering adding this to Svelte as a native feature or do you believe that a simple action like the ones mentioned above is already good enough? I've not yet worked with the actions personally but they seem to be a sub-optimal solution. A native feature sounds nicer. |
Imagine I have the following component,
<TopLevelThing>
.It's intended to be used at the top-level of the
<body>
.However, I might want to use it as part of a component which is deep inside the
<body>
, nowhere near the top-level.This could be achieved with a special tag like the following.
Svelte could inject this into the top-level of the
<body>
alongside other elements.The advantage is that it would retain all the functionality of a component: the lifecycle, properties, and being part of the component organisation and structure.
One common use case for this could be modals. They can often relate very specifically to the organisation and structure of a chain of components. However, perhaps because of styling or some other reason it may be more logical for the modal structure to be located at the top-level of the
<body>
, rather than within the parent component structure.It's quite possible that an alternative approach, using current Svelte features, is most appropriate to these kinds of use cases. However, this approach came to my mind, and I thought it was worth airing.
As an aside, this feature does raise some additional questions. For instance, if a
<:Body>
tag sounds good, would it make more sense to have a more generic<:Injection>
tag instead, which could be used with more than just the<Body>
? Also, might developers find it useful in some cases, of their choosing, for<:Injection>
tags to persist even when their parent components are destroyed?The text was updated successfully, but these errors were encountered: