svelte icon indicating copy to clipboard operation
svelte copied to clipboard

$$host (or some way to access custom element instance)

Open Rich-Harris opened this issue 6 years ago • 13 comments
trafficstars

From this Stack Overflow question — it might be nice if there was a way to get a reference to the host element, when compiling to custom elements (i.e. the <my-thing> for tag="my-thing").

Following the precedent set by $$props, we could add a $$host value. The main question is what should happen when not compiling as a custom element — error? or reference to the instance (which we've resisted thus far, because 99% of the time when someone wants that reference it's to do something inadvisable)?

Rich-Harris avatar Jun 24 '19 12:06 Rich-Harris

+1

vogloblinsky avatar Jun 26 '19 12:06 vogloblinsky

+1

a3con avatar Jul 05 '19 07:07 a3con

+1

vaheqelyan avatar Jul 10 '19 10:07 vaheqelyan

@Rich-Harris do you want a PR on that ?

vogloblinsky avatar Jul 10 '19 10:07 vogloblinsky

+1 this could be a great addition as it would enable many cool features

linorabolini avatar Oct 08 '19 20:10 linorabolini

Has there been any more thought on this?

I'd appreciate it if there were a 'reference to the instance'.

I like to organise my Svelte projects with a separation of concerns. The structure (the component template) is mostly separate from the behaviour (default data, lifecycle handlers, event handlers).

As well as being an organisational preference, this approach also integrates well with TypeScript, affording a great degree of safety and IDE integration to my behaviour code.

I was able to do this in Svelte 1 and 2, but this is not currently possible in Svelte 3.

I don't think I'm trying to do anything too funky. I understand that Svelte has become asynchronous. I'm happy to use promises to read and write component properties. I'd just like to be able to write my behaviour outside the component template itself.

If this could be made possible, it would be much appreciated.

ChrisTalman avatar Feb 11 '20 16:02 ChrisTalman

I'm using Stencil to create portable custom elements and I really found the way host elements are exposed: https://stenciljs.com/docs/host-element

$$host is being added with #4534 and this is great but do you think adding a svelte:host may be usefull?

adriengibrat avatar Mar 14 '20 18:03 adriengibrat

I'm building a web component and was desperately needing this. Turns out you can get a reference to the component with get_current_component.

  import { get_current_component } from "svelte/internal";

  const thisComponent = get_current_component();

madupuis90 avatar Jun 03 '21 21:06 madupuis90

I'm building a web component and was desperately needing this. Turns out you can get a reference to the component with get_current_component.

  import { get_current_component } from "svelte/internal";

  const thisComponent = get_current_component();

yea i use

import {get_current_component} from "svelte/internal";
const component = get_current_component();
component.shadowRoot 

vospascal avatar Jul 03 '21 12:07 vospascal

or reference to the instance (which we've resisted thus far, because 99% of the time when someone wants that reference it's to do something inadvisable)?

I think this can also be useful to do some inter-Component communication.

I known it's unsafe to use like that because it can break in the future, but it's actually possible via a trick using arguments :

<script lang="ts">
    const self = arguments[0];
</script>

Exemple here where i register the component instance to a parent component : https://svelte.dev/repl/46b37e50c482489681925f53312632c4?version=3.57.0

  • Details.svelte is a simple component that wrap a HTML details/summary. It's pretty simple and only provide some accessor, but it also registers itself into a possible parent (via the context).

  • DetailsGroup.svelte is a wrapper component that handle the <Details> childs that registered. It use the component to register event and manage his props via the accessors.

Details.svelte is mostly independant from DetailsGroup.svelte, but it can be manipulated by the latter for a specific need.

adiguba avatar Mar 18 '23 08:03 adiguba

Perhaps we can even use this for that, as it's currently undefined :

<script lang="ts">
    console.log(this); // "this" should be the current component instance
</script>

adiguba avatar Mar 18 '23 08:03 adiguba

@adiguba

I would definitely agree with you that this comes much naturally to someone who has been writing web components.

Maybe this being a reserved keyboard would be a problem? Going with the svelte guidelines $$this would be better?

tronicboy1 avatar Apr 14 '23 23:04 tronicboy1

I sense confusion above. My interpretation of @Rich-Harris's description is that he wants a reference to the HTMLElement instance, not the component itself.

That said: This varies slightly now in Svelte 4 since you can use shadow root or the light DOM, depending on your customElement options. So, if you're interested in a hack that might work, then this inelegant Satanic incantation might get you where you need to go:

<svelte:options
	customElement={{
		tag: 'example-element',
		shadow: 'none', // Or comment out to enable shadow DOM
	}}
/>

<script>
	import { onMount } from 'svelte';
	let el;
	onMount(() => {
		const hostEl = el.getRootNode().host || el.parentNode;
		console.log('element:', hostEl); // element: <example-element></example-element>
	});
</script>

<!-- should be at top level of the component -->
<div bind:this={el}></div>

In the shadow DOM, you could access it by getting a reference to an element and then calling el.getRootNode().host. For the light DOM (Svelte 4) then, from a root level element, use el.parentNode. Not that you should do this. I'm guessing you probably shouldn't do it, but... if you had to, this might be one way. 😅

patricknelson avatar Jul 13 '23 01:07 patricknelson

Documentation here (link for the lazy): https://svelte.dev/docs/custom-elements-api#component-options

patricknelson avatar Jul 18 '23 23:07 patricknelson

You can now pass the host element along using the new extend option:

<svelte:options
  customElement={{
    tag: 'custom-element',
    extend: (customElementConstructor) => {
      return class extends customElementConstructor {

        constructor() {
          super();
          this.host = this; // or this.shadowRoot, or whatever you need
        }
      };
    }
  }}
/>

<script>
  export let host;
</script>

...

dummdidumm avatar Jul 19 '23 15:07 dummdidumm