Svelte: Replace target node when mounting component

Created on 30 Apr 2017  路  3Comments  路  Source: sveltejs/svelte

It should be possible to completely replace the mount target node instead of using it as a container.

Other frameworks supply this functionality, and I've found when trying to create the minimum possible properly structured and accessible DOM it can be a very useful feature.

I can understand this is a 'nice to have' OCD feature, rather than a priority, however I didn't find a registered issue about this so I made a new one.

Loving the concept of Svelte, and will be assessed in production in the coming weeks.

Most helpful comment

This is probably something we'd want to avoid doing, because Svelte's philosophy is that generated output should only include code that everyone needs or that implements functionality that can't be done outside the component. Otherwise, the cost of all these small features quickly adds up.

Just to be clear, when you say completely replace the target, do you mean that with a template like this...

<div>
  <!-- content goes here -->
</div>

...and a page like this:

<body>
  <main id='target'>
    <p>some content we want to get rid of</p>
  <main>
</body>

...the result after mounting should be this...

<body>
  <div>
    <!-- content goes here -->
  </div>
</body>

...and not this (i.e. just getting rid of existing contents)?

<body>
  <main id='target'>
    <div>
      <!-- content goes here -->
    </div>
  <main>
</body>

Either way, it could be achieved fairly easily with helper functions:

import App from './App.html';

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

  options.target.replaceWith( frag );
  // or `options.target.parentNode.replaceChild( frag, options.target )` in older browsers

  return component;
}

const app = replaceContainer( App, {
  target: document.querySelector( '#target' )
});
import App from './App.html';

function replaceContents ( node ) {
  node.innerHTML = '';
  return node;
}

new App({ target: replaceContents( document.querySelector( '#target' ) });

All 3 comments

This is probably something we'd want to avoid doing, because Svelte's philosophy is that generated output should only include code that everyone needs or that implements functionality that can't be done outside the component. Otherwise, the cost of all these small features quickly adds up.

Just to be clear, when you say completely replace the target, do you mean that with a template like this...

<div>
  <!-- content goes here -->
</div>

...and a page like this:

<body>
  <main id='target'>
    <p>some content we want to get rid of</p>
  <main>
</body>

...the result after mounting should be this...

<body>
  <div>
    <!-- content goes here -->
  </div>
</body>

...and not this (i.e. just getting rid of existing contents)?

<body>
  <main id='target'>
    <div>
      <!-- content goes here -->
    </div>
  <main>
</body>

Either way, it could be achieved fairly easily with helper functions:

import App from './App.html';

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

  options.target.replaceWith( frag );
  // or `options.target.parentNode.replaceChild( frag, options.target )` in older browsers

  return component;
}

const app = replaceContainer( App, {
  target: document.querySelector( '#target' )
});
import App from './App.html';

function replaceContents ( node ) {
  node.innerHTML = '';
  return node;
}

new App({ target: replaceContents( document.querySelector( '#target' ) });

Thanks for the swift reply, and yes, the cost would add up quickly so to following your philosophy for Svelte, I agree with your conclusion that this functionality should be kept outside.

By replace the target I did indeed mean that:

<body>
    <div id="mount-target-for-list"></div>
</body>

would become:

<body>
    <ul class="my-mounted-list-component">
        <li></li>
        <li></li>
    </ul>
</body>

I am looking at incremental introduction of Svelte into an existing platform, so a full view rewrite is not on the cards for a good while, plus I have OCD about unnecessary tags :laughing:

I was taking a look at the code earlier, and found that since Svelte components don't have to have a root tag, the complexity would be greater than I first thought to add the feature, and reading your example, also unnecessary.

Just a quick edit on your helper for anyone reading this later:

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

    options.target.replaceWith( frag );

    return component;
}

Thanks again.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Rich-Harris picture Rich-Harris  路  3Comments

davidcallanan picture davidcallanan  路  3Comments

Rich-Harris picture Rich-Harris  路  3Comments

robnagler picture robnagler  路  3Comments

thoughtspile picture thoughtspile  路  3Comments