views:

225

answers:

2

I'm using jQuery to change the HTML of a tag, and the new HTML can be a very long string.

$("#divToChange").html(newHTML);

I then want to select elements created in the new HTML, but if I put the code immediately following the above line it seems to create a race condition with a long string where the changes that html() is making may not necessarily be finished rendering. In that case, trying to select the new elements won't always work.

What I want to know is, is there an event fired or some other way of being notified when changes to html() have finished rendering ? I came across the jQuery watch plugin, which works alright as workaround but it's not ideal. Is there a better way ?

+2  A: 

As a commenter already mentioned, JavaScript is single threaded, so you can't get race conditions.

What may trip you up however, is the fact that the UI will not update itself based on JavaScript, until a thread is finished. This means that the entire method must finish, including all code after you call html(...), before the browser will render the content.

If your code after calling html(...) relies on the layout of the page being recalculated before continuing, you can do something like this:

$("#divToChange").html(newHTML);
setTimeout(function() {
    // Insert code to be executed AFTER
    // the page renders the markup
    // added using html(...) here
}, 1);

Using setTimeout(...) with a time of 1 in JavaScript defers execution until after the current JavaScript code in the calling function finishes and the browser has updated the UI. This may solve your problem, though it is difficult to tell unless you can provide a reproducible example of the error you're getting.

Dan Herbert
Reflow doesn't wait until all running code is completed. Running several operations in sequence which each can cause a reflow will suffer substantially more of a reflow performance hit than a single operation (even if the reflow's consequences are identical).
eyelidlessness
@eyelidlessness Unless I misunderstand what you are saying, this simply is not true. This can be demonstrated by going to this simple test I threw together: http://jsbin.com/avuli/6 Click the document once, and the code will perform a `while` loop for 5 seconds. Notice that the UI is not updated until the end of the 5 seconds. The second time you click, I use a timer to update the UI in a loop. This time, you'll notice that the UI updates itself in "real time" as the DOM is changed.
Dan Herbert
To clarify, "reflow" doesn't necessarily mean the document will be visually updated. Your `while` loop is a blocking operation, and as such no visual update will occur until it completes. But reflow will occur with each operation, with the consequence that querying the DOM for data that depends on a reflow will give you correct results even if the page is visually stalled. See my edit of your script: http://jsbin.com/ayulu3 (see console after first click)
eyelidlessness
To expand on the topic, the fact that a reflow occurs with each operation is a reason that batch DOM operations perform much better when they can force only a single reflow (eg. appending either a single `<div>` or `DocumentFragment` containing a set of elements to be appended).
eyelidlessness
A: 

Thanks for your help. In trying to get a reproducible example, I realised I was wrong in my original question about where the problem is. The newHTML includes code to load a very small .swf, and the subsequent Javascript is trying to interact with it. Although the HTML itself is being changed, the Flash file itself is not fully loaded in time. It does work if I added an alert() or a long loop straight after the call to html() though, which is why I thought it was acting like a race condition.

I still need a Javascript way of knowing when the Flash file is fully ready to interact with, but using the jQuery Watch plugin works ok for that.

miket2e
The reason the appended Flash isn't immediately ready is because the SWF file must be loaded: you're waiting for the file to be transmitted over the network, not for the browser to complete some other operation. I don't know the ins and outs of being able to detect when a Flash video is "ready", but it may require setting up an event in ActionScript and calling a Javascript callback with (eg) `ExternalInterface`.
eyelidlessness
You could try attaching a jQuery `load(...)` event to your swf element, and executing any code that interacts with flash within the `load(...)` event: http://api.jquery.com/load-event/
Dan Herbert