Skip to content

Instantly share code, notes, and snippets.

@branneman
Last active May 12, 2016 09:14
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save branneman/5e4c23c8a9584f85410c to your computer and use it in GitHub Desktop.
Save branneman/5e4c23c8a9584f85410c to your computer and use it in GitHub Desktop.
Feature detect Flexbox support with JavaScript
define(function() {
var cssomPrefixes = 'Moz O ms Webkit'.split(' ');
var modElem = {
elem: document.createElement('modernizr')
};
var mStyle = {
style: modElem.elem.style
};
function cssToDOM(name) {
return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) {
return m1 + m2.toUpperCase();
}).replace(/^-/, '');
}
function domToCSS(name) {
return name.replace(/([A-Z])/g, function(str, m1) {
return '-' + m1.toLowerCase();
}).replace(/^ms-/, '-ms-');
}
function nativeTestProps(props, value) {
var i = props.length;
if ('CSS' in window && 'supports' in window.CSS) {
while (i--) {
if (window.CSS.supports(domToCSS(props[i]), value)) {
return true;
}
}
return false;
} else if ('CSSSupportsRule' in window) {
var conditionText = [];
while (i--) {
conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')');
}
conditionText = conditionText.join(' or ');
var query = '@supports (' + conditionText + ') { #modernizr { position: absolute; } }';
return injectElementWithStyles(query, function(node) {
return getComputedStyle(node, null).position == 'absolute';
});
}
return undefined;
}
function testProps(props, prefixed, value, skipValueTest) {
skipValueTest = typeof skipValueTest === 'undefined' ? false : skipValueTest;
if (typeof value !== 'undefined') {
var result = nativeTestProps(props, value);
if (typeof result !== 'undefined') {
return result;
}
}
var afterInit, i, propsLength, prop, before;
var elems = ['modernizr', 'tspan'];
while (!mStyle.style) {
afterInit = true;
mStyle.modElem = document.createElement(elems.shift());
mStyle.style = mStyle.modElem.style;
}
function cleanElems() {
if (afterInit) {
delete mStyle.style;
delete mStyle.modElem;
}
}
propsLength = props.length;
for (i = 0; i < propsLength; i++) {
prop = props[i];
before = mStyle.style[prop];
if (!!~('' + prop).indexOf('-')) {
prop = cssToDOM(prop);
}
if (mStyle.style[prop] !== undefined) {
if (!skipValueTest && typeof value !== 'undefined') {
try {
mStyle.style[prop] = value;
} catch (e) {}
if (mStyle.style[prop] != before) {
cleanElems();
return prefixed == 'pfx' ? prop : true;
}
} else {
cleanElems();
return prefixed == 'pfx' ? prop : true;
}
}
}
cleanElems();
return false;
}
function testAllProps(prop, prefixed, value, skipValueTest) {
var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1);
var props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
return testProps(props, prefixed, value, skipValueTest);
}
return {
flexbox: testAllProps('flexBasis', '1px'),
legacy: testAllProps('boxDirection', 'reverse'),
tweener: testAllProps('flexAlign', 'end'),
wrap: testAllProps('flexWrap', 'wrap')
};
});
@davidhund
Copy link

Hey Bran, this looks interesting, mainly because it tests all flexbox variants. I usually only test the new syntax with a very simple test like this but that obviously misses out the 'legacy' and 'tweener' versions.

However: Legacy and Tweener are probably going extinct soonish so I wonder if this is a bit much?
Also: can you tell me how these tests are different from Modernizr tests?

@sompylasar
Copy link

This code is broken:

  1. injectElementWithStyles is missing.
  2. testAllProps takes prefixed as the second argument, but a value is passed there instead ('1px', 'reverse', 'end', 'wrap').

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment