4

I am trying to understand what exactly is the difference between window and this in the context of a LWC component.

According to this link the Locker intercepts calls to the Window and uses Secured Window instead which acts as:

Secure wrapper for the window object, which represents a window containing a DOM document.

As for this what I found was:

To add an event listener to an element that a template doesn’t own, call addEventListener directly: this.addEventListener()

What does salesforce mean by an element that a template doesn't own? Isn't window an element that the template doesn't own?

  • +1. Although there is lot of theoritical documentation, there are no actual samples. Also unable to verify so far where to add event listener is possible only through this without template. (I checked this even when LWC was in pilot) – salesforce-sas Aug 12 at 23:50
5
  1. this refers to the default class and not window.

  2. Container component (not owner) can add event listeners and access elements directly on this and not template. So, we should use this.addEventListener and this.querySelector

  3. Owner component has to add event-listeners and identification through template. So, we should use this.template.addEventListener and this.template.querySelector

Please find below the example:

Consider 3 components Grandparent, parent, and child as below:

Grandparent.html:

<template>
    <div>Grandparent:</div>
    <c-parent>
        <span slot='myslot'>
            <c-child></c-child>
        </span>
    </c-parent>
</template>

Here, grandparent is the owner of c-child and parent component is only the container of child

Grandparent.js:

export default class Grandparent extends LightningElement {
    constructor() {
        super();
        console.log('this => ', this);
        this.addEventListener('myevent', this.myeventHandler);
        this.template.addEventListener('myevent', this.myeventHandlerTemplate);
    }
    renderedCallback() {
        console.log("Grandparent renderedCallback => ", this.querySelector('c-child'));
        console.log("Grandparent renderedCallback template => ", this.template.querySelector('c-child'));
    }
    myeventHandler(event) {
        console.log('Grand parent - myevent handled');
    }
    myeventHandlerTemplate(event) {
        console.log('Grand parent template - myevent handled');
    }
}

parent.html:

<template>
    <div>
        <slot name='myslot'></slot>
    </div>
</template>

parent.js:

export default class Parent extends LightningElement {
    constructor() {
        super();
        console.log('this => ', this);
        this.addEventListener('myevent', this.myeventHandler);
        this.template.addEventListener('myevent', this.myeventHandlerTemplate);
    }
    renderedCallback() {
        console.log("parent renderedCallback => ", this.querySelector('c-child'));
        console.log("parent renderedCallback template => ", this.template.querySelector('c-child'));
    }
    myeventHandler(event) {
        console.log('parent - myevent handled');
    }
    myeventHandlerTemplate(event) {
        console.log('parent template - myevent handled');
    }
}

We are adding event-listeners and query-selectors on both direct this and template to check the functionality in both parent and grandparent.

child.js:

export default class Child extends LightningElement {
    connectedCallback() {
        this.dispatchEvent(new CustomEvent('myevent', { bubbles: true }));
    }
}

bubbles:true and composed:false (default is false) will make the event bubble up until shadow boundary.


Below are the logs we get: (in order)

this =>  Grandparent {setAttribute: ƒ}
this =>  Parent {setAttribute: ƒ}
parent template - myevent handled
parent - myevent handled
Grand parent template - myevent handled
parent renderedCallback =>  c-child
parent renderedCallback template =>  null
Grandparent renderedCallback =>  null
Grandparent renderedCallback template =>  c-child
  1. As you see, this has returned class instance.

  2. querySelector for parent component worked only for direct this and returned null on template.

  3. Also for event listener, container component - parent listened on direct this. However, there appears to be some bug/unknown issue since container component is able to listen to the event on template.

  4. Only template binding worked in Grandparent.

0

the lexical scope is an own topic for itself and simple to much to explain in a simple forum post. Consider "this" as context in which your code / functions are running. That can be (dependent on how your code is called) refer to different things. Window represents the global context which is used in non-strict mode code when the caller does not explicitly set a context (e. g. via bind or this argument in call/ apply calls). In LWC the "this" context will be most of the times the instance of your respective component.

For some more detailed information on this topic you may want to check mdn

Window is not an element per se - its the global page context. But as you may know Salesforce (respective Lockerservice) does not want you to tamper around everywhere on the page which is the reason it restricts dom access to elements you not own etc. and why they recommend to not add listeners there because you should stick to your component instances.

An Example for elements not owned by a component in the context of LWC are e. g. elements which are passed as slots to a component - while they will be passed to the child component the parent component is still the owner (also see here at the bottom)

  • Components passed in slots - when they fire custom event, parent component is able to handle using template. I actually didnt find a scenario where parent element is able to add event listener without template. (able to verify with template though). Can you find a scenario where we are able to add event handler through ONLY this and not template? – salesforce-sas Aug 12 at 23:42
  • did not really played with such tings yet - but i guess in > 90% of the cases its this.template scenarios anyways so i think its not that super important and most of the times people will face the "this" topic when they adding / calling function in old es5 style without rebinding or using arrow functions – Renji-xD Aug 13 at 7:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.