Lately, there seems to have been an explosion of client-side CSS query. The current offerings are:
The free market tends to work in such a way as to saturate itself when consumer demand is high, but I really question the value of these engines. Was there really a demand for CSS querying of a document? Or was this some experiment that eventually led to a series of one-ups from developers looking for a challenge?
Don't get me wrong, I think the DOM is a crappy, inefficient API, but since it's part of a browser's underlying code, it will undoubtedly be much faster (depending on your use case) than any interpreted JavaScript syntax parser.
CSS queries also seem really inefficient and not very logical when compared to XPath queries, which is my favored DOM querying language (actually, I wish I could write style sheets to use XPath). In all my years as a frontend developer, I can't remember a time when using a combination of getElementById() and getElementsByTagName() didn't get my job done fairly quickly. I've never had any use for queries looking for elements with a given class name, or ones that want to find all descendant nodes of a given one, or any other thing that CSS querying claims to offer over using the provided DOM methods. If I need explicit access to an element, I give it an ID, or if I need to access a series of elements, I group them under a container and give that element an ID. Am I missing something?
Comments
Richard york says:
Don't forget about Dean Edwards's cssQuery()
http://dean.edwards.name/my/cssQuery/
I use CSS queries all the time, guess I never learned how to go without them. The most used, for me is calling up nodes by class name, and getting descendants by tag name. Yes, you can do all that with built-in DOM methods, but using a CSS selector, IMO, makes it much easier. Thinking out a client-side application is much easier when you can imagine it using the same selectors for script as you do for style. And I'd venture to say it also results in much less code.
Just about everything I develop has a dependency on cssQuery.
As for efficiency; I'm not sure, I never bothered to compare a CSS query package with built-in DOM methods.
Sean says:
In the last... say 6 months I've had to do one of the things that you mention one time.
Basically, I've got a style that makes the background-color on a table cell #cccccc. This works great on the screen, but for printing it doesn't show up (and the client *requires* that it show up [printing invoices basically].
So, I wrote some js, that onload(), will walk the DOM, looking for elements with that style, and then rearrange the contents to have three divs, one a container, one with the original content and one with an image [images print by default] of the #cccccc color.
Sounds ghetto cuz it is, but it allowed me to have a lot cleaner markup that's just "adjusted" on view. The previous implementation was very nasty and a real pain to work with.
wioota says:
Don't most of these libraries support xpath as well? I know jQuery does...
I think that more people are familiar with CSS and therefore library developers are providing something which users can utilize immediately without needing to learn even more syntax... Of course if they knew what they were missing with xpath...
Mike Shaffer says:
I guess I fall on both sides of this "argument". I agree with the two comments that do use them and they site decent and genuine reasons. I guess I fall more in line with you Nick...I tend to make my HTML take care of that stuff, either with containers or id's or both... Maybe it's "old dogs/new tricks" or something. I too, can only think of one time when I needed more than getElementById, tagName etc...didn't do the trick. Oh well...maybe I'll try to do it that way and see what happens.
XPath style sheets....mmmmm....now your talkin'.......love XPath, love it, love it.
Andrew Herron says:
I'm with you on this one. I use Mootools on just about all my projects but before then I was always using gEBI and gEBTN to get the job done. Wioota is right that most people don't want to learn the nitty gritty.
I've not checked any of them out so maybe you can confirm/deny this, but it seems many designers are looking for a way to add the JS functionality without actually learning it. With these CSS queries a designer can quickly modify the DOM based on things they are already familiar with.
benb says:
I will use both depending on the project at hand. One such example of using CSS query is when dealing with a lot of dynamic content with layered CSS where the element has more than one class applied to it. In such cases, you may be adding or removing class values from the element depending on whether the item is selected and/ or the state it is in. The elements then may have 3 classes applied to them for various reasons.
In these sorts of scenerios, it is much more reasonable to query a list of divs (example) that have a particular class value. Rather than grabbing all divs, then dealing with them or keeping a collection of currently applied items.
As such, most of the libraries you cite have a lot of power for dealing with dynamic DOM driven front-ends where this is very useful and in fact can be argued as more than reasonable. This is also the reason you often see the remove/add class in a lot of these.
Ben
Nicholas C. Zakas says:
Ben - I understand what you're saying to a point, but even your example (adding/remove classes) doesn't really require CSS querying. It's just as easy to use getElementById() and getElementsByTagName() and then check the class applied to the element. If all you're doing with a CSS query engine is to query ".enabled", then I fear you're really not using anything that's all that powerful.
benb says:
I intentionally used a simple example, whereby if you extrapolate that out, you are able to check dynamically altered attributes based on the layering of CSS. So for example, you have multiples classes applied that then have attributes that have been altered based on some interaction. Thus you need to query for a specific class whose attributes contain a certain value. This is but one example of many where this is far more powerful than simply grabbing an array of tags and then manually checking each of them, and certainly there are times where an id is not used or is not available.
For me, part of the choice boils down to the complexity of what you are attempting to do, and even whether or not you are using a library in the first place.
Ben
Nicholas C. Zakas says:
Ben - I still don't see the necessity for using CSS queries in your example. Class names can be gathered and evaluated pretty easily without using CSS queries, especially if it's elements you are going to be working with repeatedly, it would make more sense to cache those elements so that you don't need to run multiple queries on the document and then just check those cached elements for class name changes.
Brad Rice says:
You can tell me if this example is wrong, but what I've done is drawn some divs on the screen dynamically from a parsed xml file. The number of divs on the page varies each time the page is refreshed. I use a getElementsByClassName from Prototype to read those divs into an array and then add up the width based upon the lenght of the array and use the width value and some math using screen size to center them.
var aBlocks = new Array();
aBlocks = document.getElementsByClassName("charBlocks");
var iQty = aBlocks.length;
var blockWidth = ((iQty * 100) - 50);
var windowWidth = getWindowSize();
var iLeft = (((windowWidth - blockWidth) / 2) - 100);
felix says:
CSS styles typically determine the structure of your documents. So, many times when you are javascripting your documents, the way you are thinking of things is basically in CSS since that is how you are applying the styles.
I think in a very real way, CSS querying can be a more accurate view of how you are trying to access elements in the document - if it is structured using CSS it makes sense to access it that way.
I couldn't think of a better example, but if you've built a model in cartesian space you could subsequently access elements of that model using polar coordinates, but it's probably more direct to continue using cartesian coordinates.
Does that make sense?
Nicholas C. Zakas says:
Felix - CSS styles have nothing to do with the structure of a document, they are used to define the document's presentation. The structure is defined by the elements on the page and CSS is simply an overlay onto that structure defining more information. It's sort of like people and clothes...though some claim that clothes make the man, the clothes are just a decoration for what's underneath. I'm much more interested in finding my friend Mike rather than all of the people who are males who are wearing red shirts of which Mike is one of them.
benb says:
Nicholas,
I hate to say this - but many of the libraries do in fact cache the objects (i.e. when you call them they simply return the cached item). Again, it is about abstraction and re-use. At the end of the day you can choose to manually iterate through the items if you like, however, if you are already using a library like Prototype then $$(bla) is an abstracted way to go. Further, most of the libraries end up selecting based on similar means you suggest and then cache it (albeit with some optimizations and abstraction). I am not stating what you say is wrong, but this other method provides a level of abstraction that is very useful, caches and simply abstracts away things for cross-platform re-use.
Further, I gave an example where one needs to look at doing the following, whereby the query type applies:
"I've never had any use for queries looking for elements with a given class name, or ones that want to find all descendant nodes of a given one, or any other thing that CSS querying claims to offer over using the provided DOM methods. "
Ben
felix says:
Nicholas,
But that actually is sort of my point. Many times when things look the same on a page, it's because they fill the same role and you want to operate on them in the same way. That is, if you've styled many things in the same way you may also want them to have similar behaviours, so in some cases it may be helpful to be able to have your javascript access them in the same way that CSS is.
That is to say, if you are using CSS to color various things red. It is reasonable to believe that you might want all those same elements to behave in a certain way. Now, you could access them all using standard DOM/XPath methods, but it may be clearer (if less efficient) to use the same access method in your javascript as is used in your CSS.
Obviously the markup determines the underlying structure, but sometimes the presentation of an html page also represents structure. I'm not saying the CSS querying is the end all/be all of access to elements, but I guess I'm saying I can see why it could be useful in some cases.
Nicholas C. Zakas says:
@Ben - caching the results of CSS queries is a bad idea given your example. If class names are changing (as may position, parent element, child elements, etc.) then caching the results of these queries doesn't help you and, in fact, could hurt. And I still don't think querying for elements with a certain class is something that necessitates CSS querying when it's so easily accomplished using the DOM as-is.
@Felix - I'm sorry, I don't buy the "same style, same behavior" argument. My link colors are green and so is my background color, does that mean I want them to behave the same way? Is that reasonable? The presentation *should not* represent structure. Now, that's not to say that the presentation doesn't sometimes reflect the structure, but it certainly doesn't define it in any way.
benb says:
Nicholas,
To be clear, it is not just classes, as I also had stated before it is when seeking attributes that have been set perhaps either through classes or via dynamic change. Further, I disagree with your assertion about caching, given that it is entirely reasonable to (for example) seek out all children of element x that has classes y and z applied that additionally have had their backgrounds changed to blue.
Once I have that set of lists, it is entirely reasonable to cache said objects as I might only need to gain the original access in order to find out what objects are there. Once I have them I can and will work with them directly. Again, this is just one example, like the others above.
It seems you keep going back to specifics of single scenerios rather than the concept which speak to your statement:
've never had any use for queries looking for elements with a given class name, or ones that want to find all descendant nodes of a given one, or any other thing that CSS querying claims to offer over using the provided DOM methods. If I need explicit access to an element, I give it an ID, or if I need to access a series of elements, I group them under a container and give that element an ID.
Speaking to the above, I would also say that CSS querying is an abstraction that does use provided DOM methods. Further the notion of using an Id for everything sometimes is not a valid or reasonable solution. An example of that is ASP .NET users that have server controls generated from within Master pages of which there is no real control about the Id. A single id also disregards semantically grouping CSS and attributes that can help describe what the element's current state.
Ben
Nicholas C. Zakas says:
@Ben - I keep going back to single scenarios because speaking in generalities doesn't get us anywhere. Everything you have said and suggested can be accomplished using traditional DOM methods, which is exactly my point. I understand that the CSS querying engines are an abstraction, and of course they have to use DOM methods to do their thing, my point is that it's an unnecessary tool for web development and you haven't provided any examples to convince me otherwise.
benb says:
What I was attempting to provide was a scenario that speaks to your assertion that you have " never had any use for queries looking for elements with a given class name, or ones that want to find all descendant nodes of a given one, or any other thing that CSS querying claims to offer over using the provided DOM methods. " I have done that, and again will state in complex solutions, there are times where elements are dynamically created, removed and changed based on any number of user or application actions. For example, a user clicks on one pane that causes something, or the element is dragged to another location, or right-click and removed, parents changed etc.
I stated specific examples where dynamically changing UI (as an example) may have many elements under some container that are in flux or have attributes that may mean different things to different parts. I would defy you to come up with a reasonable way to not simply create your own querying function as those found in a library, that finds all elements x where y and z are true about it under element b. This is to be done without Ids, because a) some may not have them, b) some have them but are needed by another part of the application and c) the id is generated by the server and you do not know what it will be .. while you could hash them up to keep track by looping through each or some such, this is a waste, has several weaknesses and again points to complexity not needed (how would you also take into account all of the varying attributes of each of these).
So - given that scenario, state how you will provide that functionality using the DOM methods (for which we both agreed are found in external libraries) in a way that is entirely different than the code provided by these libraries. How will your code be wrapped up as a function or set of functions that will be entirely different than the query engines?
As I stated before, if you are using Prototype, Mootools, jQuery or something of that ilk, then the querying is part of what is there in most cases... You could certainly write your own (like I asked you to describe), but if you are including it and you have the need to gather elements that meet certain criteria, then how is that any different. You would simply be writing something already found in the library. That is wasteful. I will also add, if you are using the library simply for that one purpose, that would also be wasteful (in the case of the full featured ones) as that is but a drop in the bucket for what they provide.
If you are simply saying you still see no need for you or anyone else to have a desire to find element x under container v whose attributes are currently w,y,z, then that would throw out a lot of so-called web 2.0 apps that provide a rich UI.
Ben
benb says:
I also wanted to add, that for the +/- of it, there are times where multiple classes can be applied simply for tagging purposes rather than display.
Nicholas C. Zakas says:
@Ben - I'm not sure you're doing anything other than repeating yourself at this point. You provided a case where people may want to look for class names, I stated that even that doesn't rationalize to me the use of a CSS query engine and all its overhead. As for your other challenge, to come up with a way to determine that elements have y and z true about them, it's fairly easy, see my latest blog post.
David Golightly says:
In all environments, always, you're going to have a tradeoff between development time and runtime performance. I suppose where you draw the line is subjective, but I haven't noticed that most major CSS query engines out there add so much runtime overhead to make them unusable. Since CSS syntax is so simple, most engines use a super-fast (by JS standards) regex parser; the bulk of the runtime work exists in comparing each possible element to the criteria specified in your query.
I use CSS queries, and this is why: I code complex client-side interactions where multiple instances of the same widget or component appear side-by-side on the same page. The work of generating unique ID's for each sub- and sub-sub element for each dynamically generated widget would be too great; I need loose coupling between HTML and JS. Such loose coupling is provided in CSS by way of CSS queries.
Consider:
<div id="instanceA">
<p class="foo">title</p>
<input type="text" />
</div>
<div id="instanceB">
<p class="foo">title</p>
<input type="text" />
</div>
I can clone and reproduce this widget ad infinitum, each time pivoting my query on the top-level parent's id and *not manipulating anything about its children*. This is where the work is made up for in CSS queries.
Nicholas C. Zakas says:
@David - In the case of attaching events to things of the same class, I'd suggest that you use event delegation instead. Attach one event handler to the container and let all the events from the children bubble up to that container. In the handler, you can check the class name and determine what to do. There's no need to query for elements of a particular class and attach individual event handlers, which is wasteful.
David Golightly says:
I didn't specifically mention adding event handlers to many elements. There are many possible operations one might need to perform on specific sub-elements, such as manipulating text content, position, or class information, where working through IDs gets unwieldy. Especially in work environments like mine, where HTML structure changes with the whims of designers, the flexibility of CSS queries means much less work in code maintenance.
To a point, I understand your objections; however, I assert that for many front-end developers, there are significant benefits to being able to refer to DOM structure in this way. At the same time, I'm not asserting that this is the right approach in every case, or for every developer.
The discussion over preference for CSS vs. XPath selectors is a different one altogether from the need for an ability to query using a relatively concise, flexible syntax vs. using the naked DOM.
Isaac Z. Schlueter says:
I think that a lot of developers learn CSS first, and Javascript second. CSS Queries give the CSS-knowing JS novice a "quick-start" way to feel like they're getting things done. That's not to say that all users of CSS query approaches are novices, but I can understand how it can be conceptually convenient to use a shared query syntax to refer to Dom nodes in both CSS and JS. (As you said, you'd love to be able to write style sheets in XPath, I'm assuming that this is at least partly because it would be mentally convenient to use the same query language everywhere.)
In my opinion, I'd usually prefer to use the getElementsBy approach. I like to have one-liner instantiation calls in my markup that can easily "know" the ID of the element being instrumented, and from that starting point, it's all className and tagName.
It seems like a lot of these libraries implement some very complicated CSS selectors, which seems like it can be pretty handy. If you need to find all the elements matching:
h2.foo a[rel=bookmark]
then it take significantly more code to do that through the Dom directly, increasing the chance that another developer doesn't understand what you're trying to do. (I've never used CSS Queries myself, so my first instinct would be to use the YUI Dom's getElementsBy method and supply a function that specifies the criteria.)
JS API makers are catering to their market, and as such, have to give what the market demands. I personally think onDomReady and onAvailable and such represents a deeply wrong approach, but the APIs provide it because the market wants it.
No further comments allowed for this posting.