RegExp for hyphen to camelCase?

Discussion in 'Javascript' started by RobG, Jul 21, 2005.

  1. RobG

    RobG Guest

    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?

    --
    Rob
     
    RobG, Jul 21, 2005
    #1
    1. Advertising

  2. RobG

    VK Guest

    RobG wrote:
    > 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 :-(
     
    VK, Jul 21, 2005
    #2
    1. Advertising

  3. RobG

    Baconbutty Guest

    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);
     
    Baconbutty, Jul 21, 2005
    #3
  4. RobG

    fox Guest

    RobG wrote:
    >
    > 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();
    });
    }
     
    fox, Jul 21, 2005
    #4
  5. RobG

    VK Guest

    > 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 !
     
    VK, Jul 21, 2005
    #5
  6. RobG

    Baconbutty Guest

    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.
     
    Baconbutty, Jul 21, 2005
    #6
  7. RobG

    VK Guest

    > 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?
     
    VK, Jul 21, 2005
    #7
  8. RobG

    Baconbutty Guest

    >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.
     
    Baconbutty, Jul 21, 2005
    #8
  9. RobG

    RobG Guest

    Baconbutty wrote:
    > 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 ;-)


    --
    Rob
     
    RobG, Jul 21, 2005
    #9
  10. On 21/07/2005 12:39, RobG wrote:

    > 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

    --
    Michael Winter
    Prefix subject with [News] before replying by e-mail.
     
    Michael Winter, Jul 21, 2005
    #10
  11. RobG

    Baconbutty Guest

    > 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)
     
    Baconbutty, Jul 21, 2005
    #11
  12. 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?



    --

    Martin Honnen
    http://JavaScript.FAQTs.com/
     
    Martin Honnen, Jul 21, 2005
    #12
  13. 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

    --
    Michael Winter
    Prefix subject with [News] before replying by e-mail.
     
    Michael Winter, Jul 21, 2005
    #13
  14. Michael Winter wrote:

    > On 21/07/2005 14:10, Martin Honnen wrote:
    >
    >> 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.

    --

    Martin Honnen
    http://JavaScript.FAQTs.com/
     
    Martin Honnen, Jul 21, 2005
    #14
  15. "Baconbutty" <> writes:

    > 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
    --
    Lasse Reichstein Nielsen -
    DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
    'Faith without judgement merely degrades the spirit divine.'
     
    Lasse Reichstein Nielsen, Jul 21, 2005
    #15
  16. Michael Winter <> writes:

    > 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("");
    }
    ---
    It should do approximatly the same as string.replace(regexp,func),
    assuming that regexp.global is true, in somewhat less than 2K :)

    /L
    --
    Lasse Reichstein Nielsen -
    DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
    'Faith without judgement merely degrades the spirit divine.'
     
    Lasse Reichstein Nielsen, Jul 21, 2005
    #16
  17. On 21/07/2005 23:06, Lasse Reichstein Nielsen wrote:

    > Michael Winter <> writes:
    >
    >> 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

    --
    Michael Winter
    Prefix subject with [News] before replying by e-mail.
     
    Michael Winter, Jul 21, 2005
    #17
  18. Michael Winter <> writes:

    >> var index = string.indexOf(matchString, lastMatchEnd);

    >
    > 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
    --
    Lasse Reichstein Nielsen -
    DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
    'Faith without judgement merely degrades the spirit divine.'
     
    Lasse Reichstein Nielsen, Jul 21, 2005
    #18
  19. RobG

    Baconbutty Guest

    > Overkill

    On reflection it is.

    >Also remember to join "b" afterwards.


    Oops, fogot to cut and paste that :-o
     
    Baconbutty, Jul 22, 2005
    #19
  20. RobG

    RobG Guest

    Martin Honnen wrote:
    >
    >
    > Michael Winter wrote:
    >
    >> On 21/07/2005 14:10, Martin Honnen wrote:
    >>
    >>> 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).

    >>
    >>
    >>
    >> 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.
    >


    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.


    --
    Rob
     
    RobG, Jul 22, 2005
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Benson

    entering the lists against CamelCase

    John Benson, Dec 7, 2003, in forum: Python
    Replies:
    2
    Views:
    325
    John Roth
    Dec 8, 2003
  2. Mark Hahn
    Replies:
    97
    Views:
    2,255
    Martin Maney
    Apr 29, 2004
  3. Jeff Schwab
    Replies:
    8
    Views:
    823
    James Kanze
    Mar 24, 2008
  4. Replies:
    6
    Views:
    169
    Brian Candler
    May 30, 2007
  5. Joao Silva
    Replies:
    16
    Views:
    409
    7stud --
    Aug 21, 2009
Loading...

Share This Page