RegExp for hyphen to camelCase?

R

RobG

I'm messing with getPropertyValue (Mozilla et al) and currentStyle (IE)
and have a general function (slightly modified from one originally
posted by Steve van Dongen) for getting style properties:

function GetCurrentStyle( el, prop ) {
if ( window.getComputedStyle ) {
// Mozilla et al
return window.getComputedStyle(el, '').getPropertyValue(prop) );
} // IE5+
else if ( el.currentStyle ) {
return el.currentStyle[prop];
} // IE4
else if ( el.style ) {
return el.style[prop];
}
}

If property names have no hyphen (e.g. width, height) then all is fine.
But with hyphenated names (e.g. margin-right, border-top) Mozilla
wants them unmodified and IE wants them camelCased (marginRight, borderTop).

Is there a simple RegExp that will camelCase hyphenated strings? I can
do it with a loop and string operations but would prefer to use a single
RegExp if possible.

The following works fine using substring (for s having 0 or more
hyphens) but is there a simpler way?

function toCamel ( s ) {
s = s.split('-');
var i=0, j=s.length;
while ( ++i < j ) {
s[0] += s.substring(0,1).toUpperCase() + s.substring(1);
}
return s[0];
}

I've tried using RegExp but can't get a simple expression without
looping - property names have up to 3 (or more? three-d-light-shadow)
hyphens, a RegExp with global flag should be possible.

Any suggestions?
 
V

VK

RobG said:
I'm messing with getPropertyValue (Mozilla et al) and currentStyle (IE)
and have a general function (slightly modified from one originally
posted by Steve van Dongen) for getting style properties:

function GetCurrentStyle( el, prop ) {
if ( window.getComputedStyle ) {
// Mozilla et al
return window.getComputedStyle(el, '').getPropertyValue(prop) );
} // IE5+
else if ( el.currentStyle ) {
return el.currentStyle[prop];
} // IE4
else if ( el.style ) {
return el.style[prop];
}
}

If property names have no hyphen (e.g. width, height) then all is fine.
But with hyphenated names (e.g. margin-right, border-top) Mozilla
wants them unmodified and IE wants them camelCased (marginRight, borderTop).

Is there a simple RegExp that will camelCase hyphenated strings? I can
do it with a loop and string operations but would prefer to use a single
RegExp if possible.

The following works fine using substring (for s having 0 or more
hyphens) but is there a simpler way?

function toCamel ( s ) {
s = s.split('-');
var i=0, j=s.length;
while ( ++i < j ) {
s[0] += s.substring(0,1).toUpperCase() + s.substring(1);
}
return s[0];
}

I've tried using RegExp but can't get a simple expression without
looping - property names have up to 3 (or more? three-d-light-shadow)
hyphens, a RegExp with global flag should be possible.

Any suggestions?

--



The pattern search would be simple:

var re = /(-)([a-z])/g;

hyphenString.replace(re,"$2");

This would transform "a-b-c" to "abc"

Unfortunately back to NN4 they have forgotten to include in
JavaScript's RegExp the case transformation sequences (\u and \U). And
so far no one bothered to fix it. So there is no way to transform
"a-b-c" to "aBC" other than using .exec method, pass through all
matches and apply toUpperCase() with string re-assemply each time.

In this case the old charAt/substring method *much* more time/ressource
effective :-(
 
B

Baconbutty

I would agree with VK's comments.

Taking from both your comments you could try this at least:-

var s="three-d-light-shadow";
var r=/(-)([a-z])/g;
s=s.replace(r,function(a,b,c){return c.toUpperCase();});
alert(s);
 
F

fox

RobG said:
I'm messing with getPropertyValue (Mozilla et al) and currentStyle (IE)
and have a general function (slightly modified from one originally
posted by Steve van Dongen) for getting style properties:

function GetCurrentStyle( el, prop ) {
if ( window.getComputedStyle ) {
// Mozilla et al
return window.getComputedStyle(el, '').getPropertyValue(prop) );
} // IE5+
else if ( el.currentStyle ) {
return el.currentStyle[prop];
} // IE4
else if ( el.style ) {
return el.style[prop];
}
}

If property names have no hyphen (e.g. width, height) then all is fine.
But with hyphenated names (e.g. margin-right, border-top) Mozilla wants
them unmodified and IE wants them camelCased (marginRight, borderTop).

Is there a simple RegExp that will camelCase hyphenated strings? I can
do it with a loop and string operations but would prefer to use a single
RegExp if possible.

The following works fine using substring (for s having 0 or more
hyphens) but is there a simpler way?

function toCamel ( s ) {
s = s.split('-');
var i=0, j=s.length;
while ( ++i < j ) {
s[0] += s.substring(0,1).toUpperCase() + s.substring(1);
}
return s[0];
}

I've tried using RegExp but can't get a simple expression without
looping - property names have up to 3 (or more? three-d-light-shadow)
hyphens, a RegExp with global flag should be possible.

Any suggestions?


function
toCamel(s)
{
var re = /(-)(\w)/g;

return s.replace(re,
function(fullmatch,paren1,paren2)
{
return paren2.toUpperCase();
});
}
 
V

VK

var s="three-d-light-shadow";
var r=/(-)([a-z])/g;
s=s.replace(r,function(a,b,c){return c.toUpperCase();});
alert(s);

That's an absolutely cool twist!
Flushing substring !
 
B

Baconbutty

It does enable you to be quite concise.

Oddly though, I have found that if you use this type of technique on
very large strings, it actually becomes much more inefficient than
ordinary String concatenation or Array joining, by quite a margin. For
a 200k string, String concatenation is about 10x faster. But for small
Strings can be quite useful.
 
V

VK

Oddly though, I have found that if you use this type of technique on
very large strings, it actually becomes much more inefficient than
ordinary String concatenation or Array joining, by quite a margin. For
a 200k string, String concatenation is about 10x faster.

it's not so odd because regexp's are very resource intensive. So on big
texts it takes *a lot*. In some serious implementations of regexp's
there is study() method to "prepare" a large text for regexp
processing.

IE introduced re.compile() method to convert static regexp to binary
code for maximum speed. Did you try that?
 
B

Baconbutty

it's not so odd because regexp's are very resource intensive. So on big
texts it takes *a lot*.

I can see how that would be.
I suppose jumping out of the process to call a function will not help
either.
IE introduced re.compile() method to convert static regexp to binary
code for maximum speed. Did you try that?

No I didn't. I'll give it a try. Thanks.
 
R

RobG

Baconbutty said:
I would agree with VK's comments.

Taking from both your comments you could try this at least:-

var s="three-d-light-shadow";
var r=/(-)([a-z])/g;
s=s.replace(r,function(a,b,c){return c.toUpperCase();});
alert(s);

Cool, unfortunately Safari doesn't like it, returning:

threefunction (a, b, c)
{
return c.toUpperCase();
}function (a, b, c)
{
return c.toUpperCase();
}ightfunction (a, b, c)
{
return c.toUpperCase();
}hadow

It is returning the function body rather than the result - similarly
for Fox's suggestion.

So thanks, but I'll have to stick with looping for now. Hopefully
style property names will never reach 200k ;-)
 
M

Michael Winter

Baconbutty wrote:

[Use a function argument]
Cool, unfortunately Safari doesn't like it, [...]

It is returning the function body rather than the result - similarly for
Fox's suggestion.

So will IE5 and earlier as they don't support function arguments, and
Opera 6 won't perform a replacement at all (a no-op).
So thanks, but I'll have to stick with looping for now. [...]

It takes about 2KB to replace the replace method. :)

Mike
 
B

Baconbutty

Cool, unfortunately Safari doesn't like it,
Will IE5 and earlier as they don't support function arguments
Opera 6 won't perform a replacement at all

Thats a shame. It is in the ECMA spec I think, but that is of course
no guarantee that it is implemented everywhere. .
It takes about 2KB to replace the replace method. :)

Perhaps the challenge then is to come up with the most concise
alternative. A starter:-

var a=s.split(""); var b=[]; var j=a.length; var i=0;
do {b[b.length]=(a=="-")?a[++i].toUpperCase():a;}while(++i<j)
 
M

Martin Honnen

RobG wrote:

function GetCurrentStyle( el, prop ) {
if ( window.getComputedStyle ) {
// Mozilla et al
return window.getComputedStyle(el, '').getPropertyValue(prop) );
} // IE5+
else if ( el.currentStyle ) {
return el.currentStyle[prop];
} // IE4
else if ( el.style ) {
return el.style[prop];
}
}

If property names have no hyphen (e.g. width, height) then all is fine.
But with hyphenated names (e.g. margin-right, border-top) Mozilla wants
them unmodified and IE wants them camelCased (marginRight, borderTop).

getComputedStyle is supposed to return an object implementing the
CSSStyleDeclaration interface but that interface can optionally be
casted to the CSS2Properties interface which then allows accessing CSS
properties the same way you do with element.style in browsers e.g.
element.style.cssPropertyName
so in Mozilla and Opera you can do
return window.getComputedStyle(el, '')[prop]
if the CSS property name is passed in in camel case (e.g.
'backgroundColor', 'marginRight').
Could you test whether Safari allows that too?
 
M

Michael Winter

On 21/07/2005 14:10, Martin Honnen wrote:

[snip]
so in Mozilla and Opera you can do
return window.getComputedStyle(el, '')[prop]

You could but the global object has never been defined as an instance of
the AbstractView interface. I would use the slightly longer, but
better defined route through the document object:

document.defaultView.getComputedStyle(...)
if the CSS property name is passed in in camel case (e.g.
'backgroundColor', 'marginRight').

Though you should be careful as there is an exception to the case
conversion pattern: float becomes cssFloat.
Could you test whether Safari allows that too?

Neither Safari nor Konqueror support the getComputedStyle method. Though
it exists as a property of the defaultView object in Konqueror, it
always returns null.

Mike
 
M

Martin Honnen

Michael said:
so in Mozilla and Opera you can do
return window.getComputedStyle(el, '')[prop]


You could but the global object has never been defined as an instance of
the AbstractView interface. I would use the slightly longer, but better
defined route through the document object:

document.defaultView.getComputedStyle(...)

Right, I was mainly following Rob's code and only changing the part
after the getComputedStyle. I have myself pointed out that
document.defaultView and window don't have to be the same as far as the
W3C DOM says but as far as Mozilla goes Brendan Eich himself said the
current behaviour is not going to be changed:
<http://groups-beta.google.com/group/netscape.public.mozilla.dom/msg/aa219bf41334c22e?hl=en&>
And the current WHAT WG drafts also try to ensure that the current
praxis that browsers implement document.defaultView as the window object
becomes standardized:
<http://www.whatwg.org/specs/web-apps/current-work/#documentwindow>
so it seems pretty safe to do window.getComputedStyle.

Neither Safari nor Konqueror support the getComputedStyle method. Though
it exists as a property of the defaultView object in Konqueror, it
always returns null.

Ouch, one more case for not only checking for the existance of a method
in the DOM but to check for the result too.
 
L

Lasse Reichstein Nielsen

Baconbutty said:
Perhaps the challenge then is to come up with the most concise
alternative. A starter:-

var a=s.split(""); var b=[]; var j=a.length; var i=0;
do {b[b.length]=(a=="-")?a[++i].toUpperCase():a;}while(++i<j)


Overkill (mainly splitting the string into a character array and
looking at each char, not the total size of the snippet). Also
remember to join "b" afterwards.

Anyway:

var p=s.split("-"),i=p.length;
while(--i){p=p.charAt(0).toUpperCase()+p.substring(1);}
s=p.join("");

/L
 
L

Lasse Reichstein Nielsen

Michael Winter said:
It takes about 2KB to replace the replace method. :)

If you don't need full generality, or efficiency, you can do something
like:
---
function replace(string, regexp, func) {
var match, lastMatchEnd=0, res = [];
while((match = regexp.exec(string))) {
var matchString = match[0];
var index = string.indexOf(matchString, lastMatchEnd);
res.push(string.substring(lastMatchEnd, index));
var repl = func.apply(null, match.concat([index, string]));
res.push(String(repl));
lastMatchEnd = index + matchString.length;
}
res.push(string.substring(lastMatchEnd));
return res.join("");
}
 
M

Michael Winter

Michael Winter said:
It takes about 2KB to replace the replace method. :)

If you don't need full generality, or efficiency, you can do something
like:
---
function replace(string, regexp, func) {
var match, lastMatchEnd=0, res = [];
while((match = regexp.exec(string))) {
var matchString = match[0];
var index = string.indexOf(matchString, lastMatchEnd);

Have you found the index property of the returned array to be unreliable?
res.push(string.substring(lastMatchEnd, index));

If replacement of the replace method is necessary, push...
var repl = func.apply(null, match.concat([index, string]));

....and apply will probably need to be supplied as well (part of my 2KB).
For example, with IE5.

[snip]
assuming that regexp.global is true [...]

The global flag would seem to be a pain; non-existent in IE5 and always
false in early Opera. In those browsers you can match against the string
representation of that object, but I seem to recall Richard saying that
not all browsers would produce a useful value.

[mini rant]
I know that the replace method isn't usually /necessary/ to accomplish a
task, but it would be nice to be able to just call it knowing the darn
thing will work as expected. Oh well, such is browser scripting...
[/mini rant]

Mike
 
L

Lasse Reichstein Nielsen

Michael Winter said:
Have you found the index property of the returned array to be unreliable?

Nope, just haven't found it at all :)
If replacement of the replace method is necessary, push...
...and apply will probably need to be supplied as well (part of my
2KB). For example, with IE5.

True. Push is easily replacable:
foo.push(value); ==> foo[foo.length]=value;
but apply is *hard*. It's one of the few valid uses of "eval" that I
have found. However, we don't need to follow the argument structure
of String.prototype.replace as strictly, so it might be better to
use a fixed argument format, e.g. all captures in one array argument:
var repl = func(match[0], match.slice(1), index, string);
[snip]
assuming that regexp.global is true [...]

The global flag would seem to be a pain; non-existent in IE5 and
always false in early Opera.
....

In this case, it's not really the flag itself that is important, but
the behavior of the RegExp on successive calls that it implies.
If that fails, then you will definitly need more plumbing.


/L
 
R

RobG

Martin said:
Michael said:
so in Mozilla and Opera you can do
return window.getComputedStyle(el, '')[prop]

Thanks, I'll use that. No need to add humps of unnecessary code (that
was a really bad pun).
Right, I was mainly following Rob's code and only changing the part
after the getComputedStyle. I have myself pointed out that
document.defaultView and window don't have to be the same as far as the
W3C DOM says but as far as Mozilla goes Brendan Eich himself said the
current behaviour is not going to be changed:
<http://groups-beta.google.com/group/netscape.public.mozilla.dom/msg/aa219bf41334c22e?hl=en&>

And the current WHAT WG drafts also try to ensure that the current
praxis that browsers implement document.defaultView as the window object
becomes standardized:
<http://www.whatwg.org/specs/web-apps/current-work/#documentwindow>
so it seems pretty safe to do window.getComputedStyle.




Ouch, one more case for not only checking for the existance of a method
in the DOM but to check for the result too.

Agggh, I tested it in Safari (1.0.3) and surprise, Mike is right.
(typeof window.getComputedStyle) returns undefined. Using the code as
posted, Safari falls through to the IE 4 test - el.style - which is
undesirable.

I guess the bottom line is that if you want reliable information about
current element style, everything needs to be applied either inline or
as an element property. Getting CSS style stuff is not reliable.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top