Element.setStyle a resource hog?

0 views
Skip to first unread message

Malard

unread,
Feb 17, 2007, 5:27:25 PM2/17/07
to Ruby on Rails: Spinoffs
I've been doing some profiling recently of a rather simple
application. Its using the Morph function, which was leaking memory,
i've fixed that but now it seems that over 60% of my applications time
is spent either calling setStyle or camelize.

So i'm just looking at the setStyle now, while i try and do some self
optimisation to the code, maybe people would be willing to see how
they could optimise it further. One of the first things that strikes
me is that on every call we're finding out what version of the browser
we're in.

Would'nt it make sence to do this on page load once, then just check
variables.?

setStyle: function(element, style) {
element = $(element);
for (var name in style) {
var value = style[name];
if(name == 'opacity') {
if (value == 1) {
value = (/Gecko/.test(navigator.userAgent) &&
!/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
0.999999 : 1.0;
if(/MSIE/.test(navigator.userAgent) && !window.opera)
element.style.filter = element.getStyle('filter').replace(/
alpha\([^\)]*\)/gi,'');
} else if(value == '') {
if(/MSIE/.test(navigator.userAgent) && !window.opera)
element.style.filter = element.getStyle('filter').replace(/
alpha\([^\)]*\)/gi,'');
} else {
if(value < 0.00001) value = 0;
if(/MSIE/.test(navigator.userAgent) && !window.opera)
element.style.filter = element.getStyle('filter').replace(/
alpha\([^\)]*\)/gi,'') +
'alpha(opacity='+value*100+')';
}
} else if(['float','cssFloat'].include(name)) name = (typeof
element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
element.style[name.camelize()] = value;
}
return element;
},

Please comment.

Malard

unread,
Feb 17, 2007, 6:00:29 PM2/17/07
to Ruby on Rails: Spinoffs
Okay, so i've played around a little, my main concern is with the use
on name.camelize() the camelize routine is horribly inefficient in
Gecko (i havent tried other browsers).

I came up with this:

Index: prototype.js
===================================================================
--- prototype.js (revision 6770)
+++ prototype.js (working copy)
@@ -9,7 +9,11 @@
var Prototype = {
Version: '1.5.0',
BrowserFeatures: {
- XPath: !!document.evaluate
+ XPath: !!document.evaluate,
+ isIE: false,
+ isGecko: false,
+ isKHTML: false,
+ isKonquerer: false
},

ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
@@ -1393,23 +1397,28 @@


element = $(element);
for (var name in style) {
var value = style[name];

- if(name == 'opacity') {
- if (value == 1) {
- value = (/Gecko/.test(navigator.userAgent) &&
- !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
0.999999 : 1.0;
- if(/MSIE/.test(navigator.userAgent) && !window.opera)
+ if(name === 'opacity') {
+ if (value === 1) {
+ value = (Prototype.BrowserFeatures.isGecko && !
Prototype.BrowserFeatures.isKonqLike) ? 0.999999 : 1.0;
+ if (Prototype.BrowserFeatures.isIE && !window.opera) {
element.style.filter =
element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
- } else if(value == '') {
- if(/MSIE/.test(navigator.userAgent) && !window.opera)
+ }
+ } else if (value == '') {
+ if(Prototype.BrowserFeatures.isIE && !window.opera) {
element.style.filter =
element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
+ }
} else {
- if(value < 0.00001) value = 0;
- if(/MSIE/.test(navigator.userAgent) && !window.opera)
- element.style.filter =
element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
- 'alpha(opacity='+value*100+')';
+ if (value < 0.00001) {
+ value = 0;
+ }
+ if (Prototype.BrowserFeatures.isIE && !window.opera) {
+ element.style.filter =
element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
'alpha(opacity='+value*100+')';
+ }
}
- } else if(['float','cssFloat'].include(name)) name = (typeof


element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';

- element.style[name.camelize()] = value;
+ } else if(name === 'float' || name === 'cssFloat') {
+ name = (typeof element.style.styleFloat != 'undefined') ?
'styleFloat' : 'cssFloat';
+ }
+ element.style[name] = value;
}
return element;
},
@@ -2513,4 +2522,13 @@
}
}

-Element.addMethods();
\ No newline at end of file
+Element.addMethods();
+
+(function(browserAgent) {
+ Prototype.BrowserFeatures.isGecko = /Gecko/.test(browserAgent);
+ Prototype.BrowserFeatures.isIE = /MSIE/.test(browserAgent);
+ Prototype.BrowserFeatures.isSafari = /Safari/.test(browserAgent);
+ Prototype.BrowserFeatures.isKonqLike = /Konqueror|Safari|
KHTML/.test(browserAgent);
+ Prototype.BrowserFeatures.isKonq = /Konqueror/.test(browserAgent);
+ Prototype.BrowserFeatures.isKHTML = /KHTML/.test(browserAgent);
+})(navigator.userAgent);
\ No newline at end of file


Ideally the isX values could be used throughout prototype aswell. With
these changes i reduced the cpu load of my app by around 30%, the only
'loss of functionality' is the ability to put text-align or
uncamelized values in setStyle.

But i've always camelized the values so i dont have to wrap the key in
the object with quotes anyway.

Marius Feraru

unread,
Feb 17, 2007, 9:47:53 PM2/17/07
to rubyonrail...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Malard wrote:
> One of the first things that strikes me is that on every call we're
> finding out what version of the browser we're in.

Which is an already well known pile of crap. Any user-agent based validation
is feeble, but for one reason (or another) we still have some bytes based on
this clumsy checking. Hopefully we'll get rid of them sooner or later. But
let's keep'em to minimum for now, so please don't promote such kludges ;-)

> Would'nt it make sence to do this on page load once, then just check
> variables.?

Wouldn't it make even more sense for instance to skip doing all these
checks? How? By dynamically compiling different versions of those methods
based on the capabilities of the browser running them. ;-)

