2

While having one of my questions answered, cletus mentioned that when creating elements in jQuery it's better to use direct DOM element creation, instead of innerHTML. I tried googling it but I wasn't able to find a good article with comparisons.

I've provided this code bellow as an example and I was wondering if someone could help me rewrite it in direct DOM element creation form in hope that i would also learn the difference afterwards.

var img = $(this);
img.append('<p class="cap"><a href="'+img.parent().attr('href')+'">'+
img.attr('title')+'</p></a>');

Thanks so much.

Community
  • 1
  • 1
Mohammad
  • 7,344
  • 15
  • 48
  • 76
  • Mohammad: @cletus has the reason for that too: *Some browsers are exceptionally slow at using innerHTML type methods for markup creation so I tend to favour the above approach, which uses diretc DOM element creation.* – Sarfraz Jun 12 '10 at 07:51
  • 1
    There's actually a [performance tool](http://www.developer-x.com/content/innerhtml/dom_vs_innerHTML_perf_test.html) you can test various browsers with. As well, T.J. Crowder wrote a [quick summary](http://stackoverflow.com/questions/2305654/innerhtml-vs-appendchildtxtnode) of the benefits and drawbacks of innerHTML. I've never personally felt very comfortable using innerHTML, though I have found it faster at clearing the contents of a div than repeatedly removing child nodes. I'm surprised that there's actually good reason to use it. (I don't know why I posted this earlier as an "answer") – Andrew Jun 12 '10 at 08:33

2 Answers2

3

Yeah, avoid HTML string-slinging. You're giving yourself bugs and potentially cross-site scripting security holes. For example:

'<a href="'+img.parent().attr('href')+'">'

what if the URL in the parent's href contains characters that are special in this HTML context, like <, " or &? At best you'll get invalid markup that might trip the browser up; at worst, if the URL comes from user submission, a security problem.

(OK, < and " are unlikely to appear in a URL, but & is very likely indeed. The title you're putting in the text content may be more vulnerable.)

This is a large and increasing problem. Just as we're starting to get a handle on fixing the HTML-injection bugs coming from templating on the server-side (eg. PHP users forgetting to htmlspecialchars()), we're now introducing loads of new client-side XSS from naïve HTML-hacking with innerHTML and jQuery's html(). Avoid.

You can escape text you're inserting into HTML manually:

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

'<a href="'+encodeHTML(img.parent().attr('href'))+'">'

but really, you're better off using the direct DOM-style properties and methods, which, since they involve no HTML parsing, don't require any escaping. They're called DOM-style because that's the way the original DOM Level 1 HTML features work (which existed back before innerHTML reared its ugly head):

var a= document.createElement('a');
a.href= this.parentNode.href;
a.appendChild(document.createTextNode(this.title));

In jQuery you use attr() and text() to do the same:

var a= $('<a></a>');
a.attr('href', $(this).parent().attr('href');
a.text($(this).attr('title'));

And in jQuery 1.4, you can do it most conveniently using an element creation shortcut:

$(this).append(
    $('<p class="cap"></p>').append(
        $('<a></a>', {
            href: $(this).parent().attr('href'),
            text: $(this).attr('title')
        })
    )
);

This still looks a bit questionable though, what's the parent that you're getting the href from? It's invalid to put a <p>, or another link, inside a link.

bobince
  • 528,062
  • 107
  • 651
  • 834
2

This is how you create elements in jQuery:

var myDiv = $('<div class="my-class"></div>');

And then you can do all kinds of cool stuff with myDiv.

Here it is using your example:

var img = $(this);
var myP = $('<p class="cap"></p>');
var myA = $('<a></a>');
myA.attr("href",img.parent().attr('href'));
myA.html(img.attr('title'));
myP.append(myA);
this.append(myP);

It's a bit verbose, but yeah.

David Titarenco
  • 32,662
  • 13
  • 66
  • 111