Paul
Paul

Reputation: 178

Accessing generated Custom Element in Svelte 3

I'm using Svelte 3 with the option to generate Custom Elements with Shadow DOM but I can't figure out how to get a reference to the wrapper Custom Element (HTMLElement) so that I can attached event handlers and manipulate attributes. I have something like this:

<svelte:options tag="custom-button"/>

<script>
    import { onMount } from 'svelte';

    function _onClick(e) {
        this.setAttribute('pressed', null);
    }

    var _boundClick = _onClick.bind(this);

    onMount((e) => {
        this.addEventListener('click', _boundClick);

        return () => {
            this.removeEventListener('click', _boundClick);
        };
    });
</script>

<style>
  :host {
    display: block;
  }

  /* Other Styling */
</style>

<slot></slot>

The "this" bits don't work (they do in a normal vanilla Custom Element). Is there some Svelte specific way of getting a reference to the host element in a script?

Thanks

Upvotes: 1

Views: 3227

Answers (4)

Brian
Brian

Reputation: 381

There is a good solution for this now, in the issue thread that Rich Harris linked to above.

You can use customElement's "extend" option to accomplish this:

<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>

Copied from this comment by "dummdidumm": https://github.com/sveltejs/svelte/issues/3091#issuecomment-1642351615

Upvotes: 0

Pedro Ruiz
Pedro Ruiz

Reputation: 1

One possible solution is to use the get_current_component function of svelte/internal

import { get_current_component } from 'svelte/internal';

let host = get_current_component();

I think that exporting this function would be very useful.

Upvotes: 0

Rich Harris
Rich Harris

Reputation: 29615

At present this isn't possible directly, though it would be a worthwhile addition. I've just raised an issue.

The indirect approach would be to do bind:this={element} on the top-level element inside your custom element (assuming you have one), then you could do something long the lines of $: host = element && element.parentNode.host. You wouldn't have access to it upon initialisation, but it would be ready in onMount.

Upvotes: 8

Nathan McFarland
Nathan McFarland

Reputation: 21

I think you want to create an element that surrounds the slot, and then bind that to a variable using bind:this={var} as is shown in this example: https://svelte.dev/docs#Binding_a_DOM_node

so something like

<custom-button bind:this={customButton}>
<slot></slot>
</custom-button>

Then use that bound variable(customButton) that in the javascript, instead of this like so:

let customButton;

function _onClick(e) {
        customButton.setAttribute('pressed', null);
}

And so on

Upvotes: 1

Related Questions