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

Reparenting iframes without reloading #5484

Closed
josepharhar opened this issue Apr 24, 2020 · 60 comments
Closed

Reparenting iframes without reloading #5484

josepharhar opened this issue Apr 24, 2020 · 60 comments
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest

Comments

@josepharhar
Copy link
Contributor

I have heard several requests for the ability to reparent iframes without reloading, including:

Has this been brought up as a spec issue before? I imagine someone must have thought about this before...

Would it make sense to add a method to iframes to reparent them to another element without reloading?

Another potential use case I've heard from React is to reparent elements other than iframes without losing state. An example of this would be to reparent an input element without losing focus and selection. In this case, perhaps we could have a reparenting method for any element?

cc @mfreed7 @tkent-google @sebmarkbage

@josepharhar
Copy link
Contributor Author

Some more clarification: right now the use case only involves moving iframes or other elements within the same document without ever actually detaching them.

@mfreed7
Copy link
Collaborator

mfreed7 commented Apr 24, 2020

@jeremyroman

@annevk
Copy link
Member

annevk commented Apr 25, 2020

Node trees don't offer move as a primitive operation.

@annevk annevk added addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest labels Apr 25, 2020
@tkent-google
Copy link
Collaborator

Some people implemented this behavior in WebKit, found several critical issues, and removed it.
This issue contains some pointers.

@josepharhar
Copy link
Contributor Author

Based on this patch which removed magic iframes, it sounds like they were for moving iframes between documents, which sounds more vulnerability prone than moving iframes inside of one document.

Ryosuke's comment at the end of the issue makes it sound like there are also security vulnerabilities with moving iframes inside the same document, but I wonder if the security vulnerabilities they encountered were really for the same document cases...

@rniwa
Copy link
Collaborator

rniwa commented Apr 27, 2020

Based on this patch which removed magic iframes, it sounds like they were for moving iframes between documents, which sounds more vulnerability prone than moving iframes inside of one document.

Ryosuke's comment at the end of the issue makes it sound like there are also security vulnerabilities with moving iframes inside the same document, but I wonder if the security vulnerabilities they encountered were really for the same document cases...

The most of security issues were agnostic to whether an iframe was moved across a document or not. The main issue is the document inside a (temporarily) disconnected iframe being considered alive.

@josepharhar
Copy link
Contributor Author

Thanks for the feedback Ryosuke!

Do you think that it would be possible to move the iframe atomically and therefore never have a document in a disconnected frame?

@rniwa
Copy link
Collaborator

rniwa commented Apr 27, 2020

Do you think that it would be possible to move the iframe atomically and therefore never have a document in a disconnected frame?

That was the idea. But how does one move a node from one place to another? Since a node can't have multiple parents, it needs to be disconnected at some point in some internal engine state. That's precisely what caused the problem. In particular, if there are multiple iframe that could get disconnected in order, then they can access each other in weird ways in scripts, etc... I'm not gonna go into details but I don't think this is an idea we should re-pursue anytime soon.

@sebmarkbage
Copy link

Moving between parents would be very useful but the use case for React (and similar) is to be able to move position within the same parent. Eg swapping the place of two nodes in the same parent.

@annevk
Copy link
Member

annevk commented Apr 27, 2020

@rniwa did you introduce a new type of mutation for node trees? It seems to me you would have to start there. We only have remove/insert at the moment.

@rniwa
Copy link
Collaborator

rniwa commented Apr 27, 2020

@rniwa did you introduce a new type of mutation for node trees? It seems to me you would have to start there. We only have remove/insert at the moment.

I don't recall but I don't think that really helps so long as such a replacement involves more than one node at once (e.g. subtree) because then a subtree can contain iframe which then need to be kept alive, etc...

@annevk
Copy link
Member

annevk commented Apr 27, 2020

I think it would help in that you can define now rules around it in terms of script execution, but it would be a pretty large undertaking and there's a lot of non-interoperable behavior around this for the current remove/insert operations that should be sorted first.

@annevk annevk closed this as completed Apr 27, 2020
@annevk annevk reopened this Apr 27, 2020
@jakearchibald
Copy link
Collaborator

Rather than inventing a new kind of tree mutation, it feels like the simplest solution for developers would be to keep the iframe alive for a microtask after disconnection (although maybe need to think more about mutation events, as they already use microtasks).