Anyway, I concur, setStyle can really be a PITA to folks (ab)using s.a.u
Effect.* ;-)

Prototype-core should be a better place for discussions on this topic.

cheers
- --
Marius Feraru
-----BEGIN PGP SIGNATURE-----

iD8DBQFF175YtZHp/AYZiNkRAimOAKCWynWShH0FKqWOTqow/i2bIpCoXACg9/I+
sUytngjcxFzQXBRfCY9ziN0=
=N8md
-----END PGP SIGNATURE-----

Malard

unread,
Feb 18, 2007, 6:19:16 AM2/18/07
to Ruby on Rails: Spinoffs
How can you detect different versions of the browser dynamically and
compile them. You can easily change the user agent value that the
browser sends to the server, but you cant change if certain parts of
the javascript engine exist or not. So i'm pretty confident that
javascript based checking is the only way to go.

Another note. i did'nt know about prototype-core group, at www.prototypejs.org
the mailing list directs here.

Mislav

unread,
Feb 18, 2007, 6:37:40 AM2/18/07
to Ruby on Rails: Spinoffs
> Wouldn't it make even more sense for instance to skip doing all these
> checks? How? By dynamically compiling different versions of those methods
> based on the capabilities of the browser running them. ;-)

Well, duh! ;-P
see http://dev.rubyonrails.org/ticket/6696

I agree that we should move this discussion to the core ML. Malard,
there was more than one link on http://prototypejs.org/discuss :)

Martin Ellis

unread,
Feb 18, 2007, 9:05:44 AM2/18/07
to rubyonrail...@googlegroups.com
The quote you made, was not from myself. I dont believe that the web
server should decide how to serve the javascript.

As for optimisations, you use include alot, its incredibly expensive
to use that function when just looking for 2 items in array.

I was doing some tests, and the cheapest way to do this was to use an
if statement,

i.e if (foo === 'bar' || foo === 'bar2') {}

Do you think instead of just using rubyesque functions for the sake of
using them over performance is the best action to take, or should code
used in prototype use the fastest applicable.

Reply all
Reply to author
Forward
0 new messages