简体   繁体   中英

click event not working on inner tag elements

This is the markup that I'm working with

<div class="checkout-row__form-column__billing">
  <div class="checkout-row__form-column__billing__title billing-collapse collapse-btn">
     <div class="checkout-row__form-column__billing__title__arrow">
       <i class="fa fa-chevron-right" aria-hidden="true"></i>
     </div>
   <h2>1. Billing Details</h2>
</div>
<div class="checkout-row__form-column__shipping__content shipping-collapse-content collapse-target"></div>
<div class="checkout-row__form-column__shipping">
  <div class="checkout-row__form-column__shipping__title shipping-collapse collapse-btn">
     <div class="checkout-row__form-column__shipping__title__arrow">
       <i class="fa fa-chevron-right" aria-hidden="true"></i>
     </div>
   <h2>2. Shipping Details</h2>
</div>
<div class="checkout-row__form-column__shipping__content shipping-collapse-content collapse-target"></div>

All this markup is wrapped in div with a class of checkout-row__form-column . So, I'm grabbing the parent wrapper, on click I match target with .collapse-btn . and then I do class toggle on the sibling which is .collapse-target . Works great except on issue, that I can't get past. If I click on the inner element, the event doesn't happen.

Here is my js

parent = document.querySelector('.checkout-row__form-column');
    parent.addEventListener('click', (e)=>{
      if (e.target.matches('.collapse-btn')) {
        e.stopPropagation();
        
        e.target.nextElementSibling.classList.toggle('hide');
      }
    });

With these code I am able to toggle classes if I don't click on i element or h2 . How can I write my JavaScript that event will be fired if I click anywhere inside the e.target .

Any help is much appreciated fellow commarades.

Your code only checks if the event.target is the .collapse-btn element or not. You also need to check if the clicked element is a descendant element of the .collapse-btn element.

To achieve the desired result, you can use the two conditions to check if the event.target is the .collapse-btn or any of its descendant. To check for descendant elements, use the universal selector * .

parent.addEventListener('click', (e) => {
  if (
       e.target.matches('.collapse-btn') || 
       e.target.matches('.collapse-btn *')
  ) {
     // code
  }
});

Demo

Following snippet shows an example:

 const parent = document.querySelector('.container'); parent.addEventListener('click', e => { if ( e.target.matches('.collapse-btn') || e.target.matches('.collapse-btn *') ) { parent.classList.toggle('active'); } });
 .container { background: #eee; width: 120px; height: 120px; } .container.active { border: 2px solid; } .collapse-btn { background: #999; padding: 10px; } .collapse-btn span { background: #fc3; font-size: 1.3rem; cursor: pointer; }
 <div class="container"> <div class="collapse-btn"> <span>Click</span> </div> </div>

You could also put the selectors in an array and then use .some() method to check if event.target matches ay of the selector in the array.

const selectors = ['.collapse-btn', '.collapse-btn *'];

parent.addEventListener('click', e => {
  if (selectors.some(s => e.target.matches(s))) {
    // code
  }
});

Demo

Following snippet shows an example:

 const parent = document.querySelector('.container'); parent.addEventListener('click', e => { const selectors = ['.collapse-btn', '.collapse-btn *']; if (selectors.some(s => e.target.matches(s))) { parent.classList.toggle('active'); } });
 .container { background: #eee; width: 120px; height: 120px; } .container.active { border: 2px solid; } .collapse-btn { background: #999; padding: 10px; } .collapse-btn span { background: #fc3; font-size: 1.3rem; cursor: pointer; }
 <div class="container"> <div class="collapse-btn"> <span>Click</span> </div> </div>

Alternative Solution

Instead of using the .matches() method, use the combination of Element.closest() and Node.contains() methods to check if the event.target is the .collapse-btn element or is a child element of the .collapse-btn element.

For this to work, you need to do two steps:

  1. You first need to get the .collapse-btn that is the closest ancestor of the event.target .

  2. After that, you need to check if the event.target is a descendant of the button selected in step 1.

Code:

parent.addEventListener('click', (e) => {
   const targetCollapseBtn = e.target.closest('.collapse-btn');

   if (targetCollapseBtn && targetCollapseBtn.contains(e.target)) {
       ...
   }
});

If event.target is the .collapse-btn itself, then

e.target.closest('.collpase-btn')

will return the e.target , ie .collapse-btn and

targetCollapseBtn.contains(e.target)

will also return true because .contains() method returns true even if the node passed to it as an argument is the same node as the one on which .contains() method was called.

So using .closest() and .contains() methods in this way covers both the use cases, ie when .collapse-btn is clicked or when any of its descendant element is clicked.

Demo

Following snippet shows a simple example:

 const parent = document.querySelector('.container'); parent.addEventListener('click', e => { const targetCollapseBtn = e.target.closest('.collapse-btn'); if (targetCollapseBtn && targetCollapseBtn.contains(e.target)) { parent.classList.toggle('active'); } });
 .container { background: #eee; width: 120px; height: 120px; } .container.active { border: 2px solid; } .collapse-btn { background: #999; padding: 10px; } .collapse-btn span { background: #fc3; font-size: 1.3rem; cursor: pointer; }
 <div class="container"> <div class="collapse-btn"> <span>Click</span> </div> </div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM