Sanitizing user input before adding it to the DOM in Javascript

Never use escape(). It’s nothing to do with HTML-encoding. It’s more like URL-encoding, but it’s not even properly that. It’s a bizarre non-standard encoding available only in JavaScript.

If you want an HTML encoder, you’ll have to write it yourself as JavaScript doesn’t give you one. For example:

function encodeHTML(s) {
    return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
}

However whilst this is enough to put your user_id in places like the input value, it’s not enough for id because IDs can only use a limited selection of characters. (And % isn’t among them, so escape() or even encodeURIComponent() is no good.)

You could invent your own encoding scheme to put any characters in an ID, for example:

function encodeID(s) {
    if (s==='') return '_';
    return s.replace(/[^a-zA-Z0-9.-]/g, function(match) {
        return '_'+match[0].charCodeAt(0).toString(16)+'_';
    });
}

But you’ve still got a problem if the same user_id occurs twice. And to be honest, the whole thing with throwing around HTML strings is usually a bad idea. Use DOM methods instead, and retain JavaScript references to each element, so you don’t have to keep calling getElementById, or worrying about how arbitrary strings are inserted into IDs.

eg.:

function addChut(user_id) {
    var log= document.createElement('div');
    log.className="log";
    var textarea= document.createElement('textarea');
    var input= document.createElement('input');
    input.value= user_id;
    input.readonly= True;
    var button= document.createElement('input');
    button.type="button";
    button.value="Message";

    var chut= document.createElement('div');
    chut.className="chut";
    chut.appendChild(log);
    chut.appendChild(textarea);
    chut.appendChild(input);
    chut.appendChild(button);
    document.getElementById('chuts').appendChild(chut);

    button.onclick= function() {
        alert('Send '+textarea.value+' to '+user_id);
    };

    return chut;
}

You could also use a convenience function or JS framework to cut down on the lengthiness of the create-set-appends calls there.

ETA:

I’m using jQuery at the moment as a framework

OK, then consider the jQuery 1.4 creation shortcuts, eg.:

var log= $('<div>', {className: 'log'});
var input= $('<input>', {readOnly: true, val: user_id});
...

The problem I have right now is that I use JSONP to add elements and events to a page, and so I can not know whether the elements already exist or not before showing a message.

You can keep a lookup of user_id to element nodes (or wrapper objects) in JavaScript, to save putting that information in the DOM itself, where the characters that can go in an id are restricted.

var chut_lookup= {};
...

function getChut(user_id) {
    var key= '_map_'+user_id;
    if (key in chut_lookup)
        return chut_lookup[key];
    return chut_lookup[key]= addChut(user_id);
}

(The _map_ prefix is because JavaScript objects don’t quite work as a mapping of arbitrary strings. The empty string and, in IE, some Object member names, confuse it.)

Leave a Comment