Skip to content
Permalink
Browse files

Split apart jQuery.css into jQuery.css (computed values) and jQuery.s…

…tyle (currently set values).
  • Loading branch information...
jeresig committed Sep 16, 2010
1 parent 2131e1a commit 37b607d2815b893d13de4ac3461090d0dd46535e
Showing with 85 additions and 65 deletions.
  1. +56 −28 src/css.js
  2. +2 −2 src/dimensions.js
  3. +11 −11 src/effects.js
  4. +9 −9 src/offset.js
  5. +2 −10 test/unit/css.js
  6. +5 −5 test/unit/effects.js
@@ -26,6 +26,8 @@ jQuery.fn.css = function( name, value ) {
};

jQuery.extend({
// Add in style property hooks for overriding the default
// behavior of getting and setting a style property
cssHooks: {
opacity: {
get: function( elem ) {
@@ -36,7 +38,7 @@ jQuery.extend({
}
},

// exclude the following css properties to add px
// Exclude the following css properties to add px
cssNumber: {
"zIndex": true,
"fontWeight": true,
@@ -45,43 +47,67 @@ jQuery.extend({
"lineHeight": true
},

// Add in properties whose names you wish to fix before
// setting or getting the value
cssProps: {
// normalize float css property
"float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
},

css: function( elem, name, value, force, extra ) {
// don't set styles on text and comment nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
// Get and set the style property on a DOM Node
style: function( elem, name, value, extra ) {
// Don't set styles on text and comment nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
return undefined;
}

// Make sure that we're working with the right name
var ret, origName = name.replace( rdashAlpha, fcamelCase ),
style = elem.style || {}, hooks = jQuery.cssHooks[ origName ] || {};
style = elem.style, hooks = jQuery.cssHooks[ origName ];

name = jQuery.cssProps[ origName ] || origName;

// Check if we're setting a value
if ( value !== undefined ) {
// If a number was passed in, add 'px' to the (except for certain CSS properties)
if ( typeof value === "number" && !jQuery.cssNumber[ origName ] ) {
value += "px";
}

if ( !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
// If a hook was provided, use that value, otherwise just set the specified value
if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
style[ name ] = value;
}

} else {
if ( !force && "get" in hooks && (ret = hooks.get( elem, force, extra )) !== undefined ) {
// If a hook was provided get the non-computed value from there
if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
return ret;
}

} else if ( !force && style[ name ] ) {
ret = style[ name ];
// Otherwise just get the value from the style object
return style[ name ];
}
},

} else if ( force !== false && curCSS ) {
ret = curCSS( elem, name, origName );
}
css: function( elem, name, value, extra ) {
// Make sure that we're working with the right name
var ret, origName = name.replace( rdashAlpha, fcamelCase ),
hooks = jQuery.cssHooks[ origName ];

name = jQuery.cssProps[ origName ] || origName;

// Check if we're setting a value, just use jQuery.style (DEPRECATED)
if ( value !== undefined ) {
jQuery.style( elem, name, value );

// If a hook was provided get the computed value from there
} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
return ret;

// Otherwise, if a way to get the computed value exists, use that
} else if ( curCSS ) {
return curCSS( elem, name, origName );
}
},

@@ -106,19 +132,21 @@ jQuery.extend({

jQuery.each(["height", "width"], function( i, name ) {
jQuery.cssHooks[ name ] = {
get: function( elem, force, extra ) {
get: function( elem, computed, extra ) {
var val;

if ( elem.offsetWidth !== 0 ) {
val = getWH( elem, name, extra );

} else {
jQuery.swap( elem, cssShow, function() {
if ( computed ) {
if ( elem.offsetWidth !== 0 ) {
val = getWH( elem, name, extra );
});
}

return val + "px";
} else {
jQuery.swap( elem, cssShow, function() {
val = getWH( elem, name, extra );
});
}

return val + "px";
}
},

set: function( elem, value ) {
@@ -134,9 +162,9 @@ jQuery.each(["height", "width"], function( i, name ) {

if ( !jQuery.support.opacity ) {
jQuery.cssHooks.opacity = {
get: function( elem, force ) {
get: function( elem, computed ) {
// IE uses filters for opacity
return ropacity.test(elem.currentStyle.filter || "") ?
return ropacity.test((computed ? elem.currentStyle.filter : elem.style.filter) || "") ?
(parseFloat(RegExp.$1) / 100) + "" :
"1";
},
@@ -153,7 +181,7 @@ if ( !jQuery.support.opacity ) {
"" :
"alpha(opacity=" + value * 100 + ")";

var filter = style.filter || jQuery.css( elem, "filter" ) || "";
var filter = style.filter || elem.currentStyle.filter || "";

style.filter = ralpha.test(filter) ?
filter.replace(ralpha, opacity) :
@@ -217,14 +245,14 @@ function getWH( elem, name, extra ) {

jQuery.each( which, function() {
if ( !extra ) {
val -= parseFloat(jQuery.css( elem, "padding" + this, undefined, true )) || 0;
val -= parseFloat(jQuery.css( elem, "padding" + this )) || 0;
}

if ( extra === "margin" ) {
val += parseFloat(jQuery.css( elem, "margin" + this, undefined, true )) || 0;
val += parseFloat(jQuery.css( elem, "margin" + this )) || 0;

} else {
val -= parseFloat(jQuery.css( elem, "border" + this + "Width", undefined, true )) || 0;
val -= parseFloat(jQuery.css( elem, "border" + this + "Width" )) || 0;
}
});

@@ -240,7 +268,7 @@ if ( jQuery.expr && jQuery.expr.filters ) {
true :
width > 0 && height > 0 && !skip ?
false :
jQuery.css(elem, "display") === "none";
(elem.style.display || jQuery.css( elem, "display" )) === "none";
};

jQuery.expr.filters.visible = function( elem ) {
@@ -8,14 +8,14 @@ jQuery.each([ "Height", "Width" ], function( i, name ) {
// innerHeight and innerWidth
jQuery.fn["inner" + name] = function() {
return this[0] ?
parseFloat( jQuery.css( this[0], type, undefined, false, "padding" ), 10 ) :
parseFloat( jQuery.css( this[0], type, undefined, "padding" ), 10 ) :
null;
};

// outerHeight and outerWidth
jQuery.fn["outer" + name] = function( margin ) {
return this[0] ?
parseFloat( jQuery.css( this[0], type, undefined, false, margin ? "margin" : "border" ), 10 ) :
parseFloat( jQuery.css( this[0], type, undefined, margin ? "margin" : "border" ), 10 ) :
null;
};

@@ -29,7 +29,7 @@ jQuery.fn.extend({

this[i].style.display = old || "";

if ( jQuery.css(this[i], "display") === "none" ) {
if ( jQuery.css( this[i], "display" ) === "none" ) {
var nodeName = this[i].nodeName, display;

if ( elemdisplay[ nodeName ] ) {
@@ -71,7 +71,7 @@ jQuery.fn.extend({
for ( var i = 0, l = this.length; i < l; i++ ) {
var old = jQuery.data(this[i], "olddisplay");
if ( !old && old !== "none" ) {
jQuery.data( this[i], "olddisplay", jQuery.css(this[i], "display") );
jQuery.data( this[i], "olddisplay", jQuery.css( this[i], "display" ) );
}
}

@@ -139,7 +139,7 @@ jQuery.fn.extend({

if ( ( p === "height" || p === "width" ) && this.style ) {
// Store display property
opt.display = jQuery.css(this, "display");
opt.display = this.style.display;

// Make sure that nothing sneaks out
opt.overflow = this.style.overflow;
@@ -316,13 +316,13 @@ jQuery.fx.prototype = {
},

// Get the current size
cur: function( force ) {
cur: function() {
if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
return this.elem[ this.prop ];
}

var r = parseFloat(jQuery.css(this.elem, this.prop, undefined, force));
return r && r > -10000 ? r : parseFloat(jQuery.css(this.elem, this.prop)) || 0;

This comment has been minimized.

Copy link
@Flcn

Flcn Nov 17, 2010

whe you remove this? parseFloat(jQuery.css(this.elem, this.prop)) ?
it breaks some applications where values can be less then -10000.

and i wonder, why exactly -10000 ? not -100000 for example

This comment has been minimized.

Copy link
@DanielBaulig

DanielBaulig Nov 28, 2010

Why that range check at all? This range check just produced a bug in my application aswell. Aswell it doesn't seem to be documented in the jQuery documentation. If there's no good reason to keep it it should be removed imho.

var r = jQuery.css( this.elem, this.prop );
return r && r > -10000 ? r : 0;
},

// Start an animation from one number to another
@@ -349,7 +349,7 @@ jQuery.fx.prototype = {
// Simple 'show' function
show: function() {
// Remember where we started, so that we can go back to it later
this.options.orig[this.prop] = jQuery.css( this.elem, this.prop, undefined, false );
this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
this.options.show = true;

// Begin the animation
@@ -364,7 +364,7 @@ jQuery.fx.prototype = {
// Simple 'hide' function
hide: function() {
// Remember where we started, so that we can go back to it later
this.options.orig[this.prop] = jQuery.css( this.elem, this.prop, undefined, false );
this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
this.options.hide = true;

// Begin the animation
@@ -397,7 +397,7 @@ jQuery.fx.prototype = {
var old = jQuery.data(this.elem, "olddisplay");
this.elem.style.display = old ? old : this.options.display;

if ( jQuery.css(this.elem, "display") === "none" ) {
if ( jQuery.css( this.elem, "display" ) === "none" ) {
this.elem.style.display = "block";
}
}
@@ -410,7 +410,7 @@ jQuery.fx.prototype = {
// Reset the properties, if the item has been hidden or shown
if ( this.options.hide || this.options.show ) {
for ( var p in this.options.curAnim ) {
jQuery.css( this.elem, p, this.options.orig[p] );
jQuery.style( this.elem, p, this.options.orig[p] );
}
}

@@ -467,7 +467,7 @@ jQuery.extend( jQuery.fx, {

step: {
opacity: function( fx ) {
jQuery.css( fx.elem, "opacity", fx.now );
jQuery.style( fx.elem, "opacity", fx.now );
},

_default: function( fx ) {
@@ -105,7 +105,7 @@ if ( "getBoundingClientRect" in document.documentElement ) {

jQuery.offset = {
initialize: function() {
var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop", undefined, true) ) || 0,
var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0,
html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";

jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
@@ -144,8 +144,8 @@ jQuery.offset = {
jQuery.offset.initialize();

if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
top += parseFloat( jQuery.css(body, "marginTop", undefined, true) ) || 0;
left += parseFloat( jQuery.css(body, "marginLeft", undefined, true) ) || 0;
top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
}

return { top: top, left: left };
@@ -161,8 +161,8 @@ jQuery.offset = {

var curElem = jQuery( elem ),
curOffset = curElem.offset(),
curCSSTop = jQuery.css( elem, "top", undefined, true ),
curCSSLeft = jQuery.css( elem, "left", undefined, true ),
curCSSTop = jQuery.css( elem, "top" ),
curCSSLeft = jQuery.css( elem, "left" ),
calculatePosition = (position === "absolute" && jQuery.inArray('auto', [curCSSTop, curCSSLeft]) > -1),
props = {}, curPosition = {}, curTop, curLeft;

@@ -212,12 +212,12 @@ jQuery.fn.extend({
// Subtract element margins
// note: when an element has margin: auto the offsetLeft and marginLeft
// are the same in Safari causing offset.left to incorrectly be 0
offset.top -= parseFloat( jQuery.css(elem, "marginTop", undefined, true) ) || 0;
offset.left -= parseFloat( jQuery.css(elem, "marginLeft", undefined, true) ) || 0;
offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;

// Add offsetParent borders
parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth", undefined, true) ) || 0;
parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth", undefined, true) ) || 0;
parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;

// Subtract the two offsets
return {
@@ -1,7 +1,7 @@
module("css");

test("css(String|Hash)", function() {
expect(30);
expect(28);

equals( jQuery('#main').css("display"), 'none', 'Check for css property "display"');

@@ -19,10 +19,6 @@ test("css(String|Hash)", function() {
equals( parseFloat(jQuery('#nothiddendiv').css('width')), width, 'Test negative width ignored')
equals( parseFloat(jQuery('#nothiddendiv').css('height')), height, 'Test negative height ignored')

jQuery('#floatTest').css({styleFloat: 'right'});
equals( jQuery('#floatTest').css('styleFloat'), 'right', 'Modified CSS float using "styleFloat": Assert float is right');
jQuery('#floatTest').css({cssFloat: 'left'});
equals( jQuery('#floatTest').css('cssFloat'), 'left', 'Modified CSS float using "cssFloat": Assert float is left');
jQuery('#floatTest').css({'float': 'right'});
equals( jQuery('#floatTest').css('float'), 'right', 'Modified CSS float using "float": Assert float is right');
jQuery('#floatTest').css({'font-size': '30px'});
@@ -65,7 +61,7 @@ test("css(String|Hash)", function() {
});

test("css(String, Object)", function() {
expect(21);
expect(19);
ok( jQuery('#nothiddendiv').is(':visible'), 'Modifying CSS display: Assert element is visible');
jQuery('#nothiddendiv').css("display", 'none');
ok( !jQuery('#nothiddendiv').is(':visible'), 'Modified CSS display: Assert element is hidden');
@@ -75,10 +71,6 @@ test("css(String, Object)", function() {
jQuery("#nothiddendiv").css("top", "-1em");
ok( jQuery("#nothiddendiv").css("top"), -16, "Check negative number in EMs." );

jQuery('#floatTest').css('styleFloat', 'left');
equals( jQuery('#floatTest').css('styleFloat'), 'left', 'Modified CSS float using "styleFloat": Assert float is left');
jQuery('#floatTest').css('cssFloat', 'right');
equals( jQuery('#floatTest').css('cssFloat'), 'right', 'Modified CSS float using "cssFloat": Assert float is right');
jQuery('#floatTest').css('float', 'left');
equals( jQuery('#floatTest').css('float'), 'left', 'Modified CSS float using "float": Assert float is left');
jQuery('#floatTest').css('font-size', '20px');

12 comments on commit 37b607d

@kangax

This comment has been minimized.

Copy link

replied Sep 16, 2010

Is it just me or do "css" and "style" names sound rather non-descriptive? Is there anything about "css" name suggesting that method should return computed style values? Why not have "style" and "computedStyle" (or "compStyle")?

@jeresig

This comment has been minimized.

Copy link
Member Author

replied Sep 16, 2010

Because that's the API that we have - and have had for years? The methods aren't typically user-facing but they are used by plugin authors so they're likely going to stay as-is for quite some time.

@kangax

This comment has been minimized.

Copy link

replied Sep 16, 2010

I see.

I was just surprised by how unclear those 2 method names are, that you had to specify how they differ in parens. I thought — why not just make names more descriptive? Have you considered aliasing methods like these to more explanatory names? And then discourage their usage in favor of new ones? Or are method names like these not considered a problem?

@jeresig

This comment has been minimized.

Copy link
Member Author

replied Sep 16, 2010

Not really considered a problem - they're almost entirely used internally. As far as the naming goes it seems to work fairly well - our main .css() methods (used by the end user and provides the computed styling of an element) maps directly to our internal jQuery.css(). jQuery.style() is another method that we use internally to manage the setting and getting of the style property.

@GarrettS

This comment has been minimized.

Copy link

replied Sep 16, 2010

Aliasing seems like a good start.

But if the new method wants to have different behavior (bug fix), then aliasing wouldn't work. With method delegation, the new [and] old methods share scope with a new private method, introduced to perform common tasks.

{ 
  ...
  css: deprecatedCSS,
  computedStyle: computedStyle,
...
}

function deprecatedCSS(a, b, c) {
  /* legacy-related code here */
  var style = _readComputedtStyle(a, b, c);
  /* more legacy-related code */
  return style;
}

function _readComputedStyle(a, b, c) {
  var style;
  /*...*/
  return style;
}

function computedStyle(a, b, c) {
  return _readComputedStyle(a, b, c);
}
@CaViCcHi

This comment has been minimized.

Copy link

replied Oct 18, 2010

Hi, I would suggest that the row 325 effects.js

return r && r > -10000 ? r : 0;

would be changed back to

return r;

It works correctly in opera, otherwise anything moved with animate with a position > 10000 will get back to 0 and animate from there.

thank you

@Flcn

This comment has been minimized.

Copy link

replied Nov 17, 2010

return r && r > -10000 ? r : 0;
Yes. I agree with CaViCcHi. I have application where 'left property can be less when -10000

@jitter

This comment has been minimized.

Copy link
Contributor

replied Nov 17, 2010

@CaViCcHi and @Flcn this is already known (reported multiple times) check #7193 and the commit where this was introduced.

@CaViCcHi

This comment has been minimized.

Copy link

replied Nov 17, 2010

@Flcn an easy way around it according to your needs is to add this to your code. So you can keep on going :)

/*
*   Overwrites the Method created, because of a jQuery bug
*       http://github.com/jquery/jquery/commit/37b607d2815b893d13de4ac3461090d0dd46535e#commitcomment-170919
*/

jQuery.fx.prototype.cur = function() {
    if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
        return this.elem[ this.prop ];
    }

    var r = parseFloat( jQuery.css( this.elem, this.prop ) );
    return r /*&& r > -10000 ? r : 0*/;
};
@igorw

This comment has been minimized.

Copy link

replied Nov 17, 2010

I came across this myself just today. Hadn't seen this discussion yet, however. #101

@CaViCcHi

This comment has been minimized.

Copy link

replied Nov 29, 2010

use my workaround :)

@DanielBaulig

This comment has been minimized.

Copy link

replied Nov 29, 2010

Actually I propose to rather use
return r ? r : 0;
or
return r || 0;
Note though that the latter might be subject to compiler optimizations because the right part will never evaluate to true and thus might not be evaluated if JS engines optimize this kind of behavior. Also suggested this in the trac bug report.

Please sign in to comment.
You can’t perform that action at this time.