Testing CSS support with JavaScript

Nausicaä

jsunconf 2014, 2014-04-26

molily

Chaplin.js

Testing CSS support

  • Does the browser support a property?
  • Does the browser support a specific property value?
  • What’s the vendor prefix, if any?

Why bother?

  • General rule: Avoid inline styles, toggle classes
  • Separation of concerns:
    JavaScript: Behavior, CSS: Presentation
  • Sometimes inline styles are necessary
    • Trigger CSS transitions
    • Manual step animations (like jQuery#animate)
  • Load a polyfill or a less-fancy fallback

CSS Object Model (CSSOM)

CSSOM Introduction

  • Inline Styles
    • el.style.property = 'value';
    • HTML attribute style='property: value;'`
  • Computed style
    • getComputedStyle(el).property
  • Stylesheets
    • #foo { color: red; }
    • document.styleSheets

CSSOM Introduction

Stylesheets


#foo { color: red;  }

Inline styles vs. computed styles


var el = document.getElementById('foo');
console.log( el.style.color ); // ''
console.log( getComputedStyle(el).color ); // 'red'

el.style.color = 'blue';
console.log( el.style.color ); // blue
console.log( getComputedStyle(el).color ); // 'blue'

el.style.color = '';
console.log( getComputedStyle(el).color ); // 'red'

CSS vs. JavaScript naming

  • CSS uses dashed property names:
    border-bottom-right-radius
  • JavaScript uses camelCase:
    borderBottomRightRadius
  • It’s easy to transform them
  • camelize and dasherize from Prototype.js, underscore.string, …

Vendor prefixes #yolo

Experimental implementations


-moz-transform: scale(2);
-webkit-transform: scale(2);
-ms-transform: scale(2);
transform: scale(2);

el.style.MozTransform
el.style.WebkitTransform
el.style.msTransform // Yes
el.style.transform

caniuse.com

Modernizr

Modernizr’s approach

  • I will talk about the internals of the upcoming version 3.0 (master branch)
  • Modernizr tries three detection methods
  • For all possible properties (with vendor prefixes), do:
    1. CSS.supports(property, value) else
    2. Use an @supports rule else
    3. Check object properties

CSS.supports(property, value)

@supports CSS rule

  1. Create a style element, append it to the head
  2. Add a @supports rule with a normal rule that styles an element in the DOM
    
      @supports (transform-style: preserve-3d) {
        #test-element { position: absolute; }
      }
      
  3. Read the computed style of the element
    var supported = getComputedStyle(el).position === 'absolute';

@supports CSS rule

Last resort: Check object properties

  1. Test if JavaScript property exists (must be a string)
    'property' in el.style
  2. Set the value to test
    el.style.property = 'value';
  3. Check if the browser accepted the value. If not, the value is an empty string.
    var supported = el.style.property === 'valueToTest';

Credits <3

Questions? Feedback?