What Tool How to Use to Test The: Contains() CSS3 Pseudo-Class in a Browser

What tool can I use to test the :contains() CSS3 pseudo-class in a browser?

jQuery's selector engine also implements the :contains() pseudo-class. It is to my knowledge that jQuery and Selenium implement it the same way.

I'm not sure how you would be able to test it on sites that don't make use of jQuery, but on sites that do, it's just a matter of firing up your browser's JavaScript console, and running jQuery selectors in the console.

Here's an example with viewing this very Stack Overflow question on Chrome's JavaScript console (you may get different results depending on whether you're logged in or not):

> $("#mainbar div[id]:contains('selector')").get() /* DOM objects with .get() */
[<div class=​"question" id=​"question">​…​</div>​, <div id=​"answers">​…​</div>​, <div id=​"answer-9007154" class=​"answer">​…​</div>, <div id=​"answer-9008184" class=​"answer">​…​</div>​]

Having some trouble understanding why the :contains() pseudo class in CSS selectors works the way it does?

The usual thing I do when I find myself in this kind of trouble is to look at the spec.

As you probably know, there's none for :contains() in the current spec and therefore you rely on undocumented, unspeced features of a particular browser/parser. It should work, but it doesn't - obviously the implementation wasn't complete. And now the pseudo-class is gone.

Could you go for an XPath instead? Either by internal Selenium methods or JavaScript. This XPath is the same as your CSS selector number 2:

//div[contains(text(),'My Header Title')]/following-sibling::div//input[contains(@class,'my_button')]

EDIT

After your comment showed me that we're talking about Selenium RC and, therefore, Sizzle, I dug deeper.

I took your example HTML, stripped it from the hidden and (seemingly) needless elements, and was left with this:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8" />
<script src="sizzle.js" type="text/javascript"></script>
</head>

<body class="bp">
<div class="container">
<div id="nav_bar">
<div id="user_bar">
<div id="wrapper" style="border-radius: 10px 10px 10px 10px;">
<div class="content">
<div class="breadcrumb_trail">
<div class="organizer_widget root_organizer" title="WorkflowItem" style="">
<div class="object organizer">
<div class="interior">
<form method="POST" enctype="multipart/form-data">
<div class="organizer_header view_header"> My Header Title </div>
<div class="organizer_widget" title="Citation" style="">
<div id="citation" class="object organizer">
<div class="clear"></div>
<div class="interior">
<div id="Citation___id_widget" class="widget_row numeric">
<div id="Citation___title_widget" class="widget_row string">
<div id="Citation___abbreviated_title_widget" class="widget_row string">
<div id="Citation___authors_display_string_widget" class="widget_row string">
<div id="Citation___language_widget" class="widget_row choice">
<div id="Citation___link_widget" class="widget_row link">
<input id="ba_citation" class="my_button" type="button" value="Break Associations" name="break_assoc_5376dcc81102a5d76bf829513b096be8f67e560d" />
<div class="clear"></div>
</div></div></div></div></div></div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>

</html>

I downloaded the latest Sizzle and I obtained the version of Sizzle that is actually used by Selenium in the current release.

Turns out those two are very different.

E.g. the contains implementation of current Sizzle:

return ~( elem.textContent || elem.innerText || getText( elem ) ).indexOf( match[3] );

and the implementation Selenium uses:

return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;

I tried both implementations on my test document, results can be seen here (click to enlarge):

Current Sizzle - matches all perfectly
Current Sizzle Results

Selenium's Sizzle - matches 1 out of 4
Selenium's Sizzle Results


The results say it all. Selenium uses an old version of Sizzle that is somehow imperfect in handling of :contains() pseudo-class. The current Sizzle version doesn't suffer from the bug and is able to find all elements well.

Now, you can do any of these:

  1. File a Selenium bug.
  2. Use XPath as a workaround.
  3. Switch the sizzle.js file in your Selenium package.

How can I inspect and tweak :before and :after pseudo-elements in-browser?

In Chrome's Dev tools, the styles of a pseudo-element are visible in the panel:

Sample Image

Otherwise, you can also input the following line in the JavaScript console, and inspect the returned CSSStyleDeclaration object:

getComputedStyle(document.querySelector('html > body'), ':before');
  • window.getComputedStyle
  • document.querySelector

How do I test CSS selectors in JavaScript?

The simplest traditional way by far is to not use JavaScript at all, and just set up a test page by hand where you can test selectors to your heart's content. The test cases you see on the Web (like the well-known CSS3.info Selectors Test) are really just souped-up versions hosted online.

But if you're looking for a JavaScript method, you can try the Selectors API. It's available in modern DOM implementations (IE8+ and others) and it provides a JavaScript frontend for querying the DOM for element nodes using CSS selectors, as well as testing CSS selectors natively supported by a given browser.

(For browsers that don't implement the Selectors API, you'll have to rely on jQuery, but remember that it provides support for a different set of selectors than what a browser supports as well as its own non-standard extensions which aren't found in the Selectors spec. An example of using jQuery with Chrome's JavaScript console to test a selector can be found here.)

Call querySelector() or querySelectorAll() depending on what you want to test, and check the return value (preferably in your browser's developer tools since you're just testing):

  • If matches are found, the former method returns the first Element matched while the latter returns all elements matched as a NodeList.

  • If nothing is found, the former returns null while the latter returns an empty NodeList.

  • If the selector is invalid, an exception will be thrown which you can catch.

Here are some examples with the command editor (multiline) in Firebug's console on Firefox 10, tested on this very question:

  • Finding the first h1 in body:

    var h1 = document.body.querySelector('h1');
    console.log(h1);
    <h1 itemprop="name">
  • Querying descendants of that h1 element we just found:

    var subnodes = h1.querySelectorAll('*');
    console.log(subnodes[0]);
    <a class="question-hyperlink" href="/questions/9165859/how-do-i-test-css-selectors-in-javascript">
  • Testing the :-moz-any() pseudo-class in Firefox (:-webkit-any() in Safari/Chrome):

    // This selector works identically to h1 > a, li > a
    var hyperlinks = document.querySelectorAll(':-moz-any(h1, li) > a');
    console.log(hyperlinks);
    [a#nav-questions /questions, a#nav-tags /tags, a#nav-users /users, a#nav-badges /badges, a#nav-unanswered /unanswered, a#nav-askquestion /questions/ask, a.question-hyperlink /questio...vascript]
  • Testing a nonexistent selector (that perhaps many of us wish did exist):

    // :first-of-class doesn't exist!
    var selector = 'div.answer:first-of-class';

    try {
    var firstAnswer = document.querySelector(selector);
    console.log(firstAnswer);
    } catch (e) {
    console.log('Invalid selector: ' + selector);
    }
    Invalid selector: div.answer:first-of-class

How to know which CSS style is applied and from where (javascript)?

It's the chrome standard for outlining focussed input elements. These are so called "user agent styles" which might be hidden in the inspector - in that case you have to check "Show user agent styles" in the settings first. You can overwrite this with

input:focus{
outline:0;
}

But be aware, that for usability reasons, it is highly recommended to indicate to the user that a certain element is currently in focus! So you should apply some focus styles which suit your theme.

For example you could do some slight inset boxshadow (and perhaps remove the initial text or at least make it more opaque):

#yith-s:focus{
outline:0;
box-shadow:inset 0 0 2px 1px #ccc;
}

Is pseudo-class selector :checked supported in Chrome and Firefox?

Simply put yes they both do.

However the problem you're having stems from the way in which certain browsers render form elements such as checkboxes and radio buttons.

You're unable to see your background color change when the input is selected because the browser is overwriting your customization with it's own default behavior.

Luckily the fix is simple for Chrome, Safari (Webkit) you just need to set an 'appearance:none;' value to wipe out all default styles and then add your own special blend.

Firefox (Gecko) has a known bug associated with rendering checkboxes and radios so you'll get limited use out of the appearance value.

Here's a link to the fiddle http://jsfiddle.net/traxp/

Hope that helps :)

<!DOCTYPE html>
<html>
<head>
<style>
input {
-webkit-appearance:none;
-moz-appearance:none;
appearance:none;
background:white;
width:20px;
height:20px;
border:2px solid grey;
border-radius:10px;
}
input:checked {
background:red;
}
</style>
</head>
<body>
<input type="radio" checked />
<input type="radio"/>
<input type="checkbox" checked />
<input type="checkbox" />
</body>
</html>

Why doesn't the selector h3:nth-child(1):contains('a') work?

:contains() is not was going to be a CSS3 selector (thanks T.J. Crowder for the link), but it didn't make it, most likely because the way it works tends to lead to severe performance and over-selection issues. For example, if an element E matches :contains() for a given string argument, then all of its ancestors would also match; using it with a universal selector would lead to unexpected results with certain style properties, on top of being slow for the browser.

There is no other CSS selector that serves a purpose like :contains(). So you'll have to find some other way, either by modifying your HTML or even by using jQuery's :contains(), to achieve the effect you want:

Select an h3 element

if it is the first child of its parent

and its text contains the letter 'a'.

For jQuery and Selenium RC users: :contains() is implemented in the Sizzle selector engine used by jQuery, which is also used in Selenium RC (but not Selenium WebDriver). It works as described in this decade-old revision of the CSS3 spec, but again, due to how the spec describes it, you need to use it with care or it may lead to unexpected selections.

On a final note, h3:nth-child(1) can be replaced with h3:first-child, which as a CSS2 selector has better browser support.

How do I add / insert a before or after pseudo element into Chrome's Inspector?

This is the easiest way and the way I do it:

  1. Inspect the element you want to add the ::before or ::after to by right clicking it and going to "Inspect Element".

  2. Now in the Developer Tools Console, click on the plus sign icon aka. "New Style Rule". See the image below, the plus sign is next to the "Toggle Element State" button.

    Sample Image

  3. Next, you will be able to edit the selector so add ::before / ::after to it:

    Sample Image

  4. Now edit the content to whatever you like, i.e.

Like so:

.grp-row::before {       
content: '> ';
}

That's all there is to it :)



Related Topics



Leave a reply



Submit