Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the dom node of a component in Svelte?

Say I render the following Dialog:

let dialog;

<MyDialog bind:this={dialog}/>

I want to position this dialog (using afterUpdate lifecycle function I guess). However, dialog is not a dom element. When I log it, it will look something like:

MyDialog {$$: {…}, left: 785, $set: ƒ, $capture_state: ƒ, $inject_state: ƒ}
$$: {fragment: {…}, ctx: Array(1), props: {…}, update: ƒ, not_equal: ƒ, …}
$capture_state: () => {…}
$inject_state: $$props => {…}
$set: $$props => {…}
someExportedProp: (...)
__proto__: SvelteComponentDev

How can I access its dom in order to position it?

like image 547
user2923322 Avatar asked Jun 09 '20 09:06

user2923322


1 Answers

MyDialog is not a DOM element, it is a Svelte component. And so, it's what you get when you bind:this on it: a Svelte component.

You only get a DOM element when binding to an actual element:

<div bind:this={el} />

If you need access to the DOM of MyDialog from its consumer (parent) component, you need to proxy it from the MyDialog component.

MyDialog.svelte

<script>
  export let el
</script>

<div class="dialog" bind:this={el}>
  <slot />
</div>

App.svelte

<script>
  import MyDialog from './MyDialog.svelte'

  let dialog

  $: console.log(dialog) // here, DOM element!
</script>

<MyDialog bind:el={dialog}>
  Hello!
</MyDialog>

That being said, I would think twice before leaking DOM elements outside of component. It kinda breaks encapsulation, which often ends up complicating your life... Maybe you can think of a better solution to control your dialog position without giving direct access to the innards of the component to the consumer?

EDIT

To move with props...

Props are "reactive", their value is automatically reflected in the DOM by the framework, so you don't need to run a function to move things. You can just declare what you want. This is the preferred style with these declarative frameworks.

<script>
  export let top
  export let left

  $: style = `top: ${top}px; left: ${left}px`
</script>

<div class="dialog" {style}>
  <slot />
</div>

If you need too, you can still run some code when props change (imperatively) with reactive blocks:

<script>
  export let top
  export let left

  let el

  // reactive block will rerun each time el, top, or left changes
  $: if (el) {
    el.style.top = top + 'px'
    el.style.left = left + 'px'
  }
</script>

<div class="dialog" bind:this={el}>
  <slot />
</div>
like image 176
rixo Avatar answered Oct 06 '22 07:10

rixo