Bug Tracker

Modify

Ticket #12282 (closed bug: fixed)

Opened 21 months ago

Last modified 18 months ago

1.8.0 regression - document ready is fired too early on IE 9/10

Reported by: vrodic@… Owned by: mikesherov
Priority: high Milestone: 1.8.1
Component: core Version: 1.8.0
Keywords: Cc:
Blocking: Blocked by:

Description

Hi,

We have a weird banner system that injects an external script with document.write that triggers a regression in $(document).ready() in jQuery 1.8.0 (jQuery 1.7.2 works fine).

Basically, document ready is fired before the object defined below document ready code section is called (on IE 9 or IE10 - IE 10 is from last years Windows 8 developer preview).

I've created a minimal test case at:  http://www.centarnekretnina.net/dev/test.html

The page should execute alert("test");

IE10 dev tools console outputs:

SCRIPT5007: Unable to get value of the property 'init': object is null or undefined test.html, line 20 character 5

You can download the two small files (test.html and test2.js), change the jquery version and see that this works fine on jQuery 1.7.2

Change History

comment:1 follow-up: ↓ 2 Changed 21 months ago by rwaldron

  • Owner set to vrodic@…
  • Status changed from new to pending

Thanks for taking the time to contribute to the jQuery project! Please provide a complete reduced test case on jsFiddle to help us assess your ticket.

Additionally, be sure to test against the jQuery Edge version to ensure the issue still exists. To get you started, use this boilerplate:   http://jsfiddle.net/FrKyN/ Open the link and click to "Fork" (in the top menu) to get started.

comment:2 in reply to: ↑ 1 Changed 21 months ago by anonymous

I can't reliably reproduce it with jsfiddle (it probably introduces more side-effects), and my version is pretty minimal already.

I've created another version:

 http://www.centarnekretnina.net/dev/test3.html

That uses  http://code.jquery.com/jquery-git.js (jQuery edge)

You may have to reload the page for the error (no "test" alert) to occur.

comment:3 Changed 21 months ago by vrodic@…

  • Status changed from pending to new

Actually I've found a way to reproduce it on jsfiddle (testing on Windows 8 dev preview - IE10)

 http://jsfiddle.net/dKDdj/2/

After it loads once, click jsfiddle "Run" to run again and take a look at IE10 console (open it before you click run)

comment:4 Changed 21 months ago by mikesherov

  • Owner changed from vrodic@… to mikesherov
  • Priority changed from undecided to high
  • Status changed from new to assigned
  • Component changed from unfiled to core
  • Milestone changed from None to 1.8.1

comment:5 Changed 21 months ago by anonymous

Here is an update that reproduces it without any external resources beyond jQuery.

 http://jsfiddle.net/dKDdj/4/

comment:6 Changed 21 months ago by mikesherov

This is great, thanks.

comment:7 Changed 20 months ago by Denis

Confirmed in IE9 too.

comment:8 Changed 20 months ago by SoonDead

Confirmed in my application too.

$(document).ready() fires before all the javascript files are loaded in IE9.

Last edited 20 months ago by SoonDead (previous) (diff)

comment:9 Changed 20 months ago by mikesherov

Rather than more "me too", it would be helpful if you guys posted examples and links to see this in action.

comment:10 Changed 20 months ago by mikesherov

  • Keywords needsdocs added

So, this fiddle:  http://jsfiddle.net/dKDdj/4/ does not exhibit a problem with .ready(). .ready() is a deferred  http://api.jquery.com/category/deferred-object/ .

If the DOM is ready when you call it, it will execute synchronously. If the DOM is not ready when you call it, it will execute asynchronously.

In the example provided, you are relying on the async execution of .ready(), which isn't a gaurantee. This is because .ready() fires a bit earlier in 1.8 because it does a new check for DOM readiness.

If someone has proof that the DOM can't be manipulated on .ready(), I'd love to hear about it.

SoonDead, Denis, do you have examples?

needs docs to re-emphasis the promisey nature of .ready()

comment:11 Changed 20 months ago by markoj21@…

Here is the issue in jsFiddle thank you vrodic, we were having this issue when we just updated to 1.8 Using the mvc3, with partials this problem presents it self in many areas. It works correctly with IE7 and IE8, Chrome and Firefox.

