Skip to content

Commit 1656af7

Browse files
gibson042timmywil
authored andcommitted
Harden position caching against DOM manipulation for child selectors. Fixes jQuery bug #12337. Closes gh-153.
1 parent ccb60a3 commit 1656af7

File tree

2 files changed

+21
-23
lines changed

2 files changed

+21
-23
lines changed

sizzle.js

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -631,37 +631,34 @@ Expr = Sizzle.selectors = {
631631
var doneName = done++;
632632

633633
return function( elem ) {
634-
var parent, diff,
635-
count = 0,
636-
node = elem;
634+
var node, diff,
635+
childkey = doneName + "." + dirruns + ".",
636+
parent = elem.parentNode,
637+
sizset = elem.sizset;
637638

638639
if ( first === 1 && last === 0 ) {
639640
return true;
640641
}
641642

642-
parent = elem.parentNode;
643-
644-
if ( parent && (parent[ expando ] !== doneName || !elem.sizset) ) {
643+
if ( typeof sizset === "string" && sizset.indexOf( childkey ) === 0 ) {
644+
diff = sizset.substr( childkey.length );
645+
} else if ( parent ) {
646+
diff = 0;
645647
for ( node = parent.firstChild; node; node = node.nextSibling ) {
646648
if ( node.nodeType === 1 ) {
647-
node.sizset = ++count;
648-
if ( node === elem ) {
649+
node.sizset = childkey + (++diff);
650+
if ( elem === node ) {
649651
break;
650652
}
651653
}
652654
}
653-
654-
parent[ expando ] = doneName;
655655
}
656656

657-
diff = elem.sizset - last;
657+
diff -= last;
658658

659-
if ( first === 0 ) {
660-
return diff === 0;
661-
662-
} else {
663-
return ( diff % first === 0 && diff / first >= 0 );
664-
}
659+
return first === 0 ?
660+
diff === 0 :
661+
diff % first === 0 && diff / first >= 0;
665662
};
666663
}
667664

@@ -1125,8 +1122,8 @@ function addCombinator( matcher, combinator ) {
11251122
// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
11261123
if ( !xml ) {
11271124
var cache,
1128-
dirkey = doneName + "." + dirruns,
1129-
cachedkey = dirkey + "." + cachedruns;
1125+
dirkey = doneName + "." + dirruns + ".",
1126+
cachedkey = dirkey + cachedruns;
11301127
while ( (elem = elem[ dir ]) ) {
11311128
if ( elem.nodeType === 1 ) {
11321129
if ( (cache = elem[ expando ]) === cachedkey ) {

test/unit/selector.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ test("attributes", function() {
520520
});
521521

522522
test("pseudo - child", function() {
523-
expect( 41 );
523+
expect( 42 );
524524
t( "First Child", "#qunit-fixture p:first-child", ["firstp","sndp"] );
525525
t( "First Child (case-insensitive)", "#qunit-fixture p:FIRST-CHILD", ["firstp","sndp"] );
526526
t( "Last Child", "p:last-child", ["sap"] );
@@ -536,10 +536,11 @@ test("pseudo - child", function() {
536536
t( "Not Nth Child", "#qunit-fixture p:not(:nth-child(1))", ["ap","en","sap","first"] );
537537

538538
// Verify that the child position isn't being cached improperly
539-
jQuery("p:first-child").after("<div></div>");
540-
jQuery("p:first-child").before("<div></div>").next().remove();
539+
var firstChildren = jQuery("p:first-child").before("<div></div>");
541540

542-
t( "First Child", "p:first-child", [] );
541+
t( "No longer First Child", "p:nth-child(1)", [] );
542+
firstChildren.prev().remove();
543+
t( "Restored First Child", "p:nth-child(1)", ["firstp","sndp"] );
543544

544545
QUnit.reset();
545546

0 commit comments

Comments
 (0)