简体   繁体   中英

Vanilla JS: How to get index of parent element with a certain class?

In a nutshell :

On click of a link below in the given HTML, I need to get the index of the parent with class "analytics". For example, clicking the link "myID2 Link 1" would return 1.

<ul id="myID1" class="analytics" data-attribute="hpg:shocking">
  <li><a href="http://myurl.com/">myID1 Link 1</a></li>
  <li><a href="http://myurl.com/">myID1 Link 2</a></li>
  <li><a href="http://myurl.com/">myID1 Link 3</a></li>
</ul>
<ul id="myID2" class="analytics" data-attribute="hpg:expected">
  <li><a href="http://myurl.com/">myID2 Link 1</a></li>
  <li><a href="http://myurl.com/">myID2 Link 2</a></li>
  <li><a href="http://myurl.com/">myID2 Link 3</a></li>
</ul>
<ul id="myID3" class="analytics" data-attribute="hpg:unexpected">
  <li><a href="http://myurl.com/">myID3 Link 1</a></li>
  <li><a href="http://myurl.com/">myID3 Link 2</a></li>
  <li><a href="http://myurl.com/">myID3 Link 3</a></li>
</ul>

What I've tried :

I'm currently using two separate loops, one for iterating through the parents, the other for adding a click event to the links.

// helper function to check parent for a class
function hasParentWithClass(element, classname) {
  if (element.className.split(' ').indexOf(classname) >= 0) {
    return true;
  }
  return element.parentNode && hasParentWithClass(element.parentNode, classname);
}

// get all elements with class "analytics"
var analytics = document.querySelectorAll('.analytics');
var containerClass = [];

for (var z = 0; z < analytics.length; z++) {
  var inc = z + 1;
  containerClass.push(analytics[z].getAttribute('id'));
}

var links = analytics.getElementsByTagName('a');

for (var i = 0; i < links.length; i++) {
  links[i].addEventListener('click', function(e) {
    e.preventDefault();

    if (hasParentWithClass(this, 'analytics')) {
    // output index of parent
    } else {
    // do nothing
    }
  }, false);
}

The problem :

I'm struggling with using the logic from the first loop inside the second loop. Specifically, searching var containerClass from within the second for loop to get the index of the parent, as it's stored in var containerClass. .indexOf did not serve me well.

What is the most straightforward, efficient way to accomplish this?

This is much simpler than you think. If we assign a click event handler to the parent elements we can use event bubbling to solve the issue. When you click a link, it will not only trigger the click event for the link you clicked, but that event will "bubble" up through the ancestor elements of that link as well. If we capture the event at the parent, we can just get its index.

Also, as @Dave Thomas points out, your HTML structure for your list(s) is not valid.

 // Get all the .analytics elements var analytics = document.querySelectorAll(".analytics"); // Loop through them Array.prototype.slice.call(analytics).forEach(function(item, index, arry){ // Set up click event handlers for each of the parents item.addEventListener("click", function(evt){ // Just access the index of the parent within the group console.clear(); console.log("The index of the parent is: " + index) // Now, display the index of the link within its parent .analytics element // Get all the anchors in the current analytics container var anchors = Array.prototype.slice.call(item.querySelectorAll("a")); // Get the index of the anchor within the set of anchors var idx = Array.prototype.indexOf.call(anchors, evt.target); console.log("The index of the link within the parent is: " + idx); }); });
 <div id="myID1" class="analytics" data-attribute="hpg:shocking"> <ul> <li><a href="#">myID1 Link 1</a></li> <li><a href="#">myID1 Link 2</a></li> <li><a href="#">myID1 Link 3</a></li> </ul> </div> <div id="myID2" class="analytics" data-attribute="hpg:expected"> <ul> <li><a href="#">myID2 Link 1</a></li> <li><a href="#">myID2 Link 2</a></li> <li><a href="#">myID2 Link 3</a></li> </ul> </div> <div id="myID3" class="analytics" data-attribute="hpg:unexpected"> <ul> <li><a href="#">myID3 Link 1</a></li> <li><a href="#">myID3 Link 2</a></li> <li><a href="#">myID3 Link 3</a></li> </ul> </div>

Here is another way:

document.querySelectorAll('li').forEach((item) => {
    item.addEventListener('click', (e) => {
        const index = Array.from(item.parentNode.children).indexOf((item))
        console.log(index);
    })
})

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