Categories: Software
Tags: development javascript jquery software tips
Posted by: Darian Cabot
Comments:2
jQuery: Automatic HTML element manipulation
When working on my latest project I discovered a feature of jQuery that wasn’t apparent to me in the API. When using the .append() method I noticed it would automatically close HTML elements. After taking a closer look at the jQuery 1.4.2 code, I found that .append() makes use of an internal function called domManip(). Other similar methods that also use domManip() are:
- .append()
- .appendTo()
- .prepend()
- .prependTo()
- .before()
- .insertBefore()
- .after()
- .insertAfter()
- .replaceWith()
- .replaceAll()
I created a few tests to see exactly how jQuery’s domManip() handles different scenarios and how it can cause frustration if misunderstood. Each of the tests have a yellow ‘parent’ DIV containing some text. In each test I use jQuery’s .append() method to add a red ‘child’ DIV with text, but in a few different ways. Let’s begin!
Test 1: Missing closing tag
HTML Before
<p>Test if .append() automatically closes a HTML element.</p><div id="test1" class="parent-container"> <p>parent-container</p> </div>
jQuery JavaScript
$('#test1').append('<div class="child-container"><p>child-container</p>');
You can see we are going to append a small HTML string that is missing the closing </div>
tag.
HTML After
<p>Test if .append() automatically closes a HTML element.</p> <div id="test1" class="parent-container"> <p>parent-container</p> <div class="child-container"> <p>child-container</p> </div> </div>
Here the added HTML can be seen in red. jQuery automatically added the closing </div>
tag. This ensures the DOM remains valid for all jQuery DOM manipulations.
Very nifty! 🙂
The result (Firefox)
Everything’s in good order after jQuery added the missing closing tag to maintain a valid DOM.
Test 2: Extra ‘orphan’ closing tag
HTML before
<p>Test if .append() automatically removes an orphan HTML element.</p> <div id="test2" class="parent-container"> <p>parent-container</p> </div>
jQuery JavaScript
$('#test2').append('<div class="child-container"><p>child-container</p></div><strong></div></strong>');
In this test, I’ve added an extra closing </div>
tag.
HTML after
<p>Test if .append() automatically removes an orphan HTML element.</p> <div id="test2" class="parent-container"> <p>parent-container</p> <div class="child-container"> <p>child-container</p> </div> </div>
The result (Firefox)
Once again, everything’s in place and the HTML is clean thanks to jQuery. While test 1 and 2 are very good to ensure a valid DOM, it can also lead to programming mistakes if misunderstood as we’ll see next.
Test 3: The trap with nesting HTML elements
HTML before
<p>Test if .append()'s element manipulation breaks line-for-line nesting.</p> <div id="test3" class="parent-container"> <p>parent-container</p> </div>
jQuery JavaScript
$('#test3').append('<div class="child-container">'); $('#test3').append('<p>child-container paragraph one.</p>'); $('#test3').append('<p>child-container paragraph two.</p>'); $('#test3').append('<p>child-container paragraph three.</p>'); $('#test3').append('</div>');
In this test, we’re adding multiple lines of HTML. If jQuery were to append this verbatim, as text, this would be fine. However jQuery .append()
has parsed each line as a separate DOM injection and tried to correct ‘mistakes’ without any foresight…
HTML after
<p>Test if .append()'s element manipulation breaks line-for-line nesting.</p> <div id="test3"> <p>parent-container</p> <div class="child-container"></div> <p>child-container paragraph one.</p> <p>child-container paragraph two.</p> <p>child-container paragraph three.</p> </div>
The first .append()
was automatically given it’s missing closing </div>
tag – no what we wanted! The the last .append()
removed our closing </div>
tag because it seemed to be an orphan! It’s obvious now that .append()
and similar jQuery functions shouldn’t be used line-for-line like this.
The result (Firefox)
Test 4: The simple solution
HTML before
<p>The neat solution to Test 4.</p> <div id="test4" class="parent-container"> <p>parent-container</p> </div>
jQuery JavaScript
var htmlString = '<div class="child-container">'; htmlString += '<p>child-container paragraph one.</p>'; htmlString += '<p>child-container paragraph two.</p>'; htmlString += '<p>child-container paragraph three.</p>'; htmlString += '</div>'; $('#test3').append(htmlString);
The solution is to append everything in one go. This is made neat and tidy using htmlString in the example.
HTML after
<p>The neat solution to Test 4.</p> <div id="test4"> <p>parent-container</p> <div class="child-container"> <p>child-container paragraph one.</p> <p>child-container paragraph two.</p> <p>child-container paragraph three.</p> </div> </div>
The result (Firefox)
Here we’ve finally achieved the result we were after.
While the concept and tests are simplistic, the background DOM manipulation by jQuery is very important to understand. I was unable to find this sufficiently documented anywhere, so hopefully this can help a few other green jQuery developers like myself 😉
Further reading:
http://api.jquery.com/category/manipulation/dom-insertion-inside/
2 comments
Just wanted to make a note that using the string building method has a HUGE performance benefit. There is a bit of overhead involved with jQuery’s append() method: DOM parsing, code corrections, DOM manipulation… etc. By reducing the amount of calls to append(), we are greatly increasing our program’s performance.
I agree that building a string as much as possible before inserting it improves performance and is a good rule of thumb. It’s always a balance between the convenience of jQuery’s checks and balances and the performance hit that comes with them. I didn’t mention performance in this post, but a benchmark of different methods would be interesting to see.
String building methods in JavaScript will always be a matter of contention though! 😉