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

Squiggle
  • 2,427
  • 1
  • 18
  • 22
SmallMan
  • 144
  • 1
  • 2
  • 11

3 Answers3

10

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>
        );
    }
}
Squiggle
  • 2,427
  • 1
  • 18
  • 22
  • 1
    Can't get when shadow: true option is set.How to get value from a shadow element!? – Suriya Kumar Jan 30 '19 at 07:52
  • @SuriyaKumar you mean it doesn't work in a scenario similar to above, but with `shadow: true` in the `@Component` decorator? – Squiggle Jan 30 '19 at 10:54
  • @SuriyaKumar can you share your code in a gist? or perhaps open a new SO question? – Squiggle Jan 30 '19 at 15:31
  • 5
    Note that the `componentDidLoad` restriction is not true (perhaps it was true in older versions of StencilJS). The host element defined using `@Element` is available from `componentWillLoad`. Other DOM elements in the template defined using `ref` are only available after the component has loaded i.e. `componentDidLoad`. – G. Tranter Mar 29 '19 at 13:45
  • 6
    @SuriyaKumar in case of `shadow: true` you have to access the shadow DOM through `this.element.shadowRoot`: https://stenciljs.com/docs/styling#things-to-remember-with-shadow-dom – matteo.cajani Nov 08 '19 at 12:34
  • 1
    BTW, why not add the extra info to Frenando's response, rather than add an new post to provide more details for the same solution? – Lee Goddard Apr 30 '20 at 10:48
  • @Squiggle PLEASE add what SuriyaKumar and G Tranter added so that you answer is complete – Prusio Nov 02 '22 at 11:33
7

From the official docs

In cases where you need to get a direct reference to an element, like you would normally do with document.querySelector, you might want to use a ref in JSX.

So in your case:

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;

  divElement!: HTMLElement; // define a variable for html element

  getElementHere() {
    this.divElement  // this will refer to your <div> element
  }

  render() {
    return (
      <div ref={(el) => this.divElement= el as HTMLElement}> // add a ref here
        Hello, World! I'm {this.first} {this.last}
      </div>
    );
  }
}
Manoj Singh
  • 1,911
  • 1
  • 20
  • 22
4

you can get the current HTML element adding this into your component as property:

@Element() myElement: HTMLElement;

You can read more about this here

Hope this helps you :)

Fernando Del Olmo
  • 1,440
  • 17
  • 32