Getting ancestor of element instead of using document.body

Black Sulfur
Black Sulfur used Ask the Experts™
on
Instead of using
document.body.addEventListener('click', event => {

Open in new window

how can I get the most direct ancestor of all the elements that match .delete?


<% for (const products of pending) { %>
 <li>
    <a href="#" class="button gray delete" data-id="<%= products._id %>"><i class="sl sl-icon-close"></i> Delete</a>
 </li>
<% } %>

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Zakaria AcharkiAnalyst Developer
Distinguished Expert 2018

Commented:
Could you please give us more details about the context and what you're trying to achieve so we can provide a more specific solution.

Author

Commented:
Hi, Zakaria. Sorry for being vague. I am displaying a list of records from mongoDB using node.js and ejs. I can't use document.querySelector() because that just seems to only let me click on the first database record. If I click on the second, third etc. then nothing happens at all. Therefore I needed to use document.body so now clicking on any record's buttons actually triggers an event.

However, I don't think it is best practice to use document.body if I can target something closer to the actual element, but I don't know how to do it in vanilla javascript. That is my first problem. I didn't add my second problem to the initial question but have just discovered it. When I click I also want to hide ALL buttons but again it is only working for the first record and none of the records after that.

document.body.addEventListener('click', e => {

    e.preventDefault();
    renderLoader(spinner);
    const deleteBtn = e.target;
    const productId = deleteBtn.dataset.id;
    const btnSet = document.querySelector('.buttons-to-right');
    btnSet.style.visibility = 'hidden';
});

Open in new window

Zakaria AcharkiAnalyst Developer
Distinguished Expert 2018

Commented:
Ok you've described the problem well, could you please show us the HTML code structure sample, so we could know what is "buttons-to-right" and how it's related to the delete button.
Ensure you’re charging the right price for your IT

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden using our free interactive tool and use it to determine the right price for your IT services. Start calculating Now!

Author

Commented:
Sure!

                        <ul>
                            <% for (const products of pending) { %>
                            <li>
                                <div class="list-box-listing">
                                    <div class="list-box-listing-img"><a href="#"><img src="../<%= products.image %>" alt=""></a></div>
                                    <div class="list-box-listing-content">
                                        <div class="inner">
                                            <h3><a href="#"><%= products.title %></a></h3>
                                            <span><%= products.address.city %>, <%= products.address.suburb %></span>
                                            <div class="star-rating">
                                                <h5>Added <%= moment(products.createdAt).fromNow() %></h5>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="buttons-to-right">
                                    <a href="edit-listing/<%= products._id %>" class="button gray edit"><i class="sl sl-icon-note"></i> Edit</a>
                                    <a href="#" class="button gray delete" data-id="<%= products._id %>"><i class="sl sl-icon-close"></i> Delete</a>
                                 
                                </div>
                            </li>
                            <% } %>
                        </ul>

Open in new window

Author

Commented:
So, I tried this and it works for the second part of my question. But seems like overkill. Or is it correct?

    const btnSet = document.querySelectorAll('.buttons-to-right');
    btnSet.forEach(query => {
        query.style.visibility = 'hidden';
    });

Open in new window

Zakaria AcharkiAnalyst Developer
Distinguished Expert 2018

Commented:
No it's ok if you want to hide all the elements, it's the correct way.

I will submit a sample about the first part of your question in a while.
Zakaria AcharkiAnalyst Developer
Distinguished Expert 2018

Commented:
Last question to make sure I got you, do you want to hide the whole record when the user clicks on delete button?

Author

Commented:
No, just the buttons which sit on the record. So, visually you can think of it as 3 columns. Column one is the image, column 2 is some text about the item and column 3 consists of the 2 buttons, edit and delete. When a user clicks on 'delete' I just want the buttons to hide so the user can't click on them while the request is happening in the background via axios. Then when there is a response sent back the buttons can be shown again if necessary.
Zakaria AcharkiAnalyst Developer
Distinguished Expert 2018

Commented:
I got you,

The only way to attach an event to the new DOM elements added dynamically to the document is by using the event delegation event, so you need to attach the event to a constant element on the page (the must of times we chose body), but you need to control this event by condition since it will be attached to all the elements of your body that why we use the if statement like :

document.body.addEventListener('click', e => {
  const deleteBtn = e.target;
  
  if (deleteBtn.classList.contains("delete")) {
    e.preventDefault();

    deleteBtn.parentNode.style.visibility = 'hidden';
  }
});

Open in new window


Here is a very basic sample :

https://jsfiddle.net/z_acharki/rm0xg7dv/

Author

Commented:
Okay, I just wondered if there was an alternative to 'body' because I read this:

Instead of body you should use the most direct ancestor of all the elements that match .delete so you're not wasting time running a handler for a lot of irrelevant click events.
Analyst Developer
Distinguished Expert 2018
Commented:
Yes in your case it could be the ul element if you're sure that it will be always there in your code, like :

document.querySelector('ul').addEventListener('click', e => {

Open in new window

Author

Commented:
Yes, this is what I am looking for!

document.querySelector('ul').addEventListener('click', e => {

Open in new window


Except for that nothing happens when I click the buttons and there is no error in console. Will querySelector work with multiple dynamically added records though?
Zakaria AcharkiAnalyst Developer
Distinguished Expert 2018

Commented:
Yes, it should, here is an example of dynamically adding li's to the DOM after two seconds, you could see that the event work since the ul was there all the time.

https://jsfiddle.net/z_acharki/qdaz8rx7/

Author

Commented:
Okay, I took a look at your jsfiddle but when you click on the delete button it only hides it from that list item. I want it to hide ALL the buttons from every list item so that the user can't interrupt the current process.
Zakaria AcharkiAnalyst Developer
Distinguished Expert 2018

Commented:
You could use the code you already find by yourself, the logic will be like :

https://jsfiddle.net/z_acharki/qdaz8rx7/3/

if (deleteBtn.classList.contains("delete")) {
  e.preventDefault();

  document.querySelectorAll('.buttons-to-right').forEach(button => {
      button.style.visibility = 'hidden';
  });
}

Open in new window

Author

Commented:
I see it working in you fiddle but doesn't work in my code. I click on the delete buttons and nothing happens. I will have to play around with it some more to see if I can find the issue.
Zakaria AcharkiAnalyst Developer
Distinguished Expert 2018

Commented:
O try to start with sample like :

document.querySelector('ul').addEventListener('click', e => {
   alert('clicked');
)}

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial