CSS Parent/Ancestor Selector

CSS Parent/Ancestor Selector

There is no such thing as parent selector in CSS2 or CSS3. And there may never be, actually, because the whole "Cascading" part of CSS is not going to be pretty to deal with once you start doing parent selectors.

That's what jQuery is for :-)

Is there a CSS parent selector?

There is currently no way to select the parent of an element in CSS in a way that works across all browsers.

That said, the Selectors Level 4 Working Draft includes a :has() pseudo-class that will provide this capability. It will be similar to the jQuery implementation.

li:has(> a.active) { /* styles to apply to the li tag */ }

As of 2022, it is only supported by Safari, and by Chromium browsers behind a flag.

In the meantime, you'll have to resort to JavaScript if you need to select a parent element with full cross-browser support.

Is there a CSS parent selector?

There is currently no way to select the parent of an element in CSS in a way that works across all browsers.

That said, the Selectors Level 4 Working Draft includes a :has() pseudo-class that will provide this capability. It will be similar to the jQuery implementation.

li:has(> a.active) { /* styles to apply to the li tag */ }

As of 2022, it is only supported by Safari, and by Chromium browsers behind a flag.

In the meantime, you'll have to resort to JavaScript if you need to select a parent element with full cross-browser support.

CSS Selector-How to locate Parent Element

Referring to the Is there a CSS parent selector? topic, there is no parent selector in CSS. Leave the "getting the parent" part to the XPath:

WebElement we = dr.findElement(By.cssSelector("div[id='gf-BIG']"));
WebElement parent = we.findElement(By.xpath(".."));

Will future versions of CSS support parent or ancestor selector?

Yes. In CSSv4 will be the :has() pseudoselector. The problem with parent elements is old, and your suggestion is wide known:

 element < parent {} 

Will have a serious problem. Browser reads the CSS from right to left, so this kind of selectors will be low performance.

Related link (suggestion in year 2008):

  • http://shauninman.com/archive/2008/05/05/css_qualified_selectors

To achieve that, in CSS4 we can do:

 element:has(.parent) {}

And this will be perfect! It can select an element that is contained by the .parent selector. We can do more things like:

 element:not(:has(.parent)) {}
element:has(#myId > .myClass) {}

This is currently not supported by any browser. It's a working draft and will came with CSS level 4.

Related links:

  • https://snook.ca/archives/html_and_css/css-parent-selectors
  • https://remysharp.com/2010/10/11/css-parent-selector
  • https://developer.mozilla.org/en-US/docs/Web/CSS/:has
  • https://dev.w3.org/csswg/selectors4/#relational

While this awesome selectors are not available to general use, you must to do it with Javascript or thinking about another HTML structure to avoid the needed of parent selector.

EDITION:

I've found right now a polyfill that allows you to target parent elements with CSS. It's written in jQuery but it translates the CSS content,so you don't need to make anything to make it works (apart of plugin inclusion)

  • https://github.com/Idered/cssParentSelector

Quick Info extracted from the plugin page:

! - determines subject of selector according to CSS4 reference

E > F - selects an F element, child of E

E! > F - selects an E element, parent of F

CSS4 reference

In the past, this syntax was used to develop the parent element selector, but due performance reasons it was discarded. Here you are one link explaining it:

  • http://red-team-design.com/css-parent-selector/

Alternative technique

It's a technique that allows you to think different in pure CSS to achieve the same functionality (stylize parent elements). You can see in this link:

  • https://escss.blogspot.com/2014/02/parent-selector-pure-css.html

It explains how to select parent elements without weird things, plugins, or polyfills. It's only pure CSS but with a expensive thinking behind.

CSS selector for a child element whose parent element has a certain class

To select strong elements that are descendants of an element with class commandBar, use the descendant combinator along with a class selector:

.commandBar strong

In order to only select direct children strong elements, use the child combinator, >:

.commandBar > strong

Depending on your markup, you may also want to specify the element type that has the class .commandBar (in this case, div):

div.commandBar strong

CSS negation pseudo-class :not() for parent/ancestor elements

Doesn't this read, "Select all h1 elements that have an ancestor that is not a div element...?"

It does. But in a typical HTML document, every h1 has at least two ancestors that are not div elements — and those ancestors are none other than body and html.

This is the problem with trying to filter ancestors using :not(): it just doesn't work reliably, especially when the :not() is not being qualified by some other selector such as a type selector or a class selector, e.g. .foo:not(div). You'll have a much easier time simply applying styles to all h1 elements and overriding them with div h1.

In Selectors 4, :not() has been enhanced to accept full complex selectors containing combinators, including the descendant combinator. Whether this will be implemented in the fast profile (and thus CSS) remains to be tested and confirmed, but once it is implemented, then you will be able to use it to exclude elements with certain ancestors. Due to how selectors work, the negation has to be done on the element itself and not the ancestor in order to work reliably, and therefore the syntax will look a little different:

h1:not(div h1) { color: #900; }

Anyone who's familiar with jQuery will quickly point out that this selector works in jQuery today. This is one of a number of disparities between Selector 3's :not() and jQuery's :not(), which Selectors 4 seeks to rectify.

Select common parent/ancestor of nested elements with jQuery and CSS Selector

First, you need to ensure you only match the leaf nodes (nodes with no child nodes), so use:

:not(:has(*))

So to find all the exact matches (just the leaf nodes), use:

var matches = $(':not(:has(*))').filter(function () {
return $(this).text() == "Sometext";
});

or just using a combined filter on all elements (with an added check for 0 children):

var matches = $('*').filter(function () {
return !$(this).children().length && $(this).text() == "Sometext";
});

Note: I have not yet tested which of these two options is fastest.

Then you need to find the first ancestor (of the first match), that contains all the matches:

var commonparent = matches.first().parents().filter(function () {
return $(this).find(matches).length == matches.length;
}).first();

JSFiddle: http://jsfiddle.net/TrueBlueAussie/v4gr1ykg/

Based on David Thomas' suggestion, here it is as a pair of jQuery extensions (commonParents() and commonParent()) that may be of future use for people:

To find all common parents of a jQuery collection use `commonParents()':

$.fn.commonParents = function (){
var cachedThis = this;
return cachedThis.first().parents().filter(function () {
return $(this).find(cachedThis).length === cachedThis.length;
});
};

JSFiddle: (commonParents): http://jsfiddle.net/TrueBlueAussie/v4gr1ykg/3/

To find the closest common parent of a jQuery collection use commonParent():

$.fn.commonParent = function (){
return $(this).commonParents().first();
};

JSFiddle: (commonParent): http://jsfiddle.net/TrueBlueAussie/v4gr1ykg/2/

Notes:

  • jQuery optimises the combined use of first() in commonParent with the commonParents filter() and it only calls the code in commonParents until a first match is made, so commonParent does not need to be made more efficient.


Related Topics



Leave a reply



Submit