How to get a DOM element in Stenciljs?

SmallMan picture SmallMan · Apr 21, 2018 · Viewed 9.2k times · Source
import { Component, Prop } from '@stencil/core';
@Component({
    tag: 'my-component',
    styleUrl: 'my-component.css',
    shadow: true
})
export class MyComponent {

  @Prop() first: string;
  @Prop() last: string;
  getElementHere() {
     // how can I get the div here?
  }
  render() {
    return (
      <div>
        Hello, World! I'm {this.first} {this.last}
      </div>
    );
  }
}

I want to get the DOM element just like in native JS. How do you do this in Stencil? getElementById does not work.

Answer

Squiggle picture Squiggle · Apr 25, 2018

To expand on Fernando's answer, the @Element decorator binds the component's root element to this property. It's important to note a few properties of this approach:

  1. The @Element bound property is only available after the component has been loaded (componentDidLoad).
  2. Because the element is a standard HTMLElement, you can access elements within your current component using the standard .querySelector(...) or .querySelectorAll(...) methods to retrieve and manipulate them.

Here is an example showing when the element is accessible, and how to manipulate nodes within this element (correct as of stencil 0.7.24):

import { Component, Element } from '@stencil/core';

@Component({
    tag: 'my-component'
})
export class MyComponent {

    @Element() private element: HTMLElement;
    private data: string[];

    constructor() {
        this.data = ['one', 'two', 'three', 'four'];
        console.log(this.element); // outputs undefined
    }

    // child elements will only exist once the component has finished loading
    componentDidLoad() {
        console.log(this.element); // outputs HTMLElement <my-component ...

        // loop over NodeList as per https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches/
        const list = this.element.querySelectorAll('li.my-list');
        [].forEach.call(list, li => li.style.color = 'red');
    }

    render() {
        return (
            <div class="my-component">
                <ul class="my-list">
                    { this.data.map(count => <li>{count}</li>)}
                </ul>
            </div>
        );
    }
}