0

I have included a third party custom tag, and loading one external js file, the js file is appending few more elements to it. I tried to bind a click event for the element. But not working.

<!-- 3rd party custom element -->
<pin-location></pin-location>

After loading external js file, 3rd party plugin js file is appending few more elements like below

<each-pin><p>Pin1</p></each-pin>
<each-pin><p>Pin2</p></each-pin>

when i click on the paragraph i want send some analytics without removing existing event handlers from the paragraph.

I have tried below

customElements.get('pin-location', class extends HTMLElement {
            connectedCallback() {
                alert("hi");
                this.attachShadow({mode: 'open'});
                this.shadowRoot.firstElementChild.onclick = e => alert("Inner target: " + e.target.tagName);
            }
 });
connexo
  • 53,704
  • 14
  • 91
  • 128
Pioter
  • 465
  • 3
  • 8
  • 21

2 Answers2

0

This is the approach you need. For demo purposes I've simulated the custom elements you've mentioned, the relevant part for you is the last IIFE.

customElements.define('pin-location', class extends HTMLElement {
  constructor() {
    super(); 
    this.attachShadow({mode: 'open'});
  }
});

customElements.define('each-pin', class extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
    const p = document.createElement('p');
    p.textContent = 'Paragraph here';
    this.shadowRoot.appendChild(p);
  }
});

// This simulates the third party JS you mentioned
(function() {
  const eachPin = document.createElement('each-pin');
  document.querySelector('pin-location').shadowRoot.appendChild(eachPin);
}());

// here's how you attach an additional click listener to the paragraph
(function() {
  const pinLocations = document.querySelectorAll('pin-location');
  for (const pinLocation of pinLocations) {
    const allPins = pinLocation.shadowRoot.querySelectorAll('each-pin');
    for (const eachPin of allPins) {
      eachPin.shadowRoot.querySelector('p').addEventListener('click', e => {
        console.log(e.target);
      })
    }
  }
}());
<pin-location></pin-location>

Please note that as of now, you cannot access closed shadow roots this way. This is about to change with the introduction of declarative shadow DOM.

connexo
  • 53,704
  • 14
  • 91
  • 128
0
customElements.get('pin-location', class extends HTMLElement {
  connectedCallback() {
     alert("hi");
     this.attachShadow({mode: 'open'});
     this.shadowRoot.firstElementChild.onclick = e => alert("Inner target: " + e.target.tagName);
  }
 });

Your code (above) uses customElements.get(); which checks if a Custom Element pin-location is defined, it returns the definition, not the DOM element. That second parameter (class) does nothing

You can .define a Custom Element with:

<each-pin id=ONE   ></each-pin>
<each-pin id=TWO   ></each-pin>
<each-pin id=THREE ></each-pin>

<script>
  customElements.define('each-pin', class extends HTMLElement {
    constructor() {
      const p = document.createElement('p');
      p.textContent = 'Paragraph here';
      p.onclick = (evt) => alert("You clicked: " + this.id);

      super() // sets and returns this-scope
        .attachShadow({mode: 'open'}) // sets and returns this.shadowRoot 
        .append(p); // no need for appendChild 
    }
    // addListener( type, func) { ... }
  });
</script>
  • Keep Components and behaviour together; if you add listeners from other code then add least do it with a addListener method on the Component, so the Component can track and clean up listeners.

  • You want to use the constructor because the connectedCallback can run multiple times (when the element is moved around in the DOM)

  • It is your Component, you are in charge, so it is perfectly valid to use the 'oldskool' inline onclick event handler instead of addEventListener

  • Default events like click bubble; but do read all of the MDN documentation when sending (Custom)Events:
    https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49