1

I am currently trying to build a little templating engine in Javascript by replacing tags in a html5 tag by find and replace with a regex.

I am using exec on my regular expression and I am looping over the results. I am wondering why the regular expressions breaks in its current form with the /g flag on the regular expression but is fine without it?

Check the broken example and remove the /g flag on the regular expression to view the correct output.

var TemplateEngine = function(tpl, data) {
  var re = /(?:&lt;|<)%(.*?)(?:%&gt;|>)/g, match;
  while(match = re.exec(tpl)) {  
    tpl = tpl.replace(match[0], data[match[1]])
  }

  return tpl;
}

https://jsfiddle.net/stephanv/u5d9en7n/

Can somebody explain to me a little bit more on depth why my example breaks exactly on:

<p><%more%></p>
Stephan-v
  • 19,255
  • 31
  • 115
  • 201

2 Answers2

1

Check this link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex. When using the g flag the object that you store the regex in (re) will keep track of the position of the last match in the lastIndex property and the next time you use that object the search will start from the position of lastIndex.

To solve this you could either manually reset the lastIndex property each time or not save the regex in an object and use it inline like so:

while(match = /(?:&lt;|<)%(.*?)(?:%&gt;|>)/g.exec(tpl)) {
Hyddan
  • 1,297
  • 15
  • 24
1

The reason is explained in javascript string exec strange behavior.

The solution you need is actually a String.replace with a callback as a replacement:

var TemplateEngine = function(tpl, data) {
  var re = /(?:&lt;|<)%(.*?)(?:%&gt;|>)/g, match;
  return tpl.replace(re, function($0, $1) { 
     return data[$1] ? data[$1] : $0;
  });
}

See the updated fiddle

Here, the regex finds all non-overlapping matches in the string, sequentially, and passes the match to the callback method. $0 is the full match and $1 is the Group 1 contents. If data[$1] exists, it is used to replace the whole match, else, the whole match is inserted back.

Community
  • 1
  • 1
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563