CSS: Support custom properties

Fixes gh-3144
Closes gh-3199
Closes gh-3557
This commit is contained in:
Connor Atherton 2016-06-25 10:21:00 -07:00 committed by Michał Gołębiowski
parent be041e4da4
commit 619bf98d5b
3 changed files with 99 additions and 9 deletions

View File

@ -28,6 +28,7 @@ var
// except "table", "table-cell", or "table-caption"
// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
rdisplayswap = /^(none|table(?!-c[ea]).+)/,
rcustomProp = /^--/,
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
cssNormalTransform = {
letterSpacing: "0",
@ -57,6 +58,16 @@ function vendorPropName( name ) {
}
}
// Return a property mapped along what jQuery.cssProps suggests or to
// a vendor prefixed property.
function finalPropName( name ) {
var ret = jQuery.cssProps[ name ];
if ( !ret ) {
ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name;
}
return ret;
}
function setPositiveNumber( elem, value, subtract ) {
// Any relative (+/-) values have already been
@ -218,10 +229,15 @@ jQuery.extend( {
// Make sure that we're working with the right name
var ret, type, hooks,
origName = jQuery.camelCase( name ),
isCustomProp = rcustomProp.test( name ),
style = elem.style;
name = jQuery.cssProps[ origName ] ||
( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
// Make sure that we're working with the right name. We don't
// want to query the value if it is a CSS custom property
// since they are user-defined.
if ( !isCustomProp ) {
name = finalPropName( origName );
}
// Gets hook for the prefixed version, then unprefixed version
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
@ -257,8 +273,12 @@ jQuery.extend( {
if ( !hooks || !( "set" in hooks ) ||
( value = hooks.set( elem, value, extra ) ) !== undefined ) {
if ( isCustomProp ) {
style.setProperty( name, value );
} else {
style[ name ] = value;
}
}
} else {
@ -276,11 +296,15 @@ jQuery.extend( {
css: function( elem, name, extra, styles ) {
var val, num, hooks,
origName = jQuery.camelCase( name );
origName = jQuery.camelCase( name ),
isCustomProp = rcustomProp.test( name );
// Make sure that we're working with the right name
name = jQuery.cssProps[ origName ] ||
( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
// Make sure that we're working with the right name. We don't
// want to modify the value if it is a CSS custom property
// since they are user-defined.
if ( !isCustomProp ) {
name = finalPropName( origName );
}
// Try prefixed name followed by the unprefixed name
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
@ -305,6 +329,7 @@ jQuery.extend( {
num = parseFloat( val );
return extra === true || isFinite( num ) ? num || 0 : val;
}
return val;
}
} );

View File

@ -15,8 +15,9 @@ function curCSS( elem, name, computed ) {
computed = computed || getStyles( elem );
// Support: IE <=9 only
// getPropertyValue is only needed for .css('filter') (#12537)
// getPropertyValue is needed for:
// .css('filter') (IE 9 only, #12537)
// .css('--customProperty) (#3144)
if ( computed ) {
ret = computed.getPropertyValue( name ) || computed[ name ];

View File

@ -1556,4 +1556,68 @@ QUnit.test( "Do not throw on frame elements from css method (#15098)", function(
} )();
QUnit.test( "css(--customProperty)", function( assert ) {
jQuery( "#qunit-fixture" ).append(
"<style>\n" +
" .test__customProperties {\n" +
" --prop1:val1;\n" +
" --prop2: val2;\n" +
" --prop3:val3 ;\n" +
" --prop4:\"val4\";\n" +
" --prop5:'val5';\n" +
" }\n" +
"</style>"
);
var div = jQuery( "<div>" ).appendTo( "#qunit-fixture" ),
$elem = jQuery( "<div>" ).addClass( "test__customProperties" ).appendTo( "#qunit-fixture" ),
webkit = /\bsafari\b/i.test( navigator.userAgent ) &&
!/\firefox\b/i.test( navigator.userAgent ) &&
!/\edge\b/i.test( navigator.userAgent ),
oldSafari = webkit && ( /\b9\.\d(\.\d+)* safari/i.test( navigator.userAgent ) ||
/\b10\.0(\.\d+)* safari/i.test( navigator.userAgent ) ),
expected = 10;
if ( webkit ) {
expected -= 2;
}
if ( oldSafari ) {
expected -= 2;
}
assert.expect( expected );
div.css( "--color", "blue" );
assert.equal( div.css( "--color" ), "blue", "Modified CSS custom property using string" );
div.css( "--color", "yellow" );
assert.equal( div.css( "--color" ), "yellow", "Overwrite CSS custom property" );
div.css( { "--color": "red" } );
assert.equal( div.css( "--color" ), "red", "Modified CSS custom property using object" );
div.css( { "--mixedCase": "green" } );
assert.equal( div.css( "--mixedCase" ), "green", "Modified CSS custom property with mixed case" );
div.css( { "--theme-dark": "purple" } );
assert.equal( div.css( "--theme-dark" ), "purple", "Modified CSS custom property with dashed name" );
assert.equal( $elem.css( "--prop1" ), "val1", "Basic CSS custom property" );
// Support: Safari 9.1-10.0 only
// Safari collapses whitespaces & quotes. Ignore it.
if ( !oldSafari ) {
assert.equal( $elem.css( "--prop2" ), " val2", "Preceding whitespace maintained" );
assert.equal( $elem.css( "--prop3" ), "val3 ", "Following whitespace maintained" );
}
// Support: Chrome 49-55, Safari 9.1-10.0
// Chrome treats single quotes as double ones.
// Safari treats double quotes as single ones.
if ( !webkit ) {
assert.equal( $elem.css( "--prop4" ), "\"val4\"", "Works with double quotes" );
assert.equal( $elem.css( "--prop5" ), "'val5'", "Works with single quotes" );
}
} );
}