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

Replace target instead of appending to it #1549

Closed
tarjei opened this issue Jun 16, 2018 · 13 comments
Closed

Replace target instead of appending to it #1549

tarjei opened this issue Jun 16, 2018 · 13 comments

Comments

@tarjei
Copy link

tarjei commented Jun 16, 2018

Hi, when I do new MyComponent({target: document.getElementById('someId')}) then the new component gets added to the element instead of replacing it.

I would love to have a way to replace the target, f.x new MyComponent({target: document.getElementById('someId'), replace: true}). This would give my users a better experience in situations where I'm adding Svelte components to content that they already see in their browser.

@plumpNation
Copy link

plumpNation commented Jun 16, 2018

This conversation may still be relevant.

#537

function mountReplace(Component, options) {
    const frag = document.createDocumentFragment();
    const component = new Component({ ...options, target: frag });

    options.target.parentNode.replaceChild(frag, options.target);

    return component;
}

// usage
mountReplace(MyComponent, { target, data });

@constgen
Copy link

constgen commented Jun 16, 2018

Also I didn't tested the latest version but the last time I tried I couldn't render the target content as a default <slot> in my component. I expect all children in a target DOM to become children of my component in a slotted spot.

@tbillington
Copy link

This is a funny one :) I ran into this while trying to set up hot reloading for svelte.

@plumpNation
Copy link

The slot functionality relies on you being in the Svelte context. So you will need to wrap your component that contains the slot in a small svelte "app" and mount that.

@Rich-Harris
Copy link
Member

That's not quite true — there is an API for slots, though I think we haven't got round to documenting it. You can do this:

const thing = new Thing({
  target,
  slots: {
    default: someDomNodeOrFragment,
    foo: someOtherDomNodeOrFragment
  }
});

If you're compiling your components with hydratable: true and instantiating them with hydrate: true, it will reuse whatever DOM is currently in the target (and discard the rest). This is designed for rehydrating server-rendered components, so if that's what your goal is then it's what I'd recommend.

Otherwise, using a helper to clear out the existing DOM is the best way forward — it's not something we should add to Svelte itself, since it's extra code for something that most people won't need.

@plumpNation
Copy link

OK, that's super useful. Thanks for setting me straight. Sorry @constgen for poor information.

@Conduitry
Copy link
Member

Closing this, as I don't think there's anything else that needs to happen. Comment if you think otherwise.

@tarjei
Copy link
Author

tarjei commented Jul 10, 2018

@Conduitry maybe add a note containing the mountReplace function above in the docs?

@m4rrc0
Copy link

m4rrc0 commented Sep 23, 2020

For future reference.

there is an API for slots

Seems like this API was for Svelte v2.
For v3, there is this open PR and this issue which mentions a workaround using the private API.

@Lindsay-Needs-Sleep
Copy link

You can also simply do this to replace the target:

function replaceTarget (target) {
    const component = new MySvelteComponent({
        target: target.parentElement,
        anchor: target,
    });
    target.remove();
}

This prepends the new component just before the target element (in the target's parent), and then removes the target.

Relevant documentation on anchor: https://svelte.dev/docs#Creating_a_component

Probably not a good idea to use with target === <body>?

@azmeuk
Copy link

azmeuk commented Feb 9, 2021

@Lindsay-Needs-Sleep Nice! What about keeping the target attributes (name, id etc. )?

@Lindsay-Needs-Sleep
Copy link

@azmeuk My use case didn't require that, so I didn't play with it.
But I think you could have to manually extract those attributes and apply them to the new element.

eg. something like

function replaceTarget (target) {
    // Manually read the attributes you want tp preserve
    var name = target.name;
    // You can pass the value to the svelte component's props if your component has something like "export let myName;"

    const component = new MySvelteComponent({
        target: target.parentElement,
        anchor: target,
        props: {
          myName: name,  
        },
    });
    target.remove();
}

Since a svelte component doesn't necessarily have a single root node, you would have to do something pretty extra to decide which element within your svelte component to apply the attributes to.

@azmeuk
Copy link

azmeuk commented May 3, 2021

I ended with those utility functions:

function replaceTargetByComponent(target, Component, options) {
    const frag = document.createDocumentFragment();
    var props = {
        name: target.name,
        id: target.id,
        ...target.dataset,
    }
    const component = new Component( Object.assign( {}, options, {
        target: frag,
        props: props,
    } ));
    target.replaceWith( frag );
}

export function replaceClassByComponent( classname, Component, options ) {
    const targets = document.getElementsByClassName(classname)
    for (const target of targets) {
        if (target.replaceWith) {
            replaceTargetByComponent(target, Component, options);
        }
    }
}

export function replaceIdByComponent( id, Component, options ) {
    const target = document.getElementsById(id)
    if (target && target.replaceWith) {
        replaceTargetByComponent(target, Component, options);
    }
}

The good thing being one can dynamically pass property to the components by setting HTML data attributes.

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

No branches or pull requests

9 participants