HTML escaping in Underscore.js templates

Posted: July 20th, 2011 | Author: | Filed under: Javascript | Tags: , , , | 1 Comment »

When displaying values in HTML templates, you would usually want to escape special HTML characters so that they’re displayed properly and to protect against XSS.

I’ve changed my local Underscore.js (and opened a pull request at Underscore.js’s github) to add support for _.escape() and a new <%== ... %> template syntax (notice the double equal signs) which escapes the value for displaying it in HTML.

Update 2011-10-05: The pull request has been merged to Underscore.js core.

If you want to add support for that, you can either apply my commit or simply use this code (make sure to load that after Underscore.js):

_.templateSettings.encode = /<%==([\s\S]+?)%>/g;
_.extend(_, {
	// Taken from Backbone.js's escapeHTML()
	escape: function(string) {
			return (''+string).replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
	},
	template: function(str, data) {
		var c  = _.templateSettings;
		var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
			'with(obj||{}){__p.push(\'' +
			str.replace(/\\/g, '\\\\')
				.replace(/'/g, "\\'")
				.replace(c.encode, function(match, code) {
					return "',_.escape(" + code.replace(/\\'/g, "'") + "),'";
				})
				.replace(c.interpolate, function(match, code) {
					return "'," + code.replace(/\\'/g, "'") + ",'";
				})
				.replace(c.evaluate || null, function(match, code) {
					return "');" + code.replace(/\\'/g, "'")
						.replace(/[\r\n\t]/g, ' ') + "__p.push('";
				})
				.replace(/\r/g, '\\r')
				.replace(/\n/g, '\\n')
				.replace(/\t/g, '\\t')
				+ "');}return __p.join('');";
		var func = new Function('obj', tmpl);
		return data ? func(data) : func;
	}
});

(raw, minified(835 bytes))

If you prefer and have no backward-compatibility issues with it, you can change the <%= ... %> syntax to be escaped by default, and use the double equal sign for cases you do want unescaped HTML. Use that code instead:

_.extend(_.templateSettings, {
	encode: /<%=([\s\S]+?)%>/g,
	interpolate : /<%==([\s\S]+?)%>/g
});
_.extend(_, {
	// Taken from Backbone.js's escapeHTML()
	escape: function(string) {
			return (''+string).replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
	},
	template: function(str, data) {
		var c  = _.templateSettings;
		var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
			'with(obj||{}){__p.push(\'' +
			str.replace(/\\/g, '\\\\')
				.replace(/'/g, "\\'")
				.replace(c.interpolate, function(match, code) {
					return "'," + code.replace(/\\'/g, "'") + ",'";
				})
				.replace(c.encode, function(match, code) {
					return "',_.escape(" + code.replace(/\\'/g, "'") + "),'";
				})
				.replace(c.evaluate || null, function(match, code) {
					return "');" + code.replace(/\\'/g, "'")
						.replace(/[\r\n\t]/g, ' ') + "__p.push('";
				})
				.replace(/\r/g, '\\r')
				.replace(/\n/g, '\\n')
				.replace(/\t/g, '\\t')
				+ "');}return __p.join('');";
		var func = new Function('obj', tmpl);
		return data ? func(data) : func;
	}
});

(raw, minified (878 bytes))

P.S. Can anyone think of a nicer syntax than <%== ... %>? I don’t really like it, but I didn’t come up with anything better…


One Comment on “HTML escaping in Underscore.js templates”

  1. 1 Braden said at 9:19 am on April 18th, 2012:

    For anyone who, like me, is stumbling upon this now, Underscore now officially supports escaped interpolation via .

    Thank you for your advocacy, shesek.


Leave a Reply