Anchor elements (<a>
) are created when the user interacts with a web component. The problem is, that I cannot get the anchor element returned from the "outside" of the web component when an anchor is clicked.
I add an event listener to document
listening for click events. When an element somewhere in the DOM is clicked I expect the e.target
to be the clicked element.
In the case of a click somewhere inside the web component the custom element (<fancy-list></fancy-list>
) will be returned - not the clicked element.
When the mode of the shadow DOM is set to open
the DOM should be accessible.
class Fancylist extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const wrapper = document.createElement('div');
wrapper.innerHTML = `<ul></ul><button>Add item</button>`;
shadow.appendChild(wrapper);
this.on_root_click = this.on_root_click.bind(this);
}
connectedCallback() {
this.ul_elm = this.shadowRoot.querySelector('ul');
this.shadowRoot.addEventListener('click', this.on_root_click, false);
}
on_root_click(e){
switch(e.target.nodeName){
case 'BUTTON':
this.ul_elm.innerHTML += '<li><a href="p1">List item</a></li>';
break;
case 'A':
e.preventDefault();
console.log('You clicked a link!');
break;
}
}
}
customElements.define('fancy-list', Fancylist);
<!DOCTYPE html>
<html>
<head>
<title>List</title>
<meta charset="utf-8" />
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', e => {
document.body.addEventListener('click', e => {
//console.log(e.composedPath());
console.log(e.target); // why is this not returning an anchor element when an anchor is clickend inside the <fancy-list>?
}, false);
}, false);
</script>
</head>
<body>
<h1>List</h1>
<fancy-list></fancy-list>
</body>
</html>
currentTarget is only available while the event is being handled. If you console. log() the event object, storing it in a variable, and then look for the currentTarget key in the console, its value will be null . Instead, you should either use console.
Definition and Usage The target event property returns the element that triggered the event. The target property gets the element on which the event originally occurred, opposed to the currentTarget property, which always refers to the element whose event listener triggered the event.
When an event is fired, the element that fires the event is known as the emitter. This element is what we call the target. So, the target property of that event object refers to the event emitter.
The purpose of the Shadow DOM is precisely to mask the HTML content the Shadow DOM from the containter point of view.
That's also why inner events are retargeted in order to expose the Shadow DOM host.
However, you can still get the real target by getting the first item of the Event.path
Array property.
event.path[0]
NB: of course it will work only with open
Shadow DOM.
class Fancylist extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const wrapper = document.createElement('div');
wrapper.innerHTML = `<ul></ul><button>Add item</button>`;
shadow.appendChild(wrapper);
this.on_root_click = this.on_root_click.bind(this);
}
connectedCallback() {
this.ul_elm = this.shadowRoot.querySelector('ul');
this.shadowRoot.addEventListener('click', this.on_root_click, false);
}
on_root_click(e){
switch(e.target.nodeName){
case 'BUTTON':
this.ul_elm.innerHTML += '<li><a href="p1">List item</a></li>';
break;
case 'A':
e.preventDefault();
break;
}
}
}
customElements.define('fancy-list', Fancylist);
<!DOCTYPE html>
<html>
<head>
<title>List</title>
<meta charset="utf-8" />
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', e => {
document.body.addEventListener('click', e => {
console.log(e.path[0]);
}, false);
}, false);
</script>
</head>
<body>
<h1>List</h1>
<fancy-list></fancy-list>
</body>
</html>
Update 2021
As commented now you should use event.composedPath()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With