When you first access the js fiddle link it will run correctly, press the run button it will break. if you change to jquery 1.7.2 it runs correctly it both scenarios.  http://jsfiddle.net/tgo16/H6zte/4/

Test machine IE9 9.0.8112.16421, Windows 7 SP1

comment:12 Changed 20 months ago by mikesherov

markoj21, please read what I wrote. Of course it will break, as you're assuming something about .ready() that just isn't true. It isn't gauranteed to execute asynchronously.

comment:13 Changed 20 months ago by anonymous

So, it seems that in many cases (WebKit, Gecko, IE < 9) ready() currently fires asynchronously but in IE9 it does not. I get that there is no guarantee, but it is likely that many people have been making this incorrect assumption and so the change in detection logic is exposing it and causing breakage. This is going to be a headache to fix...

comment:14 Changed 20 months ago by jaubourg

I don't see how it is a headache. It's as simple as moving the call to $.ready at the end of the inline script. Where it should have been in the first place.

It's not so much that ready is asynchronous in other browsers but rather that the document is actually ready when executing the inline script in IE9. IE9 is better at detecting DOM readiness than other browsers here, seeing as, if the inline script is being executed, it means it was parsed thus so was the whole document.

Anyway, since the dom is ready, the handler is called immediately. The real problem is that our documentation doesn't emphasize the fact a handler will be called immediately if the DOM is ready, even though it's been the case since DOM readiness was first introduced in jQuery.

comment:15 Changed 20 months ago by ChrisS

I've another situation where $(document).ready() is fired too early.

I've a JSP with a default page buffer of 8kb. Which means if the buffer is full it will be flushed and the browser gets the content generated until this time.

Lets assume a client has submitted a search request and the search will take a few seconds. In case the page buffer is full, for example the header and the navigation was generated, it will be flushed to the client, before the server side search is done and has generated any content. In my case the client has already received some script blocks with a $(document).ready() call. But the javascript the $(document).ready() call should execute is at the end of the page. And due to the server side search, which takes a while, the page isn't completely generated yet. Now the Problem is that IE9 sometimes fires $(document).ready() even if the page isn't complete generated.

Below is a little JSP example which demonstrates this issue. I would expect to see "PAGE END" in the console at first. But sometimes I see "document ready" in the console at first.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
   <head> 
		<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
		<title>Test</title>
		<script type="text/javascript" src="/js/jquery-core/1.8/jquery-1.8.min.js"></script>
	</head>
	<body>
		<h1>TEST</h1>
		
		<script type="text/javascript">
		$(document).ready(function() {
			console.log("document ready");
		});
		</script>

		<%
			// Force the buffer to flush (default is 8kb)
			for (int i = 0; i < (8 * 1024); i++) {
				out.write(" ");
			}
		%>
	
		<h2>Sleeping 1 second (simulating server side process)</h2>
		<%	
			try {
				Thread.sleep(1000);
			} catch(Exception e) {
			}
		%>

		<h2>ready</h2>
		<p><a href="?">click</a></p>

		<script type="text/javascript">
			console.log("PAGE END");
		</script>
		
	</body>
</html>

comment:16 Changed 20 months ago by dmethvin

@ChrisS, are the results consistent if there is some HTML below the "PAGE END" log, or if the JavaScript in that block does a document.write? Just curious about the conditions that might affect it.

comment:17 Changed 20 months ago by jaubourg

If that's the case, it could be the nail in the coffin of readyState === "interactive" :/

comment:18 Changed 20 months ago by jaubourg

BTW, isn't that what longLoad.php in test/data/event is supposed to test for?

comment:19 Changed 20 months ago by jaubourg

Could explain this:  http://swarm.jquery.org/result/154890 though I have no clue why it wouldn't fail before.

comment:20 Changed 20 months ago by cdowns

I've had issues when a page has an .ready in the header that calls something from a script block in the body. It looks similar to an issue that requireJS had ( https://github.com/requirejs/domReady/commit/a3e0dd8b6d3d3ee636ff0ca6a5a7c302d6ab33bf). The issue when debugging in IE9 seems to show up due to line 835 of core.js (changed in f5fd41252e3ae48a655c5da4a0b2910bb897b6ed):

