The premise is not entirely correct: at least for Firefox, this is not CSS transitions which doesn't work, but recognizing x
and y
attributes as presentational attributes and hence as CSS settable and hence as CSS transition-able.
This wasn't the case in SVG1.1 and has still to be implemented in FF and IE, but SVG1.1 CSS transitions do work in FF.
To feature-detect x
and y
, you could simply check if an SVGElement has these properties available on its style
property.
var supportSVG2XYasCSS =
document.createElementNS("http://www.w3.org/2000/svg", 'foo')
.style.x !== undefined;
console.log("Your browser does%s support setting x and y attributes through CSS" +
" and thus probably%s CSS transitions on it.",
supportSVG2XYasCSS ? '' : "n't",
supportSVG2XYasCSS ? '' : ' neither does it support');
But, HTMLElements do have a height
property on their style
, and Firefox, IE and Edge will set it to SVGElement regardless it shouldn't be there.
So you can't feature detect these attributes by another mean than checking if the BBox of the element you did set these properties on has changed:
var prev = elem.getBBox();
elem.setAttribute('class', 'trig');
var next = elem.getBBox();
console.log({
width: prev.width !== next.width,
height: prev.height !== next.height,
x: prev.x !== next.x,
y: prev.y !== next.y
});
#elem{
width: 10px;
height: 10px;
x: 0;
y: 0;
}
#elem.trig{
width: 100px;
height: 100px;
x: 10;
y: 10;
}
<svg>
<rect id="elem"/>
</svg>
Now let's get back at detecting CSS transitions available on SVG.
So as said previously, Firefox does support all SVG1.1 transitions, but not the SVG2 ones, Edge does support some SVG1.1 transitions, and IE supports none.
So to feature detect it, you would have to check every properties on their own, and to check a transition property you can set up something like this:
- set initial value
- get computed value (initial state)
- set destination value
- trigger a reflow so the new values are computed and the transition can kick in
- get computed value (transition state)
If the transition is working, the computed values in the transition state should be the same as the ones in the initial state, if not supported, the destination value would be set directly.
But, as stated in the first part, some HTMLElement's CSSproperties will leak on our tests and would thus require some more work, that I will let for the readers.
Note that arguably, these are not "false positives" since browsers will indeed trigger a transition on them, they will simply have no visual effect on the element...
function testCSSTransitionsOnSVG() {
// all the properties we'll test
var props = {
// stable(?) ones
'fill': {initial: 'rgba(255, 255, 255, 1)', after: 'rgba(0, 0, 0, 1)'},
'stroke-dasharray': {initial: '1, 1', after: '1000, 1000'},
'stroke-dashoffset': {initial: '0%', after: '100%'},
'stroke-width': {initial: '1', after: '5'},
'stroke': {initial: 'rgba(0, 0, 0, 1)', after: 'rgba(255, 255, 255, 1)'},
'x': {initial: '0px', after: '100px'},
'y': {initial: '0px', after: '100px'},
// false positives in IE & Edge
'transform': {initial: 'matrix(1,0,0,1,0,0)', after: 'matrix(25,1.2,1.2,25,42,42)'},
'transform-origin': {initial: '0px 0px', after: '100px, 100px'},
// false positives in FF & IE & Edge
'width': {initial: '0px', after: '100px'},
'height': {initial: '0px', after: '100px'}
}, key;
// set up our testing elements
var SVGns = 'http://www.w3.org/2000/svg',
parent = document.createElementNS(SVGns, 'svg');
parent.setAttribute('class', '__tester__');
var style = document.createElementNS(SVGns, 'style');
// write up our initial CSS rules using a strict selector
style.textContent += 'body > .__tester__ > rect{transition:all 10s linear;';
for (key in props) {
style.textContent += key + ':' + props[key].initial + ';';
}
// the ones we should animate to
style.textContent += '}\n body > .__tester__.trig > rect{';
for (key in props) {
style.textContent += key + ':' + props[key].after + ';';
}
style.textContent += '}';
parent.appendChild(style);
// our actual element to be tested
var elem = document.createElementNS(SVGns, 'rect');
elem.setAttribute('width', '100%');
elem.setAttribute('height', '100%');
parent.appendChild(elem);
// now let's start the real checks
document.body.appendChild(parent);
parent.offsetWidth; // force a reflow
// get the initial computed values
var results = {},
computed = getComputedStyle(elem);
for (key in props) {
results[key] = computed[key];
}
// set the new class
parent.setAttribute('class', '__tester__ trig');
parent.offsetWidth; // force a reflow
computed = getComputedStyle(elem);
var support = {};
for (key in props) {
support[key] = results[key] === computed[key] && computed[key] !== undefined;
}
document.body.removeChild(parent);
return support;
}
console.log(testCSSTransitionsOnSVG());