12

Really simple: how do I most accurately test if a browser has support for a certain CSS selector?

I currently have some CSS code that makes the page a little more interactive by using the :checked selector in CSS, but I want to create a fallback script that does the same thing with JavaScript, but only if the user's browser has no support for the :checked selector.

My question is, how do I most accurately test if the user's browser supports a certain CSS selector?

Here is the code I'd like to use it on:

HTML:

<label class="coolbox">
    <input type="checkbox"/>
    <span>I want to eat some caek.</span>
</label>

CSS:

.coolbox input {display:none;}
.coolbox span::before {
    content: "";
    display:inline-block;
    width:10px;
    height:10px;
    margin-right:5px;
    border:1px solid black;
}
.coolbox:hover span::before {border:1px solid #555;}
.coolbox:active span::before {border:1px solid #999;}

.coolbox span::before {background-color:#F77;}
.coolbox input:checked + span::before {background-color:#4A4;}

Demo

PS: I'd prefer not to just use conditional comments, because I'd like to follow the standard of detecting features instead of browsers.

BoltClock
  • 665,005
  • 155
  • 1,345
  • 1,328
Joeytje50
  • 18,118
  • 14
  • 60
  • 91
  • 1
    Isn't this what http://modernizr.com/ does? – j08691 Jan 13 '14 at 15:22
  • 3
    I'd prefer it if it would be possible with just a simple script instead of having to download a library which I'd just be using once. – Joeytje50 Jan 13 '14 at 15:24
  • You can check the [source](https://github.com/Modernizr/Modernizr) or [build your own custom modernizr version](http://modernizr.com/download/) with just the tests you want/need – Andreas Jan 13 '14 at 15:30
  • 1
    @Andreas Is `:checked` test available in modernizr? – Wesley Murch Jan 13 '14 at 15:31
  • You can write some script for IE <= 9 but you may need an special comment to make the load – DaniP Jan 13 '14 at 15:32
  • 1
    @Danko See the OP's "PS". – Wesley Murch Jan 13 '14 at 15:32
  • 1
    Checked seems to be available in modernizr: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/css/checked.js The `testStyles` method is basically `injectElementWithStyles`: https://github.com/Modernizr/Modernizr/blob/master/src/injectElementWithStyles.js – Tallmaris Jan 13 '14 at 15:36
  • 1
    `:checked` isn't available in the builder yet [source](https://github.com/Modernizr/Modernizr/pull/879) but the code is (see the link of @Tallmaris) – Andreas Jan 13 '14 at 15:40
  • @WesleyMurch I know I just was point that option could be the best without using tools like modernizr. Other way you may need to make a script that evaluates browser and version (if that's possible)... – DaniP Jan 13 '14 at 15:42

4 Answers4

9

You could use querySelector:

function testSelector(selector, node){
  var scope = document.createElement("div");
  scope.appendChild(node);

  try {
    return scope.querySelector(selector) !== null;
  } catch(e) { return false; }
}

You can test it like this:

var node = document.createElement("input");
node.type = 'checkbox';
node.checked = 'checked';

testSelector("input:checked", node); // === true

See this other question for more info on querySelector.

Community
  • 1
  • 1
Matt
  • 781
  • 6
  • 15
  • If I run this script, there seems to be an error in `testSelector`'s `return` statement ("invalid argument"). I'm still looking for what is causing this exactly on google, but if that can be fixed, I think this'd be the best solution. Would you have any idea what could be the cause of this? – Joeytje50 Jan 13 '14 at 16:32
  • After testing `document.body.querySelector(':checked')` in IE11's IE8-emulator's console, it gave the same "invalid argument" error. It looks like IE8 (atleast in IE11 emulation mode) considers the `:checked` selector invalid, and throws an error. – Joeytje50 Jan 13 '14 at 16:39
  • I tested on Chromium and Firefox but not IE. I'm not sure what could be causing "invalid argument". If you're testing on IE, make sure you're using IE8+ in standards mode. Or the querySelector function will not exist. – Matt Jan 13 '14 at 16:43
  • 1
    I've decided to go with this answer, since this doesn't require any changes to either the CSS or JS, and doesn't modify the contents of the page to test anything. I've put a `try {} catch(e){}` around the part that was causing problems in IE8. I'm just going to assume that if the browser throws an error if you use `:checked` in `.querySelector`, the browser doesn't support it. – Joeytje50 Jan 13 '14 at 16:45
  • 2
    @joeytje50 is correct. An unrecognized or invalid selector should throw an error, not just return a null result, so IE is behaving correctly. When `querySelector()` returns null, that means the selector is valid but doesn't match anything. See http://www.w3.org/TR/selectors-api/#processing-selectors – BoltClock Jan 14 '14 at 11:45
  • I added a try/catch block in the solution to support selectors considered invalid. – Matt Jan 14 '14 at 12:47
  • It's much simpler to just run the selector string through `querySelector()` and return true without a null check, or false on error. As long as the browser doesn't throw an error, the selector is supported. In fact, what is really being tested here is not "if the browser supports the given selector", but "if the browser is able to find an element matching the given selector". You would need to supply an element that is guaranteed to match the selector in order for the test results to be reliable. – BoltClock Sep 19 '14 at 03:01
5

Workaround for your case:

<input type=checkbox checked=checked>

css:

input{
  font-family:'arial';
}
input:checked{
  font-family:'sans-serif';
}

checking procedure: js

alert($('input').css('font-family')=='sans-serif'?'supported':'not supported');
3

From some research I was able to find various websites that can test your browser to see the support.

This website is helpful to find what supports what but does not test your current browser.

This website will test your browser and if you don't want to use modernizr you can learn from their script.

This last website seems to do a great job and is very accurate of the support. I also am pretty sure I found the script that is doing this so like I said you can learn from how other website are doing this.

To figure how they are making this work you will need to understand how their scripts are working. The bottom three seem to be the most critical to the function.

<script src="utopia.js"></script>
<script src="supports.js"></script>
<script src="csstest.js"></script>
<script src="tests.js"></script>

These are just some options that I found and it is up to your needs. Best of luck and hopefully this has been helpful.

Josh Powell
  • 6,041
  • 5
  • 29
  • 58
  • Going by the source code on css3.info, I've made a little test script for use in the console: `var x = new CSSTestCase(['checked'], { onSuccess: function() {console.log(true,arguments)}, onFailure: function() {console.log(false,arguments)}});`, and when I browse through the logged message in the console, this script seems to require loading an iframe for every test. For the other site, I'm still looking, but it looks a bit confusing as to how they actually do the feature testing on their site. – Joeytje50 Jan 13 '14 at 16:07
  • @joeytje50 Hmm very weird, after looking through the page's source code there is no iframe anywhere on the page. I'm guess it must be generated in by the script which shouldn't be to much of a problem, you'll just have to find how the iframe is being added. **(nevermind I am talking about css3test, which seems like a better script)** – Josh Powell Jan 13 '14 at 16:10
2

A shorter way to do it would be to simply try the query selector, if it produces an error, return false, else true, like this:

function testSelector(selector) {
  document.querySelector('*');  //checks if querySelector is implemented and raises an error if not
  try {document.querySelector(selector)} catch (e) {return false}
  return true;
}

I checked it on IE9 Windows and Chrome Mac (V43.0.2357.130), Win(V39.0.2171.95m), FireFox Win (V38.0.5), and it works fine with testSelector("form:invalid"), which is not implemented by IE9, but by everybody else.

yogibimbi
  • 539
  • 5
  • 16