· css targeting · 3 min read

How to Target HTML Elements based on Child Elements with CSS

Share:

In web development, the challenge of targeting a parent element based on a specific child can be overcome with the powerful :has pseudo-selector in CSS. The versatility extends to JavaScript, providing a seamless and consistent approach for element identification.

Have you ever run into a problem where you needed to target the parent of a specific element? This happens to me from time to time. Recently, I was tasked with making a sticky header for the top of a page in AEM (Adobe Experience Manager) and a sticky button bar for the bottom of the viewport.

The problem I ran into is that AEM doesn’t allow you to give an ID or class to wrapper elements. Because I needed these to be sticky across the visible area, I needed to target an element that is parallel to the content element. Thus, I had to target the parent.

The parent level of the header that is parallel to the content has a generic class of “experiencefragment” in my case. This is a common wrapper element class and in fact the footer and a floating button set also use this as the only identifier at that same level. Thus, not easy to target.

Here is the path to the element from the level that needs to be sticky to the level that can identify the correct element branch: “experiencefragment > cmp-container > header”.

How Do We Target The Parent Based On The Child

In my example, the child identifier is two levels down. How do we identify this? By using the CSS “:has” pseudo-selector. In my first attempt I was having troubles and ended up doing a chained drilling down by using this CSS:

.experiencefragment:has(.cmp-container):has(.header)

After experimenting with this some more I realized that I didn’t have to drill down from level to level. Instead, I was able to simply have it look for any child in the subtree with the correct identifier.

My improved CSS looks like this:

.experiencefragment:has(.header)

This ends up being very useful and powerful.

But That Is Not All

The usefulness of this isn’t just limited to CSS styles. You can also use it in JavaScript as well. Take this example in JavaScript, I used the same selector to identify the element in JavaScript that I would in CSS which is very handy.

const headerFragment = document.querySelector(
  ".page .root > .cmp-container > .experiencefragment:has(.header)"
);

In this example (and in my CodePen below) you will see that I specifically only target the “experiencefragment” elements at a certain level on the page. I did this to avoid a duplicate target in the overall structure of the site. While unlikely, it still can be good to be very specific at times.

The Power of “:not” with “:has”

As an addendum, you can also pair “:has” with the “:not” pseudo selector. You can select all elements that do not have a particular element within them.

Example:

div:not(:has(div)) {
  display: none;
}

In this example, I am selecting all divs that do not have any divs within them and hiding them. Fairly simple and easy to use.

To see how this looks in a more complex HTML tree, see my CodePen embed.

See the Pen Complex targeting with :has() by Patrick Thurmond (@pthurmond) on CodePen.

Conclusion

With a little bit of creative problem-solving, we can do some very complex things with some relatively simple CSS. I encourage you to experiment and try different ways to use “:has” and “:not” to select elements.

Share: