Why CSS Selectors are the most useful Selenium WebDriver locators?

When Front end developers need to locate elements on a web page, they use CSS selectors! Why not for selenium webdriver automation?

image created via carbon.now.sh

A wide variety of locators are being used in Selenium Web Driver automation. CSS selectors come last when you ask the question, “What are all the locators do you use frequently and why?”

XPath tops the list as one size fits all solution. There is also a tendency to use XPath even when a proper id is available.

Often, it is also lack of exposure to CSS selectors that drives them towards other options. People have mentioned that they are able to get the job done with other selectors. Fair enough, no harm was done.

Why do you have to pay attention to CSS selectors?

Answer this question. When you want to put a nail on the wall, will you use a brick, cobblestone or hammer?

Brick and cobblestone can do the job. But, Hammer is the tool for the job.

If you are an automation engineer working on UI automation, invest time in learning CSS selectors (how about reading through the rest of the article?). You will reap benefits that are much more.

I can hear your question: “But, it is so easy to copy the XPath. How about CSS selectors?” Trust me, in many browsers, the option is right there next to Copy XPath. You’ll find Copy CSS Selector or Copy Selector.

Do you know why? Because the front-end developers use CSS selectors to apply visuals and behaviors of the web pages even before they come down to testing.

Why CSS Selectors Were Invented?

Have you ever wondered why all those classes and IDs are there? Did you ask anyone that I’d need some ID/classes included in these elements so that I can automate tests for this UI?

If not you, then who asked for those attributes?

The story goes back to the days when styles were added to HTML documents. This was before UI testing was automated. Each browser had its own way of styling documents. Document (HTML) authors were not in control as to how their documents were presented to readers. Authors needed a way to target tags and apply styles to arrive at the layout they wanted. Håkon Wium Lie proposed what would become the current cascading style language.

Selectors to target specific elements was specified formally by CSS Working Group. The browser developers take that specification as the requirement and build the browsers to support CSS.

Attributes such as id and class were added as hooks to target specific elements. They are there to help front-end developers style and attach behaviors to elements. Automation engineers can piggyback on the same provisions, CSS selectors, to identify elements.

How CSS selectors are used?

To style an element, FEDs need to locate the element first and then apply styling rules.

It looks like this:

#logo{color:white;background:black;}

That CSS snippet says, apply color and background styles to any element with an ID of logo. Normally, ID will be unique in any HTML document carefully crafted. But there are chances for multiple developers working on the same page to use the same ID for different elements. Most of the time, without knowing it is already being used. Don’t worry, they’ll eventually catch up with that bug, but the point is the browser throws no warning. You should tell the developers when you find such scenarios.

The javascript developer who wants to take the logo for a spin writes code like this:

letelement=document.querySelector("#logo");rotate(element);

Don’t worry about the implementation of rotate, but just look at the first line. document.querySelector is a native API available to select elements in a browser. …and it takes CSS selector syntax.

In fact, you can use it within JavascriptExecutor.

Since FEDs are using CSS selectors to locate elements (either to style them / amend the elements / to attach behavior to elements), it makes sense for the UI test automation engineers also to use the same API to locate elements.

Convinced? Here is a preview of different selectors replaced by CSS selector.

Four Selectors

Let’s look at primitive selectors to start with. There are just about 4 you need to master and this is the easy part.

Hash # for IDs

Any element with a particular ID can be located using By.cssSelector by prefixing a hash # to the ID.

For example:

By.cssSelector("#logout")

#logout will help you locate an element that has ID with a value logout. It doesn’t matter what the tag name is. It could be a link or a button, but cssSelector finds it.

Usually, the ID is unique. It’s a fair assumption that there’s usually one. Assumptions can go wrong. Twenty developers working on a very busy UI might end up slipping the same ID on two different elements. So, if you wanted to pair it with a tag, you can do so. Put the tag name in front of the ID.

By.cssSelector("a#logout")

This selector is even more specific. It says, find an anchor tag (a link) with an ID valued as logout.

Dot . for Classes

Now that we know how to deal with IDs, locating elements by class names will only be easier than ever.

You need to prefix a dot . to the class name to find an element with such a class.

Example overdue:

By.cssSelector(".btn_red")

That’s going to be a red button (if the developer adds relevant styles), but let’s not worry about that. This selector will identify an element with a class btn_red. If more than one element is found, the first one is returned.

And if you wanted to combine a tag name and a class name?

By.cssSelector("a.btn_red")

That makes sense, isn’t it?

Tag stays the same

