* { color:black; }
or
#content * { color:black; }
I'd say the first is VERY easy for a browser to paint... Or is that one shameful as well?
Why couldn't the parent selector work when the tag is closed like the :last-child one does in Safari/Chrome/Opera? Eg first P is rendered, 2nd one, third one with span, div is closed, checks for span/last-child/whatever-else, applies styles?
@Rudie: #content *
is less efficient than *
. The reason is because with just a plain *
, only one element is evaluated to determine if it matches the selector. With #content *
, every ancestor of the element being evaluated has to be checked to see if it has an ID of content applied to it.
@Sean Curtis: There are various ways it could decide to handle it, all with various pros and cons. It could not render the div
until all child elements are loaded but that might mean seeing an empty page until the entire document loads. Or, it could wait until the closing element to decide before re-rendering the style but then you still have all the child elements to re-render. That isn't an issue that :last-child has, per se. At least, not on the same scale.
Which will load faster - An HTML page with a .css or a page with all css implemented using Javascript/Jquery ?
Sean, having a parent selector would mean the child nodes would have to monitor their parents for a change to the DOM. What your suggesting would be ok for applying styles during page load but I don't think the initial load time is the big issue - the problem is recalculating styles when the browser needs to redraw the viewport - that's what killed IE's performance when using expressions.
It would be interesting to run speed tests on this stuff but I think it's beyond the scope of JavaScript - it would need to be done during the vendors build/test process.
The static test renders using Safari/Mac in 5ms. Aside from the great test case for :last-child implementation, I'm not sure there is a great value in CSS performance optimization. Google Page Speed recommends avoiding descendent selectors which seems a little absurd. A robot would suggest using only ID selectors to eliminate specificity.
I'd be curious if 1000 adjacent selectors is slower than 1 extra HTTP request. That's an extreme case, but in the total page speed scheme isn't CSS the least important?
@COOLJAZ12: that depends on whether the JavaScript is applying the same CSS and selectors as there would've been in the beginning. It also depends on when you have to worry about render time. CSS will almost always be faster.
@Brendan Falkowski: It depends on what you're doing. The problem with complex rendering is that it can impact JavaScript performance. It can also affect rendering performance if you're making a lot of repetitive changesa€”say, with animations. Complex applications like Yahoo! Mail benefit from simplified selectors and improved rendering times because of the amount of work that is being done on the client. If you have a blog then it really isn't an issue because the page is rendered once, with few rules needing to get evaluated. Changing everything to ID or class selectors won't be worth the maintenance headache.
From the "philosophical" point of view, :parent
selector breaks the concept of CSS in its' "Cascading" part. What :parent
does is reversing the flow of rules: while everything go inside and down, it goes outside.
So, I think it's not only the question of performance.
Also I think that the someone's need for this selector is a consequence of his/her misunderstang of CSS basic ideas. We don't actually need this, we can handle everything without it. And it would be bette (IMO) to not have this selector to keep CSS, you know, pure.
Actually given your explanation of descendent selectors, I don't see how a parent selector could be any worse.
Take '.module:has(span)' - when the browser gets to a span element, all it needs to do is search upwards through the current tree to find .module, same as with the descendent selector. If it finds .module then it can apply the styles to that element right away. Assuming you do not have anything more complex than that simple boolean selector, it should be exactly as fast (or slow) as descendent selectors.
That's not to say a parent selector is a good idea. Developers seem to be very keen to strip HTML to as bare bones as possible, while creating a horrid mess of CSS selectors. Usually adding one simple class can eradicate many lines of CSS.
Scott, the difference is that a descendant selector is only evaluated once when that element is inserted by going directly up the tree. A parent selector would need to be evaluated each and every time a child selector was added. And then once a parent selector was matched, each and every child element would need to be potentially re-rendered. That's a potential for magnitudes of difference when it comes to performance.
For example:
.module span would fail immediately on all of the P's. Only when it hit the span would it attempt to work its way linearly up the tree.
.module:has(span) can't render when .module renders, when it should. Instead, the P gets rendered and the styles on .module get re-evaluated (and fail). Note, we're now checking to see if a rule applies on two elements for every element inserted in the DOM. That's twice as many as before. Once we hit the span, any style on the span renders and then the .module is re-evaluated and then the styles on ALL of the child elements are re-evaluated and re-rendered because of inheritance.
Huge difference.
I disagree with your conclusion. Few reasons:
1. A language shouldn't be about performance. Performance is for developers to worry about.
2. The fact that the standard way of doing it is slow doesn't mean it's the way browsers do it. I'm pretty sure browsers can do smart evaluations of rules and determine what is the best way for evaluating them. For example - if a browser sees #content *, it could simply search that #content first. It's doable and feasible, and I doubt that with all the performance wars going on browsers aren't "cheating" a whole lot.
So my point is - CSS is always about how can you best describe the hierarchy of a page. It's up to the browser to worry about performance, and PageSpeed, although a nice tool, always yells at me for very generic uses. The amount of work it would take to add classes to very element I might use is simply non realistic.
As for the parent selector - I agree with Sccot, that it's much more probable that the reason is that it's upside down, and that's counter CSS (although as I understand cascading means that every rules that follows on the same element will cascade on the previous).
I'm pretty sure Safari lead developer Dave Hyatt himself has said that performance issues make a parent selector unfeasible.
Totally agree with Arieh, working like this wouldn't be feasible, never mind bloating your html source into a big fast bastard child.
As long as you're not developing the next Yahoo Mail or an application of similar size and scope, I think it's pretty safe to disregard Google Page Speed's results.
Of course there are a few performance tweaks it suggests that do make sense and are easy to implement but other's are just doubtful.
I've got a question about serving gzipped content, is this unproblematic? I've had some bad experiences with caching content, which is a very annoying problem to have.
best
y
Whilst I agree with everything you've said - and the performance and rendering demos are great - I don't think a parent selector is a lost cause.
Based on these demos, I can't see why E:parent wouldn't fit perfectly amongst the suite of CSS selectors.
Does it matter that it doesn't follow the "normal" CSS syntax style? I don't think it does. It still cascades, it would still have a specificity weight and I think it's long overdue - as per my post: http://remysharp.com/css-parent-selector/
Jonathan, thanks for the explanation. You are of course right - once you match that parent you do need to re-evaluate the children.
However, there may be ways to mitigate performance hits - for example combining my and Arieh's suggestions and waiting until certain elements are completely loaded in the DOM before applying styles to their children.
Now when we encounter our .module element, we know the :has selector applies to it. We simply grab its entire HTML contents before applying the CSS. If we find a span along the way, we know to apply those styles. We can think of it like adding a .containsSpan class, in the same way you could think of :nth-child(even) would add .even to alternate elements.
I guess this only works for elements further down the DOM, containing fewer elements. If the selector is applied to the body tag the performance hit is going to return.
@ Jonathan Snook I can see the application vs. blog angle making a difference to some degree. Rails apps using the domain.com/#/action/name pattern as a single-page controller would naturally ID elements that change the DOM asynchronously. There isn't any extra work to slim the CSS selectors and it helps human readability too.
Factoring JS and DOM manipulation really depends on whether the inactive elements are removed or just hidden. Rendering a new interface (even without a preloaded DOM) would still be much faster than a standard AJAX request I think.
I don't doubt at Yahoo! Mail's DOM complexity the difference is visible. For most sites I'd bet the incremental upgrades in browser engines provide more enhancement than language optimization though. JS has been 1-2x faster with each release.
Hey thanks this was a really interesting read, I really like posts that get down to the "why" as opposed to the "how" (or the "do this because I said so").
Thanks, this was nicely explained.
I'm surprised this is blocked on performance grounds though. I can do plenty of CSS that slows my browser down. It's the clever developer that chooses the right tools for the right job. If you're concerned with page speed, it's up to you to re-evaluate your selectors.
It feels a bit like someone's hiding the scissors, so we can't hurt ourselves.
I don't think we should ever have a parent selector. To cascade means to flow the style down to child elements. If you need to target the parent change the markup not the way CSS works.
So why do browsers behave like this? Why not apply the CSS after the HTML is fully loaded and use xpath to find the elements? That would be so much faster, easier and powerful. To bad some strange people decided that HTML5 doesn't have to be valid XML, like XHTML and XHTML5
Thank you for the article. I just (finally) had managed to understand how the layout elements by browsers. It is much clearer than the explanations given by Steve Souders I find (or that the Google team PageSpeed). And enhanced ability to better manage my CSS rules.
Boo to the Google Page Speed page. While in context that article is not harmful many people will needlessly take over what they are suggesting.
It's okay to worry a little about performance but when it comes to css we have worse problems to tackle. I can't imagine taking over a css of a project I never worked on that was constructed using their guidelines.
Not complaining about this article though. Good reading.
I always complained about this (to myself mostly) but I'm glad I at least know the reason now, so I thank you for that.
seems like a :parent pseudo-selector should work like last-element as someone else mentioned before, upon the closing of the tag in question. At this point, go up a single level for #mydiv:parent {color: blue}
and target #mydiv's parent element. For multiple parents, #mydiv:parents(p), simply traverse upwards for any p elements that recursively apply to <parent-node>. Parent pathing is almost always a shorter trip then adjacent pathing. In which examples would a parent element not exist?
I hate how this wasn't totally obvious already. I just kinda figured people knew this.
The one place that I would have liked a parent selector in the past is on hover.
It would be easy enough for a browser to process this on demand rather than during initial page load. So it could be included in CSS but not recommend for use with anything other than :hover states for performance reasons.
It's worth keeping in mind that CSS is not a "language" but a standard, and part of the standardization process involves successful implementations of the standard. If the W3C were to declare a parent selector, and all of the browser makers were to say it would cause performance issues, then you would have a few possible outcomes:
In none of these conditions is the web actually better. In none of these does being "standard compliant" actually improve things. I think that it's vital to remember that we aren't designing a perfect world, but rather paving a road to a better world.
I should have re-read my post. I meant in point #1 that if no browsers implement a proposed parent selector because they would lose market share by doing so, then the parent selector would have no implementations and would be stricken from the proposed standard.
My personal feeling is that the obsession with Page Speed nowadays is a bit pointless.
I remember with dial-ups when speed was probably the priority thing which needed improving, but broadband means that the only reason I get bothered by such a thing is if a site is hosted on a particularly bad server.
I'd rather improvements be made with things like parent selectors, and font embedding to make the page-building side easier.
@Jonathan Snook Your deep understanding of CSS has forced me to re-evaluate what I thought was my competent understanding of the subject. Great article!
@Iain Dooley: I too wish we had
:hover:parent
Or some other kind of pseudo class for additional hover support. I prefer doing as much mouse-hover-interaction as possible in CSS.
While we are on the topic of site performance, how do you prefer to write your CSS code? Would you rather keep all styles on one line per selector or list each style on a new line? I assume with very large sites, CSS files can become quite unmanageable and writing code on a new line would be easier to manage. If you throw sub-versioning into the mix, does your opinion change?
@Tom: As you can see by looking at the code for this site, I like the everything on one line. However, working in a larger team, I've had to adjust my style. This was also partially to do with the fact that our heavy use of CSS3 made single-line CSS harder to work with. Now, we do each property/value on its own line. We also break up the files into various components, so each file never really gets that large.
Thanks for your response Jonathan. Fellow developers of mine, on their own projects, write CSS on one line but working in teams they would switch to multi-line CSS. That seems to be the norm then, though, I haven't heard about CSS3 being an issue with single line CSS.
Why not just render the effects of has() after the element has been completed (all children loaded)?
Thanks for writing the thoughts you explained to me on IM the other day. :) Great explanation! And yes, I've given up my desire (currently) for a parent selector. I'm also rethinking how I write CSS. OOCSS is looking better and better all the time.