$lis = document.getElementsByTagName('li');
for (let i = 0; i < $lis.length; i ) {
$li = $lis[i];
$span = $li.getElementsByTagName('span')[0];
$button = $li.getElementsByTagName('button')[0];
$button.addEventListener('click', $li.remove);
}
<ul>
<li>
<span>1</span>
<button>x</button>
</li>
</ul>
On the last line I'm using Element.remove()
method, but instead of deleting the <li>
it deletes <button>
.
Explain to me, please, why. I feel that there is something with this
happening, but I want to understand this completely not just feel.
P.S. I also understand that I can solve this by wrapping the $li.remove
in an anonymous function, but the point of my question is to understand described behaviour not just to solve the problem. Thanks.
CodePudding user response:
It's because the button because the new context or this
that Element.remove()
is called with. An anyonmous function, or more accurately, an arrow function, uses the lexical scope that it's declared in as the context, that's why it works.
So button.addEventListener('click', elementX.remove)
actually takes the prototype function remove
and calls it with the button as the context or in other words, it bind
s it to the button.
A way to demonstrate binding can work, is by binding remove
to the element, then adding it as the event handler $li.remove.bind($li)
this way, regardless of the implicit context, we've explicitly set it to $li
.
$lis = document.getElementsByTagName('li');
for (let i = 0; i < $lis.length; i ) {
$li = $lis[i];
$span = $li.getElementsByTagName('span')[0];
$button = $li.getElementsByTagName('button')[0];
let remove = $li.remove.bind($li)
$button.addEventListener('click', remove);
}
<ul>
<li>
<span>1</span>
<button>x</button>
</li>
</ul>
CodePudding user response:
In this line $button.addEventListener('click', $li.remove);
you are passing the $li
's remove
method as your click handler. The nature of click handlers (and other user-event handlers) is to rebind this
in the handler function to the element you attach the listener to, so what really happens is
$li.remove.call($button)
You can fix it using bind
:
$button.addEventListener('click', $li.remove.bind($li))
Sidenote:
Replace the live HTMLCollection
document.getElementsByTagName('li')
with a static NodeList
by using document.querySelectorAll('li')
.
Removing an element from the DOM will also immediately remove it from any HTMLCollection
it is part of, messing up your indexes.