While debugging my app with 'Profiles' in DevTools I found "Detached DOM tree's" accumulating. These detached nodes have retaining tree consisting mostly of checkContext
functions (ing from sizzle inside jQuery - v1.10.1).
I'm not sure how to proceed with this. What does this result mean?
While debugging my app with 'Profiles' in DevTools I found "Detached DOM tree's" accumulating. These detached nodes have retaining tree consisting mostly of checkContext
functions (ing from sizzle inside jQuery - v1.10.1).
I'm not sure how to proceed with this. What does this result mean?
Share Improve this question asked Jun 24, 2013 at 15:23 Konrad DzwinelKonrad Dzwinel 37.9k12 gold badges102 silver badges106 bronze badges2 Answers
Reset to default 6Sizzle stores piled selectors in selector cache, which by default stores up to 50 entries. You may experiment by setting $.expr.cacheLength = 1
before doing any selections and see if they go away.
Here's the docs https://github./jquery/sizzle/wiki/Sizzle-Documentation#-internal-api. Seems internal so don't depend on it or anything in actual production code.
This is actually a bug, there's no reason that Sizzle needs to hang onto the context node, it's only doing it because it's not cleaning up after setting a temporary variable. I filed an issue for it, fixed it, ran all the Sizzle tests, and did a pull request.
If you want to patch your existing copy of jQuery or Sizzle:
Open your jQuery or Sizzle file
Search for the
matcherFromTokens
functionFind this code within it (near the top):
matchers = [ function( elem, context, xml ) { return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( (checkContext = context).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); } ];
Change the
return
tovar rv =
, and addcheckContext = undefined;
and thenreturn rv;
at the end of the anonymous function, e.g.:matchers = [ function( elem, context, xml ) { var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( (checkContext = context).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); // Release the context node (issue #299) checkContext = null; return ret; } ];
Note: That code assigns null
to checkContext
because apparently that's their style. If it were me, I'd've assigned undefined
instead.
If there's any issue with the fix raised during the pull request / merge process, I'll update the answer.
It's better to keep letting Sizzle cache selectors, because jQuery uses the piled selector stuff with event delegation, and you don't really want it to have to reparse and rebuild the matcher functions each time a relevant event occurs so it can figure out whether elements match it.
This isn't the only place jQuery holds onto elements in piled selectors, unfortunately. Each place it does is probably a bug that could use fixing. I've only had the time to track down one other, which I've also reported and fixed (pending the pull request being landed):
If you search for "Potentially plex pseudos" you'll find this for the :not
pseudo-selector:
pseudos: {
// Potentially plex pseudos
"not": markFunction(function( selector ) {
// Trim the selector passed to pile
// to avoid treating leading and trailing
// spaces as binators
var input = [],
results = [],
matcher = pile( selector.replace( rtrim, "$1" ) );
return matcher[ expando ] ?
markFunction(function( seed, matches, context, xml ) {
var elem,
unmatched = matcher( seed, null, xml, [] ),
i = seed.length;
// Match elements unmatched by `matcher`
while ( i-- ) {
if ( (elem = unmatched[i]) ) {
seed[i] = !(matches[i] = elem);
}
}
}) :
function( elem, context, xml ) {
input[0] = elem;
matcher( input, null, xml, results );
return !results.pop();
};
}),
The problem is in the function after the :
in the conditional operator:
function( elem, context, xml ) {
input[0] = elem;
matcher( input, null, xml, results );
return !results.pop();
};
Note that it never clears input[0]
. Here's the fix:
function( elem, context, xml ) {
input[0] = elem;
matcher( input, null, xml, results );
// Don't keep the element (issue #299)
input[0] = null;
return !results.pop();
};
That's all I have the time to track down at present.