This question already has answers here:
Is there a CSS parent selector?
(33 answers)
Closed 9 years ago.
Is there a way to select a parent element based on the class of a child element in the class? The example that is relevant to me relating to HTML output by a nice menu plugin for http://drupal.org. The output renders like this:
<ul class="menu">
<li>
<a class="active">Active Page</a>
</li>
<li>
<a>Some Other Page</a>
</li>
</ul>
My question is whether or not it is possible to apply a style to the list item that contains the anchor with the active class on it. Obviously, I'd prefer that the list item be marked as active, but I don't have control of the code that gets produced. I could perform this sort of thing using javascript (JQuery springs to mind), but I was wondering if there is a way to do this using CSS selectors.
Just to be clear, I want to apply a style to the list item, not the anchor.
According to Wikipedia:
Selectors are unable to ascend
CSS offers no way to select a parent or ancestor of element that satisfies certain criteria. A more advanced selector scheme (such as XPath) would enable more sophisticated stylesheets. However, the major reasons for the CSS Working Group rejecting proposals for parent selectors are related to browser performance and incremental rendering issues.
And for anyone searching SO in future, this might also be referred to as an ancestor selector.
Update:
The Selectors Level 4 Spec allows you to select which part of the select is the subject:
The subject of the selector can be explicitly identified by prepending
a dollar sign ($) to one of the compound selectors in a selector.
Although the element structure that the selector represents is the
same with or without the dollar sign, indicating the subject in this
way can change which compound selector represents the subject in that
structure.
Example 1:
For example, the following selector represents a list item LI unique child of
an ordered list OL:
OL > LI:only-child
However the following one represents an ordered list OL having a unique child,
that child being a LI:
$OL > LI:only-child
The structures represented by these two selectors are the same,
but the subjects of the selectors are not.
Although this isn't available (currently, November 2011) in any browser or as a selector in jQuery.
You can use has():
li:has(a:active) {
/* ... */
}
Unfortunately, there's no way to do that with CSS.
It's not very difficult with JavaScript though:
// JavaScript code:
document.getElementsByClassName("active")[0].parentNode;
// jQuery code:
$('.active').parent().get(0); // This would be the <a>'s parent <li>.
Late to the party again but for what it's worth it is possible using jQuery to be a little more succinct. In my case I needed to find the <ul> parent tag for a <span> tag contained in the child <li>. jQuery has the :has selector so it's possible to identify a parent by the children it contains (updated per #Afrowave's comment ref: https://api.jquery.com/has-selector/):
$("ul").has("#someId")
will select the ul element that has a child element with id someId. Or to answer the original question, something like the following should do the trick (untested):
$("li").has(".active")
THE “PARENT” SELECTOR
Right now, there is no option to select the parent of an element in CSS (not even CSS3). But with CSS4, the most important news in the current W3C draft is the support for the parent selector.
$ul li:hover{
background: #fff;
}
Using the above, when hovering an list element, the whole unordered list will be highlighted by adding a white background to it.
Official documentation: https://www.w3.org/TR/2011/WD-selectors4-20110929/#overview (last row).
The first draft of Selectors Level 4 outlines a way to explicitly set the subject of a selector. This would allow the OP to style the list element with the selector $li > a.active
From Determining the Subject of a Selector:
For example, the following selector represents a list item LI unique child of an ordered list OL:
OL > LI:only-child
However the following one represents an ordered list OL having a unique child, that child being a LI:
$OL > LI:only-child
The structures represented by these two selectors are the same, but the subjects of the selectors are not.
Edit: Given how "drafty" a draft spec can be, it's best to keep tabs on this by checking the CSSWG's page on selectors level 4.
Future answer with CSS4 selectors
New CSS Specs contain an experimental :has pseudo selector that might be able to do this thing.
li:has(a:active) {
/* ... */
}
The browser support on this is basically non-existent at this time, but it is in consideration on the official specs.
Answer in 2012 that was wrong in 2012 and is even more wrong in 2018
While it is true that CSS cannot ASCEND, it is incorrect that you cannot grab the parent element of another element. Let me reiterate:
Using your HTML example code, you are able to grab the li without specifying li
ul * a {
property:value;
}
In this example, the ul is the parent of some element and that element is the parent of anchor. The downside of using this method is that if there is a ul with any child element that contains an anchor, it inherits the styles specified.
You may also use the child selector as well since you'll have to specify the parent element anyway.
ul>li a {
property:value;
}
In this example, the anchor must be a descendant of an li that MUST be a child of ul, meaning it must be within the tree following the ul declaration. This is going to be a bit more specific and will only grab a list item that contains an anchor AND is a child of ul.
SO, to answer your question by code.
ul.menu > li a.active {
property:value;
}
This should grab the ul with the class of menu, and the child list item that contains only an anchor with the class of active.
I had the same problem with Drupal. Given the limitations of CSS, the way to get this working is to add the "active" class to the parent elements when the menu HTML is generated. There's a good discussion of this at http://drupal.org/node/219804, the upshot of which is that this functionality has been rolled in to version 6.x-2.x of the nicemenus module. As this is still in development, I've backported the patch to 6.x-1.3 at http://drupal.org/node/465738 so that I can continue to use the production-ready version of the module.
Many people answered with jQuery parent, but just to add on to that I wanted to share a quick snippet of code that I use for adding classes to my navs so I can add styling to li's that only have sub-menus and not li's that don't.
$("li ul").parent().addClass('has-sub');
I actually ran into the same issue as the original poster. There is a simple solution of just using .parent() jQuery selector. My problem was, I was using .parent instead of .parent(). Stupid mistake I know.
Bind the events (in this case since my tabs are in Modal I needed to bind them with .live instead of a basic .click.
$('#testTab1 .tabLink').live('click', function() {
$('#modal ul.tabs li').removeClass("current"); //Remove any "current" class
$(this).parent().addClass("current"); //Add "current" class to selected tab
$('#modal div#testTab1 .tabContent').hide();
$(this).next('.tabContent').fadeIn();
return false;
})
$('#testTab2 .tabLink').live('click', function() {
$('#modal ul.tabs li').removeClass("current"); //Remove any "current" class
$(this).parent().addClass("current"); //Add "current" class to selected tab
$('#modal div#testTab2 .tabContent').hide();
$(this).next('.tabContent').fadeIn();
return false;
})
Here is the HTML..
<div id="tabView1" style="display:none;">
<!-- start: the code for tabView 1 -->
<div id="testTab1" style="width:1080px; height:640px; position:relative;">
<h1 class="Bold_Gray_45px">Modal Header</h1>
<div class="tabBleed"></div>
<ul class="tabs">
<li class="current"> Tab Title Link
<div class="tabContent" id="tabContent1-1">
<div class="modalCol">
<p>Your Tab Content</p>
<p>tabBased Anchor Link </p>
</div>
<div class="tabsImg"> </div>
</div>
</li>
<li> Tab Title Link
<div class="tabContent" id="tabContent1-2">
<div class="modalCol">
<p>Your Tab Content</p>
<p>tabBased Anchor Link </p>
</div>
<div class="tabsImg"> </div>
</div>
</li>
</ul>
</div>
</div>
Of course you can repeat that pattern..with more LI's
Another thought occurred to me just now that could be a pure CSS solution. Display your active class as an absolutely positioned block and set its style to cover up the parent li.
a.active {
position:absolute;
display:block;
width:100%;
height:100%;
top:0em;
left:0em;
background-color: whatever;
border: whatever;
}
/* will also need to make sure the parent li is a positioned element so... */
ul.menu li {
position:relative;
}
For those of you who want to use javascript without jquery...
Selecting the parent is trivial. You need a getElementsByClass function of some sort, unless you can get your drupal plugin to assign the active item an ID instead of Class. The function I provided I grabbed from some other genius on SO. It works well, just keep in mind when you're debugging that the function will always return an array of nodes, not just a single node.
active_li = getElementsByClass("active","a");
active_li[0].parentNode.style.whatever="whatever";
function getElementsByClass(node,searchClass,tag) {
var classElements = new Array();
var els = node.getElementsByTagName(tag); // use "*" for all elements
var elsLen = els.length;
var pattern = new RegExp("\\b"+searchClass+"\\b");
for (i = 0, j = 0; i < elsLen; i++) {
if ( pattern.test(els[i].className) ) {
classElements[j] = els[i];
j++;
}
}
return classElements;
}
Related
I have a web code generated by an aplication (built in angular). It is a menu choice where I need to hide some of them. It looks e.g. like this:
<div class=first>
<div class=second>
<a href=href1>
</div>
<div class=second>
<a href=href2>
</div>
<div class=second>
<a href=href3>
</div>
</div>
Now what I need is to hide the div which contains a element with href2.
I can hide the a element:
.first .second a[href="href2"] {display:none}
But I need to hide the whole div element. I thought:
.first .second < a[href="href2"] {display:none}
that doesn't work.
I KNOW THE JQUERY SOLUTION with has function. The problem is I can only adapt css files of the application. If i'm right I cannot use jquery in css file.
Please...any Idea how to do this ?
thanks a lot for help
best regards
Marek
At the moment there is (sadly) no way to adress the parent element with CSS.
I don't know your layout or CSS Code but maybe you can just structure your HTML-Code in a different way.
Edit
And now I understand your question...
To hide (for example) the 3th .second div you don't need to adress it from the child element but from the parent element.
What you are probably looking for are the nth selectors,
for instance: nth-child() or nth-of-type().
You can find more info here.
Also, you should probably take a look at the basics of HTML and CSS.
In your code you have not closed the <a> tags or wrapped the values of the attributes in quotation marks.
Wrong:
<div class=first></div>
Right:
<div class="first"></div>
To hide (for instance) the first element you could use the :first-child selector or the :nth-child() selector. Since you will probably use the nth-child() selector this would be:
.first > .second:nth-child(1) {
display: none;
}
Is there any sort of way to select a parent element in sass if the child contains a certain class?
Basically I am using the tooltipster plugin and have this issue
HTML
<div class="tooltipster-content">
<span id="note-options">
<ul>
<li>Create new note</li>
<li>Add new edit note</li>
</ul>
</span>
</div>
CSS
#note-options {
//From here, select the .tooltipster-content parent
ul {
li {
}
}
}
I need to be able to select the .tooltipster-content class based on the id of the span tag.
Tooltipster will always generate with the same HTML structure, but I dont want to change the tooltipster wrapper for all tooltips, I want to do it per each tooltip.
As tooltipster doesn't add your specified id to the parent wrapper for basic CSS editing, the only way I can think of is trying to use the span ID to select the closest .tooltipster-content class.
Is this achievable?
Note - I do not want to use javascript/jquery to fix this, I want to try and achieve it in CSS/SASS.
Nope. There's no way in CSS or any preprocessor that you can travel up the dom to select a parent. JS is the only way.
If you have jQuery, you can simply use .parent().
I can write the CSS selector using the child combinator > or just a space indicating any descendant. For example, I have this HTML code:
<span id='test'>
Hello
</span>
And I can write my CSS code in the following two ways:
#test > a {
...
}
or
#test a {
...
}
What is the best way, regarding the performance, to write the following CSS code?
Browsers parse selectors from right to left.
Then if you use #test > a, for each a element in your page, the browser checks whether its parent has id="test".
But if you use #test a, for each a element in your page, the browser checks if some of its ancestors have id="test". There shouldn't be many of those elements in the document, so probably the browser will have to check all ancestors of each a.
I haven't done any tests, but I expect checking all ancestors is slower than checking only the parent.
So probably #test > a is faster.
Please be aware that both selectors doesn't give you same output! In your example, it does.
But let's check the next HTML code, which contains two links.
<div id="test">
<p> lorum ipsum here dolor </p>
read more
</div>
There is a link between the text, and a link after the paragraph, to allow the user to read more text. (like on news sites).
If you use
#test a {
...
}
Then you will select both links.
If you use
#test > a {
...
}
it will only select the direct descendant link of #test, which is the read more-link ! That is because > is a child combinator. The link in the paragraph won't be selected.
Here below is a snippet to show you the difference.
#test a {
color: red;
}
#test > a {
font-weight: bold;
font-size: 18px;
}
<div id="test">
<p>lorum ipsum here dolor</p>
read more
</div>
As you can see, the second CSS rule has only edited the first child <a> element (the "read more" link).
Now, back to your question about the performance in your situation: if you use both selector on the next HTML part
<div id="test">
Hello
</div>
Performance wise it's same here.
-- edit1: clarification of the above sentence --
When using a selector, it checks from right to left. In this situation, with one div in the body, and one link element inside that div, the performance is same. That is because when using #test > a, it checks for all links to see if the parent of it has id="test". It doesn't traverse the tree further.
When using #test a, it checks for all parents of the link element (till the root) to see if it has id="test". Since the div is already found at the first parent check in this situation, it doesn't have to traverse the tree further.
But in other situation, when the div isn't the only element in the document, it of course make a difference. You cannot compare the difference here because, as mentioned before, the results are different. Unless you ensure that there are no second-level links under the given div. Then, the use of > is faster of course.
-- end edit --
However, if the person extends the content of the above div, he/she may face an unexpected CSS styling.
But to know the overall performance, it really depends of what you want to achieve by CSS (style all links or only the first level ?) and the DOM tree (depth of the tree ?) in the div#tree.
I think this is impossible :-(
Using CSS, I need to select the last label in the list of paragraphs, which is inside of a span.
<div>
<p>
<span>
<label>no good, because not the last</label>
</span>
</p>
<p>
<span>
<label>good</label> <!-- This one should be selected -->
</span>
</p>
<p>
<label>no good, because is not inside of a span</label>
</p>
</div>
Virtually the selector would be something like this:
div (p > span):last-child label {
background: red;
}
But I don't think CSS understands parenthesis (yet).
The reason for this is that ExtJS (Sencha) puts radio buttons in nested containers. The visibility of buttons is declared in the inner containers (that would be the span tags in the example above). I want to round corners of the last VISIBLE label, thus I need to find the last outer container that has an inner container declaring it's visibility.
Perhaps there is a different workaround for this? As a last resort, I'd accept a JS solution, as long as it's based on native ExtJS components/elements traversing syntax, rather than jQuery.
Please ask for more detail if needed.
It will (maybe, depends on if the selector will be in the final spec) partly be possible with CSS 4:
!p > span {
background: red;
}
But this will select all <p/> that have a <span/> inside, not only the last one. CSS currently does not know a :last selector, and as far as I can see even with CSS 4 this won't be implemented1.
So the summary is: Currently there is no way to do this in pure CSS.
Currently your only option is to use JavaScript. A sample in jQuery would be:
$('p:has(span)').last().css({ 'background': 'red' });
Here is a demo.
Or, as you mentioned in your comment, with extjs:
Ext.select('p:has(span):last').setStyle('background', 'red');
Here is a demo.
Answer to your updated question
Your new example does not need a parent selector anymore. The partly working CSS would be
div > p > span > label {
background: red;
}
But still: There is no :last selector in CSS1. Updating the above JavaScript samples:
jQuery:
$('div > p > span > label').last().css({ 'background': 'red' });
extJS:
Ext.select('div > p > span > label:last').setStyle('background', 'red');
1 About the :last selector:
To make it more clear: :last-child selects the last child inside an element in the dom, whatever it is. It is no sub query. So, even if your parenthesis version would be implemented, :last-child would select nothing because the really last element is not part of the query. You would need a :last selector like in some JavaScript libraries which selects the last item of the resultset, so it's a sub query. This is a completely different selector and will not be part of CSS soon.
Normally CSS is my thing, but I'm somehow dumbfounded why this isn't working for me. I'm building a site through Cargo for CMS purposes and you can see it here: http://cargocollective.com/mikeballard
In my menu, I have five main categories, and clicking on them (images, for example) reveals the list of work under that category.
<div id="menu_2444167" class="link_link">
<a id="p2444167" name="mikeballard" target="" href="http://cargocollective.com/mikeballard/filter/images">Images</a>
</div>
<div id="menu_2444188" class="project_link">
<a name="mikeballard" rel="history" href="mikeballard/#2444188/Ultra-Nomadic-Def-Smith-Cycle-2011">Ultra Nomadic Def Smith Cycle, 2011</a>
</div>
<!-- more divs here -->
<div id="menu_2444201" class="project_link">
<a name="mikeballard" rel="history" href="mikeballard/#2444201/Archive">Archive</a>
</div>
Basically, I'm trying to select the last div in this set, and add a margin-bottom:15px to that div. I've tried using:
.project_link:last-child or .project_link:last-of-type but it doesn't seem to be working.
The HTML, which can't be altered too much to rely on Cargo, isn't great as if they had used list items, instead of divs with anchor tags I'm assuming this would be a lot easier.
The :last-of-type and :last-child selectors are not supported before IE9.
Class names, etc are not looked at when it comes to the :last-child and :last-of-type selectors. The .project_link:last-child selector will only trigger if the specific element is the last child in the parent element and has the class "project_link", and the .project_link:last-of-type selector will only trigger if the specific element is the last element of that type and has the class "project_link".
Both should trigger in a supporting browser, since it is implied as *.project_link:last-of-type and will check for every type of element inside that parent (which appears to only be divisions anyways). The last division shown here has the class "project_link" which would match this rule. The only reason these wouldn't trigger is if you had extra elements (or divisions) below what you're showing us, or you're using a browser which doesn't support it.