Question

I'm trying to use CSS Transitions with a :before selector and currently the only browser that supports this is Firefox. I can create a jQuery fallback with no problem, but I'm not sure how to do feature detection with a pseudo-element like that.

Here's a JSBin which shows the HTML and CSS that I'm working with.

(... and here's a similar SO question, but about using regular elements.)

Update: wow, even the platform preview of IE10 has support for this, what's up with that webkit?!

IE 10 Platform preview has CSS Transition support on :before

(Edit: solutions moved to answer below)

Was it helpful?

Solution 3

Thanks to @Asad for digging up some handy code, I was able to come up with a nice solution here's the jQuery version:

$(function() {
  var isTransitionSupported = (function (pseudo, transProp, transPropStart, transPropEnd) {
    var id = pseudo + transProp + '-' + (new Date()).valueOf(),
        prefixes = ['o', 'ms', 'moz', 'webkit'],
        prop = "transition: " + transProp + " 99s linear;",
        allprops = (function () {
          var props = "";
          for (var l = prefixes.length; l--;) {
            props += "-" + prefixes[l] + "-" + prop;
          }
          return props + prop;
        }()),
        $css = $("<style>" +
                 "#" + id + "{position:absolute;left:-999em;}" + 
                 "#" + id + ":" + pseudo + "{display:block;content:'M';" + transProp + ":" + transPropStart + ";}" + 
                 "#" + id + ".t:" + pseudo + "{" + allprops + transProp + ":" + transPropEnd + ";}" + 
                 "</style>"),
        $bct = $('<div id="' + id + '" />');

      $css.appendTo("head");
      $bct.appendTo("body");

      try {
        // get style value before any changes
        window.getComputedStyle($bct[0], ':' + pseudo).getPropertyValue(transProp);

        $bct.addClass("t");

        // test style after changes
        return (window.getComputedStyle($bct[0], ':' + pseudo).getPropertyValue(transProp) !== transPropEnd);
      } catch (e) {}
      return false;
    }("before", "width", "0px", "1000px"));
});

Here's a version that doesn't use jQuery:

var isTransitionSupported = (function (pseudo, transProp, transPropStart, transPropEnd) {
    var ticks = (new Date()).valueOf(),
        id = pseudo + transProp + '-' + ticks,
        prefixes = ['o', 'ms', 'moz', 'webkit'],
        prop = "transition: " + transProp + " 99s linear;",
        allprops = (function () {
            var props = "";
            for (var l = prefixes.length; l--;) {
                props += "-" + prefixes[l] + "-" + prop;
            }
            return props + prop;
        }()),
        body = document.body || document.createElement('body'),
        node = document.createElement('div'),
        css = "<style>" +
                    "#" + id + "{position:absolute;left:-999em;}" + 
                    "#" + id + ":" + pseudo + "{display:block;content:'M';" + transProp + ":" + transPropStart + ";}" + 
                    "#" + id + ".t" + ticks + ":" + pseudo + "{" + allprops + transProp + ":" + transPropEnd + ";}" + 
                    "</style>",
        bct = document.createElement('div'),
        isSupported = false;

    bct.id = id;
    node.innerHTML += css;
    node.appendChild(bct);
    body.appendChild(node);

    try {
        // get style value before any changes
        window.getComputedStyle(bct, ':' + pseudo).getPropertyValue(transProp);

        bct.className += "t" + ticks;

        // test style after changes
        isSupported = (window.getComputedStyle(bct, ':' + pseudo).getPropertyValue(transProp) !== transPropEnd);
    } catch (e) {}

    node.parentNode.removeChild(node);

    return isSupported;
}("before", "width", "0px", "1000px"));

document.documentElement.className += ' ' + (isTransitionSupported ? '' : 'no-') + "pseudo-trans";

Here's all that code in a gist on github, if anyone wants to fork and improve it.

OTHER TIPS

I know the recommendation states that you should use fallbacks and not polyfills, but just between you and me, using unconditional polyfills isn't all that awful for bleeding edge stuff like CSS3.

If you must know the feature status, you could use Modernizr, but if you're using a library anyway you might as well just use Selectivizr and get full CSS support cross browser.

Aha! Here is a very nice fiddle from the Modernizr folks over at github. Basically it checks if the computed style value for a pseudoelement has changed from its original value within a timespan that is shorter than the transition duration. The problem, of course, is the unreliability of using setTimeouts (syncing problems) and the fact that you need to postpone everything until the setTimeout test is complete. Check your console to see whether the browser has pseudoelement transitions or not.

To be fair, upcoming IE10 does support transitions on generated content (tested myself on Windows 8 Enterprise trial).

Actually, I'm sure there is no need for detection of this type of things. Transitions are just slight improvement, not a critical functionality. So it's perfectly OK to have transitions working in more advanced browsers while not having it in less advanced ones.

But if you need to detect it, considering this cannot be reliably detected directly, you could use browser engine detection by testing existence of standard global JS objects. For example, since we know IE10 has support for transitioning generated content, we can quite future-proofly filter IE9 and older IEs with document.all && !window.atob condition. Opera can be detected by testing existence of window.opera, so when Opera will fix this issue, we could use window.opera && !someGlobalObjectAddedInFixedOpera condition to detect older versions. It's possible that WebKit can be detected some similar way.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top