Side navigation
#11082 closed bug (wontfix)
Opened December 20, 2011 11:57PM UTC
Closed June 26, 2012 03:23AM UTC
jQuery.support object now incorrect/incomplete until jQuery.ready fires
Reported by: | ppan235@gmail.com | Owned by: | |
---|---|---|---|
Priority: | low | Milestone: | None |
Component: | support | Version: | 1.7.1 |
Keywords: | Cc: | ||
Blocked by: | Blocking: |
Description
Summary
Two recent changes (links below) mean that the jQuery support object is now incorrect/incomplete until jQuery.ready fires. This means jQuery functions incorrectly before this point. The behaviour change is a regression: 1.7.0 doesn't do this; 1.7.1 does. Test case is below.
Cause
Various tests that probe the browser environment and update jQuery.support accordingly are now being run inside a function passed to jQuery.ready.
https://github.com/jquery/jquery/commit/93750cee36f0b2edc1afd2dad64d9306865a39b9
https://github.com/jquery/jquery/commit/9bea2167c434d5b97643f53da16b04927dc02a7d
Search for "jQuery(function() {".
Problem
A race condition now exists for client code that loads jquery and then uses it immediately, without explicitly waiting for jQuery.ready to fire. If jQuery.ready/browser-probes run first, the client code sees the complete/correct jQuery.support object. If the client code runs first, it sees the incomplete/correct version. This means that jQuery functions incorrectly at this point.
I've seen this problem as I'm loading jQuery into an already loaded page (using a bookmarklet), and then calling jQuery immediately. My client code isn't inside its own jQuery.ready, as I've seen problems in the past with the "ready" event not firing on an already loaded page.
Solution
I "fixed" the problem on Mac Firefox 8.0.1 by making all the browser probes for jQuery.support run inline, rather than at jQuery.ready. I just commented out the two uses of "jQuery(function() {". However, this doesn't seem like a long-term fix.
Ideally, jQuery.support (and jQuery as a whole) should be fully initialized by the time jQuery loads. So all setup must run within the jquery load thread, not triggered by a later event. In the case of the browser probes for jQuery.support:
- If the probes need a fully loaded DOM, is there some way of detecting this inline, rather than needing to wait for the jQuery.ready event to fire?
- Or could a function passed to jQuery.ready be run inline immediately, if the DOM is already loaded?
Test case
Copy html code below into test file, then load and view console. Output shows support object is incorrect before jQuery.ready:
$.support.reliableHiddenOffsets before jQuery.ready: undefined $.support.reliableHiddenOffsets after jQuery.ready: true
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <body> <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.js"></script> <script type="text/javascript"> console.log("$.support.reliableHiddenOffsets before jQuery.ready: " + $.support.reliableHiddenOffsets); $(function() { console.log("$.support.reliableHiddenOffsets after jQuery.ready: " + $.support.reliableHiddenOffsets); }) </script> </body> </html>
To see correct 1.7.0 behaviour, just load jquery-1.7.0.js instead. Output should be:
$.support.reliableHiddenOffsets before jQuery.ready: true $.support.reliableHiddenOffsets after jQuery.ready: true
Tested on Mac Firefox 8.0.1 and Safari 5.1.2
Attachments (0)
Change History (3)
Changed December 21, 2011 12:10AM UTC by comment:1
component: | unfiled → support |
---|---|
priority: | undecided → low |
status: | new → open |
Changed December 21, 2011 03:29AM UTC by comment:2
Replying to [comment:1 dmethvin]:
Thanks for the quick response. Three points:
Would you be willing to fix this bug for the case when jQuery is being loaded into already loaded page? Stability shouldn't be an issue in this case, as the whole DOM tree is already there. Rough sketch:
- When doing feature detects that require a full DOM tree, test if the DOM tree is loaded, maybe using the check logic that currently fires the jQuery.ready event.
- If the DOM tree is loaded, do feature detects inline, else postpone to a jQuery.ready function
I'd very much appreciate this, as this the case I specifically care about.
'''This bug isn't really connected to jQuery.support being externally accessible.''' I tracked this bug down when investigating a huge slowdown in jQuery.is(':visible') performance. The root cause was that the underlying logic has a fast way of determining element visibility, if $.support.reliableHiddenOffsets is true. Since the variable now returns undefined, the visibility logic falls back to using the slow alternative.
Improving performance is a laudable goal, but being correct is also important :-) Improving jquery "boot" time is a great idea; doing this by putting off non-essential feature detects seems a good way of doing this. However, this current approach of postponing feature detects until jQuery.ready fires has downsides:
- jQuery interface functions that are internally dependent on a correct jQuery.support object can now behave slowly, or possibly incorrectly, as with jQuery.is(':visible')
- jQuery usage will now become more complex, and possibly confusing. Users will need to be told (and remember) that jQuery will only behave as expected once jQuery.ready has fired.
I imagine that the ideal solution is some form of just-in-time feature detection, triggered by calling an interface function that uses a particular jQuery.support variable. So, the first call to jQuery.is(':visible') triggers a feature detect to set $.support.reliableHiddenOffsets.
Sorry, I agree with your analysis but this is something we're more likely to document than change.
Our experience is showing that page load is a very inopportune time to do many of these feature detects. First there is the performance issue; if all the tests have to be done before document ready then we are holding up the page for tests that may not be needed until much later. Then there is the problem that some browsers (particularly old ones) seem to be in a relatively unstable state until the page has fully and completely loaded; feature tests sometimes stress them out.
My preferred solution would be to deprecate jQuery.support as an external interface. We tried to share these feature detects when we could, but they become a liability when others depend on them and we can no longer deliver them reliably or on time. In cases where we no longer need those feature detects due to code rewrites we are technically unable to remove them because it's an external interface.