Assigning numbers to child elements within individual parents

Hi there,

I’m attempting to assign a number to child elements depending on the number of elements within a set of containers.

For example, container 1 contains 2 elements so the child elements would be assigned numbers 1 & 2 respectively. Container 2 contains 3 elements so they would be assigned numbers 1, 2 & 3 respectively.

Here is what I’ve got so far but the code is targeting the child elements as a whole, rather than taking into account their containers. I imagine it’s a case of adjusting the targeting?

$('.container a.link').each(function (i) {
    $(this).data('linkNumber', { number: ++i }); 
    linkNumberIdent = 'link--' + 'child' + $(this).data('linkNumber').number;
    $(this).addClass(linkNumberIdent);
});

So far I’ve tried using children() to specify it, but it doesn’t work at all. My thought was that using children() would target each child element of the container.

$('.container').children('a.link').each(function (i) {
    $(this).data('linkNumber', { number: ++i }); 
    linkNumberIdent = 'link--' + 'child' + $(this).data('linkNumber').number;
    $(this).addClass(linkNumberIdent);
});

Please let me know if anyone has any thoughts!

Thanks in advance.

I’m not really sure what you’re trying to achieve, but this code:

$('#container > a').each((i, el) => {
  $(el).attr('data-linkNumber', i);
  $(el).addClass(`link--child${i}`);
});

will change this HTML:

<div id="container">
  <a href="https://www.sitepoint.com">SitePoint</a>
  <a href="https://www.sitepoint.com">SitePoint</a>
  <a href="https://www.sitepoint.com">SitePoint</a>
</div>

into this HTML:

<div id="container">
  <a href="https://www.sitepoint.com" data-linknumber="0" class="link--child0">SitePoint</a>
  <a href="https://www.sitepoint.com" data-linknumber="1" class="link--child1">SitePoint</a>
  <a href="https://www.sitepoint.com" data-linknumber="2" class="link--child2">SitePoint</a>
</div>

Is that what you want?

If not please post a snippet of the HTML you are working with and what you would like to change it into.

Ok great, thanks for your help!
This script will be used to identify specific links within each container (i.e. navigation) so that the clicks on the links can be tracked and pushed into analytics software. I need to be able to independently track the links, in relation to the container - for example container-one__link1. The number of links within the containers will vary and so the script will need to be dynamic.

Below is the setup I have:

<div id="container container--one">
    <a href="https://www.sitepoint.com">SitePoint</a>
    <a href="https://www.sitepoint.com">SitePoint</a>
    <a href="https://www.sitepoint.com">SitePoint</a>
</div>
<div id="container container--two">
    <a href="https://www.sitepoint.com">SitePoint</a>
</div>
<div id="container container--three">
    <a href="https://www.sitepoint.com">SitePoint</a>
    <a href="https://www.sitepoint.com">SitePoint</a>
    <a href="https://www.sitepoint.com">SitePoint</a>
    <a href="https://www.sitepoint.com">SitePoint</a>
</div>

Let me know if it needs any further clarification.

Ok, that’s the starting point.
What should the resultant HTML look like?

just a gotcha with custom data is that the data is a string even if it is a number.

What kind of number do you want to use that can’t be cast / coerced / converted ?

Hi @Shoxt3r, well you can’t assign multiple IDs to an element; actually, the whitespace is treated as part of the ID here. If you’re using classes instead however, the problem will be to identify the class that serves as the block part of the element class (e.g. container vs container--one) – especially since you’re using words for the containers, but actual numbers for the children.

If using numbers throughout is an option though, a nested each() should do the trick:

$('.container').each((containerIndex, container) => {
  $(container)
    .addClass(`container-${containerIndex}`)
    .children('a')
    .each((childIndex, child) => {
      $(child).addClass(`container-${containerIndex}__link-${childIndex}`)
    })
})

(While at it, I’d also add the corresponding container classes using JS to make sure the numbering matches.)

PS: For the purpose of dispatching analytics events, data attributes would indeed be more suitable IMO as are intended to store arbitrary data. Otherwise you’ll eventually face the same problem as with the container classes: how to identify the child class that holds the desired information.

1 Like

Whoops yes of course - I was replying in a bit of a rush before leaving the office! Have tried to edit but seems I’m not able to now.

id= should have been class=… :blush:

Thanks for the tip about the nested each. I actually gave that a go offline and it was working fine, but then couldn’t figure out how to mark each child element separately, but your “each child” lookup fits the bill nicely.

Agreed, I will look at using data attributes instead - what about using “.data” methods instead? One main reason for using the class-based method was carryover from another tagging script I wrote, which used the classes to mark the impressions (i.e. when an element with that class name was visible, it would do a data push to the Data Layer).

Any thoughts please let me know :smile:

The jQuery data() method is quite different from data-* attributes as it stores the data internally, rather than directly on the DOM element. The advantage is that you can store not just strings but any value; for example, you might store a reference to the actual container element (which would admittedly be a bit silly as this would just be the parentElement or parent() here). ^^

The disadvantage however is that this data can only be accessed using jQuery, and there is also the danger of memory leaks if the reference element is getting removed from the DOM by non-jQuery code. So for analytics events I would definitely go with data-* attributes as the data needs to be serializable anyway, and probably accessible to 3rd party code as well.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.