Is there an IE Render Complete event?

2019-03-19 20:30发布

问题:

While trying to determine why a page was taking 20s to load, I found some odd behavior in IE8.

The scenario is this.

I make an ajax call, it returns and the callback looked something like this

$("#StoreDetailsContainer").html($(tableHtml));
var StoreDetailsTable = $("#StoreDetailsTable");
StoreDetailsTable.tablesorter({ sortList: [[0, 0]], cssChildRow: "SubTable" });
StoreDetailsTable.filtertable({ cssChildRow: "SubTable" });

However, this bit of code took 20s to complete.

I was messing around, timing things, and popping up alerts between methods, and suddenly, it took only 6s. I played around a little more to find that if I introduced a delay after the .html() call, and before I attempted to manipulate the DOM, the page rendered MUCH faster. It now looks like this

$("#StoreDetailsContainer").html($(tableHtml));
window.setTimeout(function() {
    var StoreDetailsTable = $("#StoreDetailsTable");
    StoreDetailsTable.tablesorter({ sortList: [[0, 0]], cssChildRow: "SubTable" });
    StoreDetailsTable.filtertable({ cssChildRow: "SubTable" });
}, 100);

It also only takes 6s despite having an extra 1/10th of a second added to the process.

My theory is that because the DOM wasn't fully rendered to the screen by IE by the .html() call before attempting to work with it, there is some kind of locking happening.

Is there a way to determine when IE has finished rendering what was added to the DOM by .html() so I don't need to use an arbitrary value in a setTimeout call?

回答1:

You could add a single pixel image to your callback response, get that image from the DOM after .html(..) and attach to its onload event. I can't imagine it's possible for the image's onload event to fire until the browser has rendered it.

Make sure the image has a unique identifier in the src so that it doesn't get cached...

Odd problem you're having though - I'm sure someone will offer a more graceful solution :)

B



回答2:

You're almost on the spot with your analysis. Let me attempt to explain why setTimeout is making the difference.

If you look at this great article about DOM rendering, you'll understand that .html() will cause a reflow to happen.

Now with the next two lines, what is happening is that you're tying up the browser's rendering thread. Browsers may choose to wait till script execution completes before attempting a reflow (to avoid multiple reflows or to buffer all changes).

The solution - we need to tell the browser that our html changes are done and you can complete the rendering process. The way to do it - 1) end your script 2) use setTimeout to yield the execution to the browser.

Doing a setTimeout with 0ms delay also works because setTimeout basically relinquishes control of the sript block. One of the reasons why animation related script rely so heavily on setTimeout and setInterval.

Another potential improvement would be to use documentFragments, explained succinctly by John Resig here

I think combining these two should yield more speed but of course, no way to know until profiling is done!



回答3:

Calling setTimeout delays the given function at least for the specified time but it is never run before the current script execution finished. That said, you could replace your timeout with 0 seconds.

Another approach that might be worth trying is that you access some layout property of the generated content (for example height of StoreDetailsContainer). This way you force IE to finish rendering before returning control to your script since it can only provide the correct value that your script requested after finishing to calculate the layout.

Third guess that might help is that you ensure to parse the HTML out-with the page's layout. This would prevent painting half-done layouts over and over again. To do so, you could detach the StoreDetailsContainer element from the DOM prior your call to html. Now, IE has the change to construct the DOM without affecting the layout. After that you would re-append the StoreDetailsContainer into the DOM. Compared to a normal innerHTML set, this detaching and re-attaching of the container allows you to control when the HTML is parsed to build the DOM tree and when the layout is calculated.



回答4:

Try this code. The load event is fired after ready event, so it may work.

$(document).ready(function(){
  $("#StoreDetailsContainer").html($(tableHtml))
  });
$(window).load(function(){
  $("#StoreDetailsTable").tablesorter({ sortList: [[0, 0]], cssChildRow: "SubTable" }).filtertable({ cssChildRow: "SubTable" });
  });


回答5:

I think that it could work like this:

$("#StoreDetailsContainer").html($(tableHtml))
.find("#StoreDetailsTable")
.tablesorter({ sortList: [[0, 0]], cssChildRow: "SubTable" })
.filtertable({ cssChildRow: "SubTable" });