Sanitize HTML in Javascript object when constructing dynamic HTML forms

Viewed 308

I am constructing a dynamic HTML form and pre-fill the fields with data from a Javascript object like so:

function generatePersonalForm(data) {
    html= '<form id="personalForm">'
    html+='<label for="fname">First name:</label>'
    html+='<input type="text" id="firstname" name="firstname" value="'+data.billing.contact.firstname+'"><br>'
    html+='<label for="lname">Last name:</label>'
    html+='<input type="text" id="lastname" name="lastname" value="'+data.billing.contact.lastname+'"><br>'
    html+='<label for="lname">Data:</label>'
    html+='<textarea id="emailbody" name="emailbody" rows="12" cols="50" value="'+data.emailbody+'"></textarea><br>'
    html+='<input type="submit" value="Submit">'
    html+='</form>'
    return html;
}

I receive the data object from an API and afterwards I create the form and pre-fill it:

document.getElementById("personalData").innerHTML = generatePersonalForm(resp);

My Javascript object data.emailbody sometimes contains HTML (which is correct and intended!). However, if it contains a link like <a href=\"http://www.example.com\">www.example.com</a> the "(double quotes) in the object will break the form.

The same is with single quotes. Is there any option to show HTML content in a form value field dynamically added?

3 Answers

You can't put a hyperlink in textarea. Textarea are meant to handle text only. If you want a simple text with active links, then you should use a div container as your text area.

Any un-escaped double-quotes in any of your attribute values will break your code.

let myExample = 'this is an "example" with double quotes';

'<textarea value="' + myExample + '"></textarea>'

That will produce the following invalid HTML mess:

<textarea value="this is an "example" with double quotes"></textarea>

To prevent this, you can escape all double-quotes:

let escaped = myExample.replace(/"/g, '\\"');
// 'this is an \"example\" that will break'

Which will produce this valid HTML:

<textarea value="this is an \"example\" with double quotes"></textarea>

Even better, you can change from creating HTML with strings to creating DOM elements directly:

// append <label>, <input>, <br> to form
function appendInput(form, labelTxt, inputName, inputValue) {
  let label = document.createElement('label');
  form.appendChild(label);
  label.htmlFor = inputName;
  label.innerHTML = labelTxt;
  let input = document.createElement('input');
  form.appendChild(input);
  input.id = inputName;
  input.name = inputName;
  input.value = inputValue;
  let br = document.createElement('br');
  form.appendChild(br);
}

function generatePersonalForm(data) {
  const form = document.createElement('form');
  form.id = 'personalForm';
  
  appendInput(form, 'First name:', 'firstname',
    data.billing.contact.firstname);
    
  appendInput(form, 'Last name:', 'lastname',
    data.billing.contact.lastname);

  // label and textarea
  let label = document.createElement('label');
  form.appendChild(label);
  label.htmlFor = 'emailbody';
  label.innerHTML = 'Data:';
  let txtArea = document.createElement('textarea');
  form.appendChild(txtArea);
  txtArea.id = txtArea.name = 'emailbody';
  txtArea.rows = '12';
  txtArea.cols = '50';
  txtArea.value = data.emailbody;
  let br = document.createElement('br');
  form.appendChild(br);
  
  // submit input
  let submit = document.createElement('input');
  form.appendChild(submit);
  submit.type = 'submit';
  submit.value = 'Submit';
  
  return form;
}

const dummyData = {
  billing: {
    contact: {
      firstname: 'Joe',
      lastname: '"the dude" Dokes'
      }
  },
  emailbody: 'this is an "emailbody" with double-quotes'
}

window.onload = (() => {
  const formData = generatePersonalForm(dummyData);
  let result = document.getElementById('result');
  result.appendChild(formData);
})();
<div id="result">
  Result:<br>
</div>

To sanitize HTML, you would need to replace the characters <, >, ", ', and & with their HTML entities, like this function does:

function sanitizeHTML(str){
  return str.replace(/[<>"'&]/g, function(match) {
    return "&#" + match.charCodeAt(0) + ";";
  });
}

Then, in your code, replace the call to data.emailbody with sanitizeHTML(data.emailbody), and you will get sanitized output that does not break your code.

Related