I realise this doesn't make implementation easier.

@annevk
Copy link
Member

annevk commented Apr 27, 2020

How would that work specification-wise? Please note whatwg/dom#808 and ideally everything linked from there to get an idea about the complexity with regards to script execution that we have with insert and remove today. (Mutation events also fire synchronously for insert/remove and are also not specified yet and indeed also cause problems here.)

@jakearchibald
Copy link
Collaborator

I figured it'd be something like:

An iframe has a pending discard flag which is unset.

When an iframe element is removed from a document:

  1. If the iframe's pending discard flag is set, abort these steps.
  2. Set the iframe's pending discard flag.
  3. Queue a microtask to run the following steps:
    1. Unset the pending discard flag.
    2. If the iframe is not connected, discard the nested browsing context & set it to null.

Although, if the iframe is inserted into another document, the current nested browsing context should also be discarded (if that was a particular source of security bugs).

@sophiebits
Copy link

For what it's worth, from a web dev perspective, there's already a way to ask for an element to be moved in a single step: just call .appendChild or .insertBefore for its new position without ever calling .removeChild. This is in fact what React already does when reordering children within a list.

@sebmarkbage
Copy link

sebmarkbage commented Apr 27, 2020

I'd like to call out this part too:

Another potential use case I've heard from React is to reparent elements other than iframes without losing state. An example of this would be to reparent an input element without losing focus and selection.

The example of iframes is one particularly complicated case but it's one of several examples of state being lost.

Text selection and focus within an <input> when that input moves position within the same parent is another case.

CSS transitions playing in a subtree will be reset if any parent is removed and appended.

These are much more common and problematic than iframes.

In this case, perhaps we could have a reparenting method for any element?

It might be worth while listing out all these cases, because I could very well imagine that it's not web compatible to change this behavior for all of them using appendChild only. That might motivate why a new API for moves is needed.

This is a big deal in diffing based solutions (this include virtual DOMs like React and template based solutions based on diffing rather than change tracking). The reason it is such a big deal is that there's nothing that says which node to move. If you swap two nodes and diff them, there's nothing in the API to imply which one should be moved. The algorithm just picks one to move. Depending on the algorithm tradeoffs you could also end up moving four other nodes to move one since it's not worth computing the longest common subsequence.

Any browser built-in diffing based approach will also need to consider this problem.

In an imperative manual API you might have some intuition about which is safer and less disturbing to move. As such this was much less of a problem for the first decades of the web. However, even then there are cases where you just have to live with the worse experience.

In React we have some code to work around cases such as restoring selection and focus which adds to the code size of React, but we leave some unsolved.

@annevk
Copy link
Member

annevk commented Apr 28, 2020

@sophiebits unfortunately that's not an atomic move and will do a remove and then an insert (with all the associated script running bits).

@sebmarkbage an exhaustive list of problematic moving scenarios would help a lot.

@smaug----
Copy link
Collaborator

An iframe has a pending discard flag

And what happens when JS runs in the iframe while the iframe isn't really connected. How does window.parent in the iframe's window work, what should history.go() do, or location.href = "newurl"? How should window.frames behave in the parent window? ...

@emilio
Copy link
Contributor

emilio commented Apr 28, 2020

Somewhat limited, but for what is worth, you can sorta get that effect with shadow DOM and slotting, it seems to work in all browsers:

<!doctype html>
<button>Toggle</button>
<div id="host">
  <iframe id="frame" src="https://wikipedia.org" slot="a" style="border: 0"></iframe>
</div>
<script>
  document.getElementById("host").attachShadow({ mode: "open" }).innerHTML = `
    <style>
      div { height: 150px; background: blue; }
      #b { background: purple }
    </style>
    <div id="a">
      <slot name="a"></slot>
    </div>
    <div id="b">
      <slot name="b"></slot>
    </div>
  `;
  document.querySelector("button").addEventListener("click", function() {
    let frame = document.getElementById("frame");
    frame.slot = frame.slot == "a" ? "b" : "a";
  })
</script>

@jakearchibald
Copy link
Collaborator

jakearchibald commented Apr 28, 2020

@smaug----

An iframe has a pending discard flag

And what happens when JS runs in the iframe while the iframe isn't really connected. How does window.parent in the iframe's window work

I looked at .parent yesterday, and it seems like that uses the owner document, so it'd still work, as in it'd return the same parent as if it were connected, which seems reasonable.

what should history.go() do, or location.href = "newurl"?

I think this should behave the same as if it were connected, but I'm sure there are tricky details.

How should window.frames behave in the parent window?

That one's less clear to me.

(I didn't mean to give the impression that this would be easy, btw)

@josepharhar
Copy link
Contributor Author

Somewhat limited, but for what is worth, you can sorta get that effect with shadow DOM and slotting, it seems to work in all browsers:

Thanks for the workaround @emilio, I didn't see that get suggested in the stackoverflow post or anywhere else!

Rather than inventing a new kind of tree mutation, it feels like the simplest solution for developers would be to keep the iframe alive for a microtask after disconnection (although maybe need to think more about mutation events, as they already use microtasks).

@jakearchibald I agree this sounds like it would be awesome for developers because it would just make iframes stop reloading as is. However, @rniwa and @smaug----'s words make me feel afraid of going forward with this due to the non-atomic nature of doing it async.

It might be worth while listing out all these cases, because I could very well imagine that it's not web compatible to change this behavior for all of them using appendChild only.

@sebmarkbage I totally agree, I think the best step for us to do right now is collect information on all the different cases where we are losing state due to reparenting, and how common/painful each one is.

Does anyone have any thoughts on where it would be best for me to start compiling this list? Maybe a github gist or a google doc? And does anyone have any other cases to add or links to anything that would help assess how common/painful each case is?

@annevk
Copy link
Member

annevk commented May 2, 2020

I think we need two things to make progress here:

  1. Getting Side effects due to tree insertion or removal (script, iframe) dom#808 resolved somehow so it's more clearly understood what the side effects of insertion/removal are (which would allow us to be explicit about not triggering them for move).
  2. Gathering use cases for move that cannot be addressed with insertion/removal. It doesn't really matter where this is done, but the outcome needs to be presented in a whatwg/dom issue as the DOM Standard would have to add this primitive. Note that adding that will require some design work as it also impacts mutation observers and such. We'd probably also have to check all the elements that have insertion/removal hooks of some kind and ensure they cannot end up in a broken state due to solely being moved. (Elements with such steps might also be a good source for use cases.)

@josepharhar
Copy link
Contributor Author

@annevk Thanks for suggesting this path forward!

I put the problematic use cases @sebmarkbage listed here: https://github.com/josepharhar/reparenting-loses-state

@sebmarkbage are there any other related problematic use cases you know of? Are there any react issues you could point us to which show interest in any of these use cases?

@marvinhagemeister
Copy link

Happy to see that this is getting traction again!

Just wanted to re-iterate the weight of the scenarios @sebmarkbage shared. The main issue we run into with working on Preact are the same. Loosing transition state or input state (focus/cursor position/text selection) are easily at the top of the most frequent issues we have with working with the DOM.

So far we haven't found more issues than those already mentioned here.

@worldpeaceenginelabs
Copy link

worldpeaceenginelabs commented Dec 9, 2022

@Stehlampe2020 i really hate pop-ups, but i found this article last week (https://javascript.info/popup-windows) and thought about using !labeled! pop-ups instead. (they come with the danger not popping up because of adblock or not supported, but not supported or allowed can also happen with iframes and even js itself, so pfff) :D

then they would get managed by the general tab manager of the browser and/or window manager of the computer os.
but you could define size and look of these tabs and windows, where they pop up, if with address bar or not, with menu or minimize/close or not, etc etc....

and since they are labeled you can check and call for/on them...

@worldpeaceenginelabs
Copy link

worldpeaceenginelabs commented Dec 9, 2022

@Stehlampe2020 Maybe you could just use the svelte slot feature in your app with adding svelte via npm svelte@latestf?
And then something like this: https://ashutosh.dev/svelte-slots/

Because i just remembered this:

You can use Svelte to build single, reusable components for projects of any kind, including larger applications written with Angular, React, Vue, or any other frameworks.

But even easier:
If you want to go the full svelte way, start a new repo with npm vite@latest and choose svelte/TS in the prompt. (ready pre-configured, much better than npm svelte. you can start out of the box in typescript, just copy paste your code from the video into the new repo)
That gets you the lightspeed fast vite compiler on top. run dev, hot reload ready in under a sec, builds in under a minute, even on larger apps. edge-ready of course. 😍👍

Tell me please, in case you found the svelte slot equivalent in whatever language/framework you code in!

@Stehlampe2020
Copy link

Stehlampe2020 commented Dec 10, 2022

@Stehlampe2020 i really hate pop-ups, but i found this article last week (https://javascript.info/popup-windows) and thought about using !labeled! pop-ups instead. (they come with the danger not popping up because of adblock or not supported, but not supported or allowed can also happen with iframes and even js itself, so pfff) :D

then they would get managed by the general tab manager of the browser and/or window manager of the computer os. but you could define size and look of these tabs and windows, where they pop up, if with address bar or not, with menu or minimize/close or not, etc etc....

and since they are labeled you can check and call for/on them...

This wouldn't work for me as Firefox doesn't allow for hiding of the address bar (not even in popups from trusted sites) and I don't want to make real windows in the real OS but just simulated windows inside the page.

And to the thing with svelte:
I want to keep my page free from any CMS or not-self-written code. I only do HTML, CSS and vanilla JavaScript and even if some code is copied from StackOverflow I have inserted it in the exact way it looks in the page source sent to the user.
That's my sites' principle, only things that I fully understand and have full control over should happen there.
I have even written my own "library" that implements a lot of little, useful functions the site may need (cookie handling, page reloading to another URL, ...).

@worldpeaceenginelabs
Copy link

@Stehlampe2020

I want to keep my page free from any CMS or not-self-written code. I only do HTML, CSS and vanilla JavaScript and even if some code is copied from StackOverflow I have inserted it in the exact way it looks in the page source sent to the user.

we doing the exact same thing. i switched to svelte, because every component is like this:

<script>
<markup>
<style>

this stackoverflow coping was important to me to. and the clean code of course.

you can go with vanilla also, but svelte will even transfer your copied typescript or whatever snippets into vanilla automatically.

plus the svelte environment from vite@latest is just devs dream.

i shit you not :D
svelte is just a better compiler / environment, but no svelte specifics neccesary.

Just copy/paste your vanilla and stackoverflow stuff and see for yourself.

you will love it

@worldpeaceenginelabs
Copy link

worldpeaceenginelabs commented Dec 11, 2022

I want to keep my page free from any CMS or not-self-written code. I only do HTML, CSS and vanilla JavaScript and even if some code is copied from StackOverflow I have inserted it in the exact way it looks in the page source sent to the user.

I tried a lot of frameworks and cms libs and services, but came to the conclusion that the JAMstack is my best new friend, since i want it clean vanilla, small, fast, edge-ready and re-usable (community friendly) which the svelte compiler/vite bundler in vite@latest comes with, right out of the box 😍😍😍 (npm build makes vanilla) 😍😍😍

so I can highly suggest it.
btw: svelte(best jamstack compiler), NOT sveltekit!!! (framework)

and if I couldn't convince you of the svelte, minimum go for jamstack. https://jamstack.wtf/

both supports exactly what you are doing now, just better. ;)

@jryans
Copy link

jryans commented Dec 11, 2022

@worldpeaceenginelabs Your last few messages don't really seem on topic for an issue in the HTML repo like this. Let's keep the discussion here focused on reparenting iframes and discuss other things somewhere else.

@worldpeaceenginelabs
Copy link

@jryans Hi :)
i see, that i totally forgot the mainreason why i started mentioning svelte 😅 sorry

There is no DOM in svelte. For your reparenting iframes issue it means, no dom update neccesary. Svelte advertises itself as truly reactive and its true. Svelte apps update itself partially. (in this case updating the frame, but not reloading the content)

@Stehlampe2020
Copy link

Stehlampe2020 commented Dec 13, 2022

@worldpeaceenginelabs You can't convince me to use svelte. I'll try to look into other simulation's code to figure out an alternative solution.
@jryans And it would be nice if iframes could keep their status at least if they're immediately reinserted, like for example calling Element.appendChild(iframesParent); on an element already in the same DOM.
[EDIT]: Now that Windows93 will soon release their Sys42 (which seems to be able to handle iframes) I could see myself using that for my OS simulation.

dmitry-yudakov added a commit to momentum-xyz/ui-client that referenced this issue Feb 13, 2023
We support several screen elements that need to be aligned together: maximized elements on top-left (like video/screenshare/plugins), top-right elements (voice and text chats) and previews on bottom-right (video/plugins/screencast). Video/screencast/plugins can also move between top-left and bottom-right - the important thing here is to not make them detach from parent when moving between screen sections - and it's pretty challenging to satisfy all this.

Current solution uses flexbox for SectionedScreen component and SectionedScreenPortal elements (used as Portal for all abovementioned). SectionedScreenPortal appends a child to SectionedScreen using proper class based on target section.

Different sections' classes have slightly different styles/order and additional logic is applied to force the flexbox break into 2 rows if both top-right and bottom-right elements are detected. We use ":has" selector that currently has relatively limited support - it currently works on Chrome/Brave, but on Firefox you still need to activate it with config. There's a fallback option allowing almost same functionality without the ability to use full height of the screen when only top-left and/or top-right elements are present - they're at 60% height then.

Using flexbox is a working though not perfect solution as it's not really intended for such layout.

I tried display:grid but it becomes even more complex to manipulate it in flexible enough manner based on content.

My original approach was with 2 nested containers - one for top/bottom row and one left/right for each row - it was working nicely as a layout but moving between sections triggers child detach, which causes interruptions with iframe videos, etc. It's probably possibly to use shadow DOM for translating screen element into different sections but I doubt it's possible using only css.

Another planned approach was having React Context used by SectionedScreen and SectionedScreenPortals for sharing info between them and applying different styles and helper elements based on that, but it seems css with :has selector allows to satisfy all our current requirements.

Some relatively new css selectors like :only-child, :only-of-type, :nth-of-type could also be of use in future.

Some useful links:
https://ishadeed.com/article/conditional-css/
https://tobiasahlin.com/blog/flexbox-break-to-new-row/

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Aligning_Items_in_a_Flex_Container

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout

Shadow DOM:
whatwg/html#5484 (comment) - nice and simple example
https://stackoverflow.com/questions/42274721/shadow-dom-and-reactjs
https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow
dmitry-yudakov added a commit to momentum-xyz/ui-client that referenced this issue Feb 13, 2023
We support several screen elements that need to be aligned together: maximized elements on top-left (like video/screenshare/plugins), top-right elements (voice and text chats) and previews on bottom-right (video/plugins/screencast). Video/screencast/plugins can also move between top-left and bottom-right - the important thing here is to not make them detach from parent when moving between screen sections - and it's pretty challenging to satisfy all this.

Current solution uses flexbox for SectionedScreen component and SectionedScreenPortal elements (used as Portal for all abovementioned). SectionedScreenPortal appends a child to SectionedScreen using proper class based on target section.

Different sections' classes have slightly different styles/order and additional logic is applied to force the flexbox break into 2 rows if both top-right and bottom-right elements are detected. We use ":has" selector that currently has relatively limited support - it currently works on Chrome/Brave, but on Firefox you still need to activate it with config. There's a fallback option allowing almost same functionality without the ability to use full height of the screen when only top-left and/or top-right elements are present - they're at 60% height then.

Using flexbox is a working though not perfect solution as it's not really intended for such layout.

I tried display:grid but it becomes even more complex to manipulate it in flexible enough manner based on content.

My original approach was with 2 nested containers - one for top/bottom row and one left/right for each row - it was working nicely as a layout but moving between sections triggers child detach, which causes interruptions with iframe videos, etc. It's probably possibly to use shadow DOM for translating screen element into different sections but I doubt it's possible using only css.

Another planned approach was having React Context used by SectionedScreen and SectionedScreenPortals for sharing info between them and applying different styles and helper elements based on that, but it seems css with :has selector allows to satisfy all our current requirements.

Some relatively new css selectors like :only-child, :only-of-type, :nth-of-type could also be of use in future.

Some useful links:
https://ishadeed.com/article/conditional-css/
https://tobiasahlin.com/blog/flexbox-break-to-new-row/

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Aligning_Items_in_a_Flex_Container

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout

Shadow DOM:
whatwg/html#5484 (comment) - nice and simple example
https://stackoverflow.com/questions/42274721/shadow-dom-and-reactjs
https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow
dmitry-yudakov added a commit to momentum-xyz/ui-client that referenced this issue Feb 13, 2023
We support several screen elements that need to be aligned together: maximized elements on top-left (like video/screenshare/plugins), top-right elements (voice and text chats) and previews on bottom-right (video/plugins/screencast). Video/screencast/plugins can also move between top-left and bottom-right - the important thing here is to not make them detach from parent when moving between screen sections - and it's pretty challenging to satisfy all this.

Current solution uses flexbox for SectionedScreen component and SectionedScreenPortal elements (used as Portal for all abovementioned). SectionedScreenPortal appends a child to SectionedScreen using proper class based on target section.

Different sections' classes have slightly different styles/order and additional logic is applied to force the flexbox break into 2 rows if both top-right and bottom-right elements are detected. We use ":has" selector that currently has relatively limited support - it currently works on Chrome/Brave, but on Firefox you still need to activate it with config. There's a fallback option allowing almost same functionality without the ability to use full height of the screen when only top-left and/or top-right elements are present - they're at 60% height then.

Using flexbox is a working though not perfect solution as it's not really intended for such layout.

I tried display:grid but it becomes even more complex to manipulate it in flexible enough manner based on content.

My original approach was with 2 nested containers - one for top/bottom row and one left/right for each row - it was working nicely as a layout but moving between sections triggers child detach, which causes interruptions with iframe videos, etc. It's probably possibly to use shadow DOM for translating screen element into different sections but I doubt it's possible using only css.

Another planned approach was having React Context used by SectionedScreen and SectionedScreenPortals for sharing info between them and applying different styles and helper elements based on that, but it seems css with :has selector allows to satisfy all our current requirements.

Some relatively new css selectors like :only-child, :only-of-type, :nth-of-type could also be of use in future.

Some useful links:
https://ishadeed.com/article/conditional-css/
https://tobiasahlin.com/blog/flexbox-break-to-new-row/

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Aligning_Items_in_a_Flex_Container

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout

Shadow DOM:
whatwg/html#5484 (comment) - nice and simple example
https://stackoverflow.com/questions/42274721/shadow-dom-and-reactjs
https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow
@quartexNOR
Copy link

I have to respectfully ask that something be done with how IFrames behave when moving an IFrame element within the same parent, this is intolerable.

Like others which have commented I am making a desktop designed as a front-end for a NAS system, and like most "web desktops" it operates with fake (div based) windows. These fake windows look and behave like ordinary windows and have a "send to back" and "bring to front" glyph on its borders.

The problem is, that whenever someone clicks on the "send to back" glyph, I have no choice but to remove the window's element from the parent (which is the "desktop" div in this case), and then re-insert it into the same parent at either the beginning or end of the child collection ( insertBefore() | appendChild() ).

We have spent 4 years building our own compiler, IDE and RTL for this project. The project is written in a unique flavor of Object Pascal designed especially for web-development, and the compiler emits highly effective JavaScript.
The desktop system is a huge project that will be used both for teaching (schools use our devtools to teach kids to program in object pascal and Javascript side by side), and also as a NAS front-end, ala Synology.

Being able to move an element at least within the same parent (read: no re-parenting of the element, just move it within the child collection where the element is presently located) is something I quite frankly find strange is still lacking in 2023. This has been requested for 17 years (!) on the Mozilla forums.

I strongly urge you to reconsider your position on this. What I would suggest is something that would not require a lot of work, but can be done relatively easily with the code that is already in the browser core. My suggestion is as follows:

Add methods for moving elements within the same parent, which means no re-parenting is needed, and thus does not cause issues with scripting or security.

Candidates on top of my head would be:

  1. Element.swapChildren( int32 child1, int32 child2)
  2. Element.bringToFront()
  3. Element.sendToBack()

(*) The above would not affect z-order, only the position of the element in a parent's child collection. It would not affect security, mutation events or any of the existing infrastructure. And it would save people from having to re-map the zorder for all child elements just to change display order of an item.

Looking at various posts around the internet where people debate the IFrame problem, the above would solve the majority of cases I have seen.

Kind regards
Jon-Lennart Aasenden

@Anprotaku
Copy link

Anyone found any solution excluding the slot element?

@flackr
Copy link

flackr commented Sep 14, 2023

@sophiebits unfortunately that's not an atomic move and will do a remove and then an insert (with all the associated script running bits).

@annevk What are the script running bits that happen after the remove before the insert? Could we eliminate these? Alternately since we know from the single API call that a node is being moved could we give this move semantics and not apply side effects from insertion/removal in the internal operation if the source / destination documents are the same?

@dead-claudia
Copy link

@sophiebits unfortunately that's not an atomic move and will do a remove and then an insert (with all the associated script running bits).

@annevk What are the script running bits that happen after the remove before the insert? Could we eliminate these? Alternately since we know from the single API call that a node is being moved could we give this move semantics and not apply side effects from insertion/removal in the internal operation if the source / destination documents are the same?

Legacy mutation events are synchronous. They can observe the difference very easily.

Also, iframes have semantics run when inserted and removed from the live tree, and there's ways to almost-synchronously observe the resulting navigation when the child and parent frames cooperate.

Just to name a couple.

@flackr
Copy link

flackr commented Sep 14, 2023

Even if we create a new API, e.g. whatwg/dom#891, we would still have to answer these questions. My hope is that there are reasonable answers for how these should behave.

@annevk What are the script running bits that happen after the remove before the insert? Could we eliminate these? Alternately since we know from the single API call that a node is being moved could we give this move semantics and not apply side effects from insertion/removal in the internal operation if the source / destination documents are the same?

Legacy mutation events are synchronous. They can observe the difference very easily.

How breaking would it be if your event observers ran after the frame was already parented to the new node? This would leave no observable state where the frame was disconnected. Admittedly this would be a pretty big change for synchronous legacy mutation events but per this intent to deprecate they are already fairly low usage - the cases where the difference in timing of the removal during an operation which is immediately readding the node would be some fraction of those cases.

Also, iframes have semantics run when inserted and removed from the live tree, and there's ways to almost-synchronously observe the resulting navigation when the child and parent frames cooperate.

I'd have to familiarize myself with all of these semantics, but I think it may be reasonable to skip most/all of them in the case where it is known to remain in the document. e.g. newParent.appendChild(alreadyAttachedFrame) as in these cases we explicitly don't want to reload the frame or change/lose state. It shouldn't navigate.

@dead-claudia
Copy link

How breaking would it be if your event observers ran after the frame was already parented to the new node? This would leave no observable state where the frame was disconnected. Admittedly this would be a pretty big change for synchronous legacy mutation events but per this intent to deprecate they are already fairly low usage - the cases where the difference in timing of the removal during an operation which is immediately readding the node would be some fraction of those cases.

@flackr Thanks for the call out there on usage. I'll take that bit back.

I'd have to familiarize myself with all of these semantics, [...]

To be fair, iframes are literally mentioned in the title of this issue. Animations are another example, though in theory that could be worked around through some very hacky CSS (read: animation-delay) + event glue.

@flackr
Copy link

flackr commented Sep 21, 2023

To be fair, iframes are literally mentioned in the title of this issue. Animations are another example, though in theory that could be worked around through some very hacky CSS (read: animation-delay) + event glue.

Right, but what I'm suggesting is that these side effects shouldn't happen when the element is known to be moving to a different position in the same document. It would make sense that animations shouldn't be canceled and restart as well (as long as the style in the new DOM location still resulted in the animation).

@AwokeKnowing
Copy link

AwokeKnowing commented Sep 21, 2023

I'm glad this is still being looked at because it is vital to be able to use iframes for isolation yet move them as needed, for workflows where you transition between two different views but need the content to be unaffected. In particular full screen views vs regular views, and other cases.

@anthonyhoegberg
Copy link

What about instead of reparenting or moving iframes etc, we allow the slot element or something like slots to work not only in shadow root but everywhere?
So that we can keep an element in its position in the dom tree and we just slot it somewhere else where we want it to be rendered? We will kind of get the same effect but we wont have to move the element anywhere. We just slot it somewhere else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest
Development

No branches or pull requests