2

I have a web component (Polymer 3) that is embedded in other components and want to "sense" clicks outside of it. For example, in React I would do something like this inside of the component:

document.addEventListener('click', this.handleClickOutside, true);

 public handleClickOutside = event => {
        const domNode = ReactDOM.findDOMNode(this);

        if (!domNode || !domNode.contains(event.target)) {
           //logic
        }
    }

Above we simply add a click event, and then check if the event have targeted the current component via a reference.

As its "react syntax" I need to to something similar but for web components. I need a reference to the current web component. I have tried:

 const domNode = this;

And

const domNode = this.$['my-component'];

without luck. Any help?

Arte2
  • 309
  • 4
  • 19

2 Answers2

3

Technically, you couldn't. I do something like it, some time ago.

You could listen when a click is done in the rest of dom and act. Use :not css selector, or just, simply, listen all clicks and compare:

//listen clicks
window.addEventListener('click', function(event){   
  //check if click was outside of #myObject
  if !(document.getElementById('myObject').contains(event.target)){
    //Only if click outside...
  }
});
1

const domNode = ReactDOM.findDOMNode(this);

LOL, "Let's query the whole (virtual)DOM"
and they still claim React is fast?

Reverse your logic, detect clicks inside your component.

It is 2021, we no longer support IE

Event.composedPath() gives you all DOM nodes the click Event passed.

And do read the focusout Event and :focus-within CSS answers
at: How do I detect a click outside an element?

I added some nested shadowDOMs to show composedPath gives you all DOM nodes.

Remember: CustomEvents need composed:true as option setting, for the Event to escape shadowDOM.
Native Events like click always escape shadowDOM

And remember... React never supported the native Event model.
https://custom-elements-everywhere.com/libraries/react/results/results.html
You have to write a React wrapper for each and every native Web Component
That is like buying a can of soup, and always having to buy a canopener with it.

<style>
  body { cursor:pointer }
  ::part(shadowdiv) { /* modern CSS shadowParts to style 'parts' inside shadowDOM */
    color: white;
    font: 14px Arial;
    width: 80%;
  }
</style>

<custom-element></custom-element>

<script>
  window.CEdepth = 0;
  window.addEventListener("click",e=>console.clear());
  customElements.define("custom-element", class extends HTMLElement {
    constructor() {
      super().attachShadow({mode:'open'});
      if (window.CEdepth++ < 3) {
        this.id = window.CEdepth;
        window.addEventListener("click", (evt) => {
          let inside = evt.composedPath().includes(this);
          console.log("clicked inside #"+this.id, ":", inside);
        });
        this.style.display = "inline-block";
        this.style.padding = "1em";
        this.style.background = ["red", "blue", "green"][this.id - 1];
        this.shadowRoot.innerHTML = `<div part='shadowdiv'>Web Component #${this.id}`
                                   +`<custom-element><b/></custom-element><div/>`;
      }
    }
  })
</script>
Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49