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

Allow transitions to work within iFrames #3624

Closed
jacwright opened this issue Sep 26, 2019 · 21 comments · Fixed by #3625
Closed

Allow transitions to work within iFrames #3624

jacwright opened this issue Sep 26, 2019 · 21 comments · Fixed by #3625

Comments

@jacwright
Copy link
Contributor

Sometimes your app needs to run within an iframe. For example, Intercom's chat widget which needs to be style-sandboxed from the rest of the page is run in an iFrame. My app wraps a navigation component inside an iFrame for better performance in the rest of the page.

The only thing that can't be worked around in this situation is Svelte's CSS transitions. They add the animation styles to the global (top-level) document and not the component's document.

For Svelte's transitions to work, Svelte will need to support adding styles to the document a component lives in.

I've created a solution which works well and avoids memory leaks. I reference this ticket in the PR.

jacwright added a commit that referenced this issue Sep 26, 2019
Svelte works great in iframes except for transitions and animations. This fixes that issue.

See #3624.
@simeydotme
Copy link
Contributor

@jacwright , do you have a simple REPL to demo how to render content in an iFrame ? I wanted to do this but I couldn't figure it out :)

@jacwright
Copy link
Contributor Author

Unfortunately because the REPL sandboxes it's iframe, any iframes within it are sandboxed and it doesn't work. In the test I added as part of the pull request I include a Frame.svelte component which you can use. You'll need to add the styles to the body in the onload event, but it should work.

I did try to get it working with the REPL.

@simeydotme
Copy link
Contributor

ok, I'll look , thanks...

Just in case you were unaware; www.codesandbox.io also has a Svelte template, and you can "pop out" the preview pane in to a separate (non-framed) window, maybe that'll help your PR 🙂

@jacwright
Copy link
Contributor Author

Unless I can load a branch of Svelte into www.codesandbox.io I don't know if that will help determine the PR correctly fixes any issues.

@2beers
Copy link

2beers commented Oct 12, 2019

I'm also in a similar situation.
From what I saw the compiler returns the javascript and css code for every .svelte component, then the bundler just bundles the code in 2 separate files: bundle.js and bundle.css
I think we need something similar to <svelte:head> but for iframes.
Maybe <svelte:iframe target={myiFrame}>css code</svelte:iframe>

@jacwright I'm also interested on how to add content to an iFrame. I managed to do this by manually creating a component, but I don't think this is the best solution.
Here is code, let me know if your solution is better. My problem is the name is not updated in the FrameBody component and I need to use component.$set(props) se update properties, which is against svelte framework.

<script>
    import FrameBody from './FrameBody.svelte';
    let name = "Jim";

    function frameLoaded(ev) {
        const iframe = ev.target;
        const component = new FrameBody({
            target: iframe.contentWindow.document.body,
            props: {
                name: name
            }
        });
    }

    function changeName() {
        name = 'Tim';
    }
</script>

<h2>{name}</h2>
<iframe on:load="{frameLoaded}"></iframe>
<div>
    <button on:click={changeName}>Change name</button>
</div>

@Tzelon
Copy link

Tzelon commented Oct 14, 2019

I also need this feature implemented.
@jacwright is there a way to use your pull requests inside my project?

@jacwright
Copy link
Contributor Author

@2beers I use a Svelte component to simplify the iframe. See https://gist.github.com/jacwright/7003916d7ae402b528dbaaca3061ca92#file-app-svelte-L6 as an untested example.

@jacwright
Copy link
Contributor Author

@Tzelon you could clone my branch locally, then:

  1. build svelte
  2. link svelte (for local use)
  3. link inside your project
npm run build
npm link
cd ../path/to/your/project
npm link svelte

That's how I'm using it right now.

@jacwright
Copy link
Contributor Author

Oh yeah, that gist is from the REPL. It just didn't work in the REPL because of sandboxing issues. https://svelte.dev/repl/7003916d7ae402b528dbaaca3061ca92?version=3.12.1

@Tzelon
Copy link

Tzelon commented Oct 16, 2019

@Tzelon you could clone my branch locally, then:

  1. build svelte
  2. link svelte (for local use)
  3. link inside your project
npm run build
npm link
cd ../path/to/your/project
npm link svelte

That's how I'm using it right now.

Works perfectly

@jtormey
Copy link

jtormey commented Nov 17, 2019

Is this specific to transitions and animations, or is this an issue for all styles? I'm trying to render a component with an iframe document body as the target, and all component styles get appended to the root document head.

It looks like Svelte briefly had support for styles in an iframe by accessing ownerDocument (added in #362), but was removed later (in #377).

@jacwright
Copy link
Contributor Author

@jtormey This PR is specific to transitions and animations. It would be great to get another PR for component styles.

@jtormey
Copy link

jtormey commented Nov 20, 2019

Thanks! I'll take a look at that. In the meantime, were you able to find a workaround that allowed you to inject component styles generated by Svelte into the iframe for your navigation component?

@jacwright
Copy link
Contributor Author

jacwright commented Nov 20, 2019

My iframe component does this little number on load:

Array.from(document.querySelectorAll('style, link[rel="stylesheet"]')).forEach(node => head.appendChild(node.cloneNode(true)));

Here it is in context:

import { onMount, onDestroy } from 'svelte';

export let component;
let frame; // bound in HTML with <iframe bind:this={frame}>
let content;

function onLoad() {
  const head = frame.contentDocument.head;
  const body = frame.contentDocument.body;

  Array.from(document.querySelectorAll('style, link[rel="stylesheet"]'))
        .forEach(node => head.appendChild(node.cloneNode(true)));

  if (component) {
    const { component, ...props } = $$props;
    content = new component({
      target: body,
      props,
    });
  }
}


onMount(async () => {
  if (frame.contentDocument.readyState === 'complete' && frame.contentDocument.defaultView) {
    onLoad();
  } else {
    frame.addEventListener('load', onLoad);
  }
});

onDestroy(() => {
  if (frame) frame.removeEventListener('load', onLoad);
  if (content) content.$destroy();
});

@jtormey
Copy link

jtormey commented Nov 20, 2019

This is great! Thanks for sharing.

@ostaplisovyj
Copy link

ostaplisovyj commented Dec 29, 2019

I can see that the PR for this issue is still opened. Are there any updates on this? (my app really needs this feature). Thanks in advance.

@jacwright
Copy link
Contributor Author

jacwright commented Feb 19, 2020

I haven't had the time to update the PR to the latest version of Svelte and resubmit. I should be able to soon (in the next month). Sorry for the delay!

It helps knowing others need this too.

@Conduitry
Copy link
Member

This has been released in 3.20.0, thank you!

@naorye
Copy link

naorye commented Apr 21, 2020

@jacwright
Is there a way to take only the app styles instead of taking all the styles of the parent?

@jacwright
Copy link
Contributor Author

jacwright commented Apr 24, 2020 via email

@naorye
Copy link

naorye commented May 6, 2020

@jacwright You right. Thats what I've done.
Eventually I managed to add an actual iframe (not rendered by svelte) which isolated the styles completely. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants