Search code examples
javascriptjqueryregexreplaceparameterization

Javascript parameterized strings: replace(RegExp, function) unexpected grouping / matching


Regular expressions often eludes me. I am struggling to understand an unexpected result with grouping, and would like someone to tell me:

  • Why it is not working as intended
  • How I could achieve the expected result

Context:

I'm working with strings containing hex color codes. I use replace() on that string to "feed" the matched color codes to a function returning HTML - kind of a parameterized string. One of the parameters is ok, but the other matches the whole string?

Some code:

Here is a JSFiddle with a simple example (beware: does not take letters as far as hex codes go).

The fiddle's code:

HTML:

<p>Please enter two hex color codes (digits only)</p>
<input id="colorsInput" type="text"></input>
<button id="booyah">Roll</button>
<p>Teh colorz</p>
<div id="colors"></div>
<p>Teh source</p>
<div id="html"></div>

JS:

$(document).ready(function() {

  function parameterizedHtml(p1, p2) {
    return '<div style="background-color:#' + p1 + '; width:50px; height:50px;">&nbsp;</div><div style="background-color:#' + p2 + '; width:50px; height:50px;">&nbsp;</div>'; 
  }    

  $('#booyah').click(function() {
    var colorsString = $('#colorsInput').val();
    var html = colorsString.replace(/(\d{6}).*(\d{6})/, parameterizedHtml);
    $('#html').text(html);
    $('#colors').html(html);
  });

});

Output:

<div style="background-color:#548423 564212; width:50px; height:50px;">&nbsp;</div><div style="background-color:#548423; width:50px; height:50px;">&nbsp;</div>

Notice the first background-color attribute.


Solution

  • The first argument replace gives to the callback function is the overall match. That's then followed by additional arguments for any capture groups. So if you change your function signature to:

    function parameterizedHtml(match, p1, p2)
    

    ...it will start working as expected.

    Here's an updated fiddle with the change. I've also changed the regex to /([a-f0-9]{6}).*([a-f0-9]{6})/i so it accepts hex, but note that numeric color values are not required to be six digits long. They can also be three digits long. #abc is the same as #aabbcc. If you want to accept both, then: /([a-f0-9]{3}|[a-f0-9]{6}).*([a-f0-9]{3}|[a-f0-9]{6})/i.