Reputation: 2688
How can I access a property from a CSS class by jQuery? I have a CSS class like:
.highlight {
color: red;
}
And I need to do a color animation on an object:
$(this).animate({
color: [color of highlight class]
}, 750);
So that I can change from red
to blue
(in the CSS) and my animation will work in accordance with my CSS.
One approach would be to place an invisible element with the highlight
class and then get the color of the element to use in the animation, but I guess this is a very, very bad practice.
Any suggestions?
Upvotes: 39
Views: 63772
Reputation: 41
Unfortunately I can't comment on this awesome answer, but found a case that isn't catered for (when CSS class is declared multiple times and the first declaration doesn't have the style you're looking for), made a jsFiddle to cater for it:
function getStyleRuleValue(style, selector, sheet) {
var sheets = typeof sheet !== 'undefined' ? [sheet] : document.styleSheets;
for (var i = 0, l = sheets.length; i < l; i++) {
var sheet = sheets[i];
if( !sheet.cssRules ) { continue; }
for (var j = 0, k = sheet.cssRules.length; j < k; j++) {
var rule = sheet.cssRules[j];
if (rule.selectorText && rule.selectorText.indexOf(selector) !== -1 && rule.style[style] !== '') {
return rule.style[style];
}
}
}
return null;
}
Also took out the split in the conditional, not necessary and now it confirms that the style is present in the rule being checked.
Just for shigiggles created a jsFiddle to cache the styles by selector:
var styleCache = {};
function getStyleRuleValue(style, selector, sheet) {
if (typeof styleCache[selector] !== 'undefined') {
if (typeof styleCache[selector][style] !== 'undefined') {
return styleCache[selector][style];
}
}
else {
styleCache[selector] = {};
}
var sheets = typeof sheet !== 'undefined' ? [sheet] : document.styleSheets;
for (var i = 0, l = sheets.length; i < l; i++) {
var sheet = sheets[i];
if( !sheet.cssRules ) { continue; }
for (var j = 0, k = sheet.cssRules.length; j < k; j++) {
var rule = sheet.cssRules[j];
if (rule.selectorText && rule.selectorText.indexOf(selector) !== -1 && rule.style[style] !== '') {
return styleCache[selector][style] = rule.style[style];
}
}
}
return null;
}
Although if you do use that, I would suggest putting it in a closure/class. Thanks again to rlemon for the awesome original.
Upvotes: 1
Reputation: 33228
The only solution that come to my mind is the following:
//create an element with this class and append it to the DOM
var eleToGetColor = $('<div class="highlight" style="display: none;">').appendTo('body');
//get the color of the element
var color = eleToGetColor.css('color');
//completly remove the element from the DOM
eleToGetColor.remove();
$("div").animate({
//set the new color
color: color,
}, 1000);
.highlight {
color: red;
}
div {
width: 200px;
height: 100px;
color: blue;
font-size: 6em;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/themes/smoothness/jquery-ui.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/jquery-ui.min.js"></script>
<div>TEST</div>
Upvotes: 2
Reputation: 273
Why don't add .highlighted
class, cache the color
style, than remove it and animate to cached color? I.e. don't append elements and don't parse & loop styles.
var $element = $('.my-class').addClass('highlighted');
var colorToAnimate = $element.css('color');
$element.removeClass('highlighted');
alert(colorToAnimate);
.my-class {
color: blue;
}
.highlighted {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<span class="my-class">Animated color text</span>
Upvotes: 1
Reputation: 2455
Here's another method: add a hidden div with the class applied. Use jQuery.css to look up the style value. Then remove the element.
http://plnkr.co/edit/Cu4lPbaJWHW42vgsk9ey
function getStyleValue(className, style) {
var elementId = 'test-' + className,
testElement = document.getElementById(elementId),
val;
if (testElement === null) {
testElement = document.createElement('div');
testElement.className = className;
testElement.style.display = 'none';
document.body.appendChild(testElement);
}
val = $(testElement).css(style);
document.body.removeChild(testElement);
return val;
}
console.log( 'The style value is ' + getStyleValue('dark-red', 'color') );
Upvotes: 2
Reputation: 6086
I've just wrote this function get all styles by a selector. Notice: The selector must be the same as in the CSS.
/**
* Gets styles by a classname
*
* @notice The className must be 1:1 the same as in the CSS
* @param string className_
*/
function getStyle(className_) {
var styleSheets = global_.document.styleSheets;
var styleSheetsLength = styleSheets.length;
for(var i = 0; i < styleSheetsLength; i++){
var classes = styleSheets[i].rules || styleSheets[i].cssRules;
var classesLength = classes.length;
for (var x = 0; x < classesLength; x++) {
if (classes[x].selectorText == className_) {
var ret;
if(classes[x].cssText){
ret = classes[x].cssText;
} else {
ret = classes[x].style.cssText;
}
if(ret.indexOf(classes[x].selectorText) == -1){
ret = classes[x].selectorText + "{" + ret + "}";
}
return ret;
}
}
}
}
Upvotes: 1
Reputation: 17667
I wrote a small function that traverses the stylesheets on the document looking for the matched selector, then style.
There is one caveat, this will only work for style sheets defined with a style tag, or external sheets from the same domain.
If the sheet is known you can pass it in and save yourself from having to look in multiple sheets (faster and if you have colliding rules it's more exact).
I only tested on jsFiddle with some weak test cases, let me know if this works for you.
function getStyleRuleValue(style, selector, sheet) {
var sheets = typeof sheet !== 'undefined' ? [sheet] : document.styleSheets;
for (var i = 0, l = sheets.length; i < l; i++) {
var sheet = sheets[i];
if( !sheet.cssRules ) { continue; }
for (var j = 0, k = sheet.cssRules.length; j < k; j++) {
var rule = sheet.cssRules[j];
if (rule.selectorText && rule.selectorText.split(',').indexOf(selector) !== -1) {
return rule.style[style];
}
}
}
return null;
}
example usage:
var color = getStyleRuleValue('color', '.foo'); // searches all sheets for the first .foo rule and returns the set color style.
var color = getStyleRuleValue('color', '.foo', document.styleSheets[2]);
I neglected to take into consideration grouped rules. I changed the selector check to this:
if (rule.selectorText.split(',').indexOf(selector) !== -1) {
now it will check if any of the selectors in a grouped rules matches.
Upvotes: 77
Reputation: 6416
How about this?
$('<span class="highlight"></span>').appendTo('body');
$(this).animate({
color: $('.highlight').css('color')
}, 750);
$('.highlight').remove();
It's kind of dirty but will give you an (empty) element to reference to get the CSS value for which you are looking.
Update Here is a decent solution from CSS parser/abstracter? How to convert stylesheet into object
function findColorProperty(selector) {
rules = document.styleSheets[0].cssRules
for(i in rules) {
//if(rules[i].selectorText==selector)
//return rules[i].cssText; // Original
if(rules[i].selectorText == selector)
return rules[i].style.color; // Get color property specifically
}
return false;
}
Usage
$(this).animate({
color: findColorProperty('.highlight')
}, 750);
Here is a fiddle http://jsfiddle.net/wzXDx/1/. I had to use styleSheets[1]
to get this to work in the fiddle due to the embedded nature of the environment.
Upvotes: 2
Reputation: 146350
Since you are already using jQuery try using the jQuery-ui's function switchClass
which would allow you to animate to this new color.
For example:
$('div').switchClass( "", "highlight", 1000 );
In case you do not want to include the whole UI library here is the code they use:
switchClass: function( remove, add, speed, easing, callback) {
return $.effects.animateClass.call( this, {
add: add,
remove: remove
}, speed, easing, callback );
}
And the animateClass fn:
var classAnimationActions = [ "add", "remove", "toggle" ],
shorthandStyles = {
border: 1,
borderBottom: 1,
borderColor: 1,
borderLeft: 1,
borderRight: 1,
borderTop: 1,
borderWidth: 1,
margin: 1,
padding: 1
};
function styleDifference( oldStyle, newStyle ) {
var diff = {},
name, value;
for ( name in newStyle ) {
value = newStyle[ name ];
if ( oldStyle[ name ] !== value ) {
if ( !shorthandStyles[ name ] ) {
if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
diff[ name ] = value;
}
}
}
}
return diff;
}
function getElementStyles( elem ) {
var key, len,
style = elem.ownerDocument.defaultView ?
elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
elem.currentStyle,
styles = {};
if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
len = style.length;
while ( len-- ) {
key = style[ len ];
if ( typeof style[ key ] === "string" ) {
styles[ $.camelCase( key ) ] = style[ key ];
}
}
// support: Opera, IE <9
} else {
for ( key in style ) {
if ( typeof style[ key ] === "string" ) {
styles[ key ] = style[ key ];
}
}
}
return styles;
}
$.effects.animateClass = function( value, duration, easing, callback ) {
var o = $.speed( duration, easing, callback );
return this.queue( function() {
var animated = $( this ),
baseClass = animated.attr( "class" ) || "",
applyClassChange,
allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
// map the animated objects to store the original styles.
allAnimations = allAnimations.map(function() {
var el = $( this );
return {
el: el,
start: getElementStyles( this )
};
});
// apply class change
applyClassChange = function() {
$.each( classAnimationActions, function(i, action) {
if ( value[ action ] ) {
animated[ action + "Class" ]( value[ action ] );
}
});
};
applyClassChange();
// map all animated objects again - calculate new styles and diff
allAnimations = allAnimations.map(function() {
this.end = getElementStyles( this.el[ 0 ] );
this.diff = styleDifference( this.start, this.end );
return this;
});
// apply original class
animated.attr( "class", baseClass );
// map all animated objects again - this time collecting a promise
allAnimations = allAnimations.map(function() {
var styleInfo = this,
dfd = $.Deferred(),
opts = $.extend({}, o, {
queue: false,
complete: function() {
dfd.resolve( styleInfo );
}
});
this.el.animate( this.diff, opts );
return dfd.promise();
});
// once all animations have completed:
$.when.apply( $, allAnimations.get() ).done(function() {
// set the final class
applyClassChange();
// for each animated element,
// clear all css properties that were animated
$.each( arguments, function() {
var el = this.el;
$.each( this.diff, function(key) {
el.css( key, "" );
});
});
// this is guarnteed to be there if you use jQuery.speed()
// it also handles dequeuing the next anim...
o.complete.call( animated[ 0 ] );
});
});
};
Working fiddle with all the functions that are needed: http://jsfiddle.net/maniator/3C5ZQ/
Upvotes: 4