if ( document.readyState === "complete" || ( document.readyState !== "loading" && document.addEventListener ) )

IE9 at that point is readyState === "interactive" so it goes through with the .ready, but then you get to line 379 of the .ready:

// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
		if ( !document.body ) {
			return setTimeout( jQuery.ready, 1 );
		}

IE9 will give back a document.body that hasn't run all the scripts. Would it be better at that section to actually do the same .addEventListener stuff that is in the ready.promise instead of just calling the ready again until document.body exists? So for the case that document.body doesn't exist, couldn't you rely on the DOMContentLoaded firing like you do in the ready promise?

comment:21 Changed 20 months ago by mikesherov

@cdowns, the problem here seems to be that document.body exists, and readyState === "interactive", but DOM clearly isn't ready. I think we can fix this by changing the location in which we do Diego Perini's famous doScroll check. We'll see.

@jaubourg, it's not what longLoad.php is doing. That is a long loading iframe on a page to keep interactive going even though DOM is ready. This test case would be partial page load triggers interactive too early when DOM clearly isn't ready.

Hopefully, I can repro this. Silly IE.

comment:22 Changed 20 months ago by dmethvin

@mikesherov, maybe @cdowns has something there. If we could detect whether the browser supports DOMContentLoaded we could just ignore .readyState entirely, which would cover IE9 and IE10. Unfortunately, the trick we already use in support.js runs afoul of Content Security policy so we can't do it that way.

Last edited 20 months ago by dmethvin (previous) (diff)

comment:23 Changed 20 months ago by anonymous

@ChrisS @SoonDead @vrodic @mikesherov @jaubourg I have the same problem : (jQuery 1.8.0) - IE9 sometimes fires $(document).ready() even if the page isn't complete generated.

works fine on jQuery 1.7.2

comment:24 Changed 20 months ago by mikesherov

 https://github.com/jquery/jquery/pull/901

ChrisS and cdowns, thank you thank you thank you.

comment:25 Changed 20 months ago by Mike Sherov

  • Status changed from assigned to closed
  • Resolution set to fixed

Fix #12282. IE has premature .readyState == "interactive". Close gh-901.

Changeset: 0f553ed0ca0c50c5f66377e9f2c6314f822e8f25

comment:26 follow-up: ↓ 29 Changed 20 months ago by ChrisS

I can confirm that the bug is fixed, now. The current version from github works fine in my test and production scenario.

Thank you, mikesherov.

comment:27 Changed 20 months ago by michaeldobbins1@…

I also confirmed this behavior in IE9. It doesn't seem to matter whether you are in IE8 compatibility mode or 'edge' (eg. <meta http-equiv="X-UA-Compatible" content="IE=8,chrome=1,requiresActiveX=true" />). I noticed that ie9 was inconsistent in finding all of the items in a collection on $(document).ready; I added an alert right after my $(document).ready call and found that the alert would popup before all of the DOM was rendered. I upgraded to the github jquery source (1.8.1pre) and all is good; $(document).ready doesn't fire until the DOM is fully loaded. Thanks for getting this fixed - just can't wait for the official release of 1.8.1. There are several other issues with ie9 and jQuery 1.8, which I am hoping will be resolved in the upcoming release.

comment:28 Changed 20 months ago by lucas@…

Please, I can't see the solution. I have the same problem with IE9. Can I help me?

comment:29 in reply to: ↑ 26 Changed 20 months ago by anonymous

Please, put the file to download. Thanks

Replying to ChrisS:

I can confirm that the bug is fixed, now. The current version from github works fine in my test and production scenario.

Thank you, mikesherov.

comment:30 Changed 19 months ago by tentonaxe

Was it intended for this fix to cause IE7 and IE8 to fire document.ready later than modern browsers?  http://jsfiddle.net/Camilo/PFWmS/ it now seems to wait until onload.

Edit: I guess it has always been that way.

Last edited 19 months ago by tentonaxe (previous) (diff)

comment:31 Changed 18 months ago by mikesherov

  • Keywords needsdocs removed

Please follow the  bug reporting guidlines and use  jsFiddle when providing test cases and demonstrations instead of pasting the code in the ticket.

View

Add a comment

Modify Ticket

Action
as closed
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.