Bug Tracker

Opened 8 years ago

Closed 7 years ago

#11082 closed bug (wontfix)

jQuery.support object now incorrect/incomplete until jQuery.ready fires

Reported by: ppan235@… 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

Change History (3)

comment:1 Changed 8 years ago by dmethvin

Component: unfiledsupport
Priority: undecidedlow
Status: newopen

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.

comment:2 in reply to:  1 Changed 8 years ago by ppan235@…

Replying to 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.

comment:3 Changed 7 years ago by dmethvin

Resolution: wontfix
Status: openclosed

We'll track this in #10814, and #11766 moved jQuery.browser to an unstable list with the idea that we may change it without deprecation notices.

Note: See TracTickets for help on using tickets.