3

I am trying to dynamically populate a <select> element using jQuery and option values from an array of strings returned in a JSON response. One of the option values is Joe&#39;s Cafe which when decoded is Joe's Cafe. However, when I use the following code:

$.each(optionVals, function(idx, elem) {
    $('#restaurant').append('<option value="' + elem + '">' + elem + '</option>');
});

the corresponding option is not what I expect. I see this:

<option value="Joe's Cafe">Joe's Cafe</option>

instead of this:

<option value="Joe&#39;s Cafe">Joe&#39;s Cafe</option>

How do I prevent jQuery from decoding the HTML entity &#39; in the .append() operation? I checked the JSON response, and the HTML entity is intact. I checked the source after the options were loaded and the HTML entity has been decoded into the single quote '.

Web User
  • 7,438
  • 14
  • 64
  • 92
  • Do you check actual HTML with good old "View Source" (Ctrl+U in many browsers) or through the DOM pane? – Álvaro González Nov 17 '15 at 10:19
  • @ÁlvaroGonzález I checked using Chrome's developer tools. Good ol' View Source won't show dynamic portions of the HTML source. – Web User Nov 17 '15 at 10:23
  • See this http://stackoverflow.com/a/10715834/3036342 – user3036342 Nov 17 '15 at 10:25
  • That's the issue then: you don't see the actual HTML. Not sure about Chrome but Firefox shows actual *generated* HTML if you right click on a selection and pick "View selected source". – Álvaro González Nov 17 '15 at 12:13
  • Checked in Chrome, Firefox and Microsoft Edge... all three display actual generated HTML as the single quote and not the HTML entity `'`. – Web User Nov 17 '15 at 13:11

2 Answers2

6

How do I prevent jQuery from decoding the HTML entity &#39; in the .append() operation?

You can't, because it isn't.

jQuery is generating some HTML and then getting the browser to convert it to a DOM. It then appends that DOM fragment to the document DOM that represents the page.

When you examine the DOM using a DOM inspector (or when you use innerHTML to convert it to a string) you are serialising the DOM to HTML. At this point the browser does not know (because it hasn't kept track, because it doesn't need to) if the apostrophe in the DOM came from a literal apostrophe or a character reference for one. The algorithm it uses to decide how to represent the character uses the literal one (there is no point in it using a 5 character code when it can use 1 more readable character instead).

<option value="Joe's Cafe">Joe's Cafe</option> and <option value="Joe&#39;s Cafe">Joe&#39;s Cafe</option> are two different ways of writing exactly the same thing in HTML. Which you get should not matter.


It may be the same thing in HTML but it is completely different when stored in a database.

It sounds like you want the form to submit the string Joe&#39;s Cafe to the server.

<option value="Joe&#39;s Cafe">Joe&#39;s Cafe</option> would not do that.

That would require: value="Joe&amp;#39;s Cafe"

Now you can achieve that by generating your DOM sanely instead of mashing strings together (because when you mash strings together, characters with special meaning in HTML get treated as having that special meaning).

$('#restaurant').append(
    $("<option />").val(elem).text(elem)
);

however your implication is that having raw apostrophes in the data is breaking your SQL.

That means you are vulnerable to SQL injection attacks and that the real solution is to fix this server side.

See How can I prevent SQL-injection in PHP? or look up how to use prepared statements to prevent SQL injection in whatever programming language you are using on server.

Community
  • 1
  • 1
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • It may be the same thing in HTML but it is completely different when stored in a database. – Stewartside Nov 17 '15 at 10:35
  • @Stewartside — Updated the answer. You were asking the wrong question. – Quentin Nov 17 '15 at 10:46
  • Nice, it was just your original answer, to me, seemed to be lacking a solution to the problem and instead was just covering why it was doing it. +1 – Stewartside Nov 17 '15 at 10:48
  • @Stewartside — That's because there was no actual problem in the original question. You were asking how to convert something into an identically equivalent state, which isn't a problem. :) – Quentin Nov 17 '15 at 10:50
  • @Quentin it is interesting to note (and learn) that mashing strings together causes HTML entities to be treated as their representation. So your workaround worked perfectly. Regarding the vulnerability to SQL injection attacks, the server side code mitigates that risk already, but I appreciate the alert on it! – Web User Nov 17 '15 at 17:40
  • How does the server mitigate the risk? If it is doing it sanely then having literal `'` in the data shouldn't cause a problem. – Quentin Nov 17 '15 at 18:18
  • it was unexpected and a bit confusing, for me, that when i added encoded values to a data attribute with `var my_encoded_value = he.encode(sanitized_unruly_html); $("#my_div").attr("data-some_value", my_encoded_value)`, i see the _decoded_ values in chrome dev tools, and when getting the value with `$("#my_div").attr("data-some_value")` it is also already decoded (`he.decode()` is not required). if, however, i encode the original value with `encodeURI()` and add it to a data attribute, the values are still _encoded_ when viewing in chrome dev tools, and when getting the attribute value. – user1063287 Aug 11 '19 at 13:37
2

In order to avoid HTML entitites to be decoded, you can replace globally & by &amp; in your strings:

var optionVals = ["Joe&#39;s Cafe"];
$.each(optionVals, function(idx, elem) {
    elem = elem.replace(/&/g, "&amp;");
    $('#restaurant').append('<option value="' + elem + '">' + elem + '</option>');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="restaurant"></select>
Magicprog.fr
  • 4,072
  • 4
  • 26
  • 35
  • @Magicprogfr I went with the second answer but I realized there is more than one way to work around the issue thanks to your answer, – Web User Nov 17 '15 at 17:42