Now, this one is even easier. What if you want to locate a particular element using its tag name? No prefix, no suffix. Just the tag name will do.

By.cssSelector("table")

That will give you the first table on the page.

[attr] for Attributes

The last selector among the four. This is entirely based on attributes within an element. HTML elements tend to have attributes such as id, name, class, type, src, href, alt and a lot more under data- prefix.

All these attributes can be used to identify elements if you know that their attributes are uniquely identifiable.

If you wanted to access all images that are a sibling of a h1 tag, you can use the general sibling combinator. If there is only one img, you’ll get just that.

By.cssSelector('h1 ~ img')

In this instance, it doesn’t matter if the img tags follow h1 immediately. What matters is, they all have the same parent and img tags are a sibling to h1.

What if you use img ~ img? You’ll get the last two. Go figure.

And by the way, your dear most xpath gives you a way to get elements by sibling via special function:

By.xpath("//img/following-sibling::h1")

Angle Bracket > For Children

Siblings are fine. But children started crying now. Let’s pay attention to them. These are widely used combinators too. You can target an element which is a direct child of a parent element using RIGHT ANGULAR BRACKET > sign.

If you wanted to locate an input box that is not mandatory (which does not have a class required so to speak):

By.cssSelector('input:not(.required)')

If you wanted to select all inputs that are not email type, then 'input:not([type=email])' will do.

And you can chain them too, like this: input:not([type=radio]).not(.required). That would give you all inputs that are not radio buttons and do not have a class required. In this case, that’s just one text type and another email type inputs.

:nth-child

This one helps you with targeting a particular element based on its position.

Remember child selector from previous sections. We are going to use it. How do you say, “Get me the second child within the profile”. #profile > *:nth-child(2) will do. * denotes any tag and nth-child looks for a particular child based on the number you’ve given.

You could have used the right tag name instead of *, as in #profile > img:nth-child(2). You need to be careful when the tag name does not match. No element will be selected if you use a tag name that is not there. For example, #profile > h1:nth-child(2) returns nothing, as the second child is not a h1 tag.

:nth-child takes input like a number, odd, even and 3n+2. You can read 3n+2 like, every 3rd element starting from the 2nd element. MDN defines it as An+B. This is used more in terms of styling elements. May not be so much for test automation. Let’s leave it right there.

There is another one to get an element counting from bottom: :nth-last-child. For example, #profile > *:nth-last-child(1) will give you the last child, which is p tag that has the text “Conclusion…”. The spec has :last-child as a pseudo-class that will give you the similar outcome.

I’ll leave you to guess what :first-child would return.

:nth-of-type

While nth-child takes all children into account, nth-of-type takes only the tag type into account. Let’s use the same piece of code.

We’ve seen that, #profile > img:nth-child(1) would return nothing, as first child is h1, but #profile > img:nth-of-type(1) returns the first image. The spec also has something called :first-of-type as a shorter version.

What do you do to count from the bottom (reverse order)? Yeah, :nth-last-of-type will come to your help. You also have :last-of-type to get the bottom-most child of the specified type.

:disabled

One last pseudo-class we’ll look at is :disabled. This is handy when you look for a particular element that is either disabled or enabled. In fact, the specification has classes like :enabled, :checked and other pseudo-classes for you to use.

In Xpath’s defense

Knowing CSS selectors and then choosing XPath as a solution is different from NOT knowing CSS selectors and choosing XPath.

Final words, do not get discouraged if you have been using XPath all the while. By now, you would have seen that they are very similar in nature. They have their own pros and cons. cssSelectors perform well as they are natives to the browser. XPath is more readable in certain instances. Whichever gets the job done is ok. One of these make your code readable? Go for that. Performs well over the other, choose that. Just don’t limit your options.

In my experience, test automation engineers started using XPath universally. So, this article was to add a counterweight to cssSelectors so that the scale is balanced against XPath. In fact, John Resig, creator of jQuery has written about such a comparison showing how powerful xPath can be.

All righty! Thank you so much if you managed to reach this far. Hope it was useful and you get to try your hands on cssSelectors the next time you target an element.

Newsletter

Hi, I am Vijayabharathi, a software quality assurance and test automation lead for enterprise IT organizations. I am also
a web developer with a
front-end developer certification from freecodecamp. I write about my learnings on Web Development in this site.

Privacy note

In case you don't fully understand GDPR, just know that it is a good thing and protects your personally identifiable information (PII). I removed third party visitor tracking scripts from this site, and added my own solution that tracks non-PII data just to know if anyone reads articles on this site. Privacy page is updated with more details if you are interested.