diff --git a/CHANGELOG b/CHANGELOG
index 643b69b..fd7e73c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* Performance optimizations for Element#descendantOf. Costliness should no longer be dependent on the difference in depth between the parent and the child. [Andrew Dupont]
+
* Apply the workaround for the Firefox "blinking element" opacity=1 bug only to Firefox 1.5. [Thomas Fuchs]
* Add event.stopped, a boolean that is set to `true` when Event#stop is called. [Andrew Dupont, sam]
diff --git a/src/dom.js b/src/dom.js
index a3d24e2..17161eb 100644
--- a/src/dom.js
+++ b/src/dom.js
@@ -358,6 +358,20 @@ Element.Methods = {
descendantOf: function(element, ancestor) {
element = $(element), ancestor = $(ancestor);
+
+ if (element.compareDocumentPosition)
+ return (element.compareDocumentPosition(ancestor) & 24) === 8;
+
+ if (element.sourceIndex) {
+ var e = element.sourceIndex, a = ancestor.sourceIndex,
+ nextAncestor = ancestor.nextSibling;
+ if (!nextAncestor) {
+ do { ancestor = ancestor.parentNode; }
+ while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
+ }
+ if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
+ }
+
while (element = element.parentNode)
if (element == ancestor) return true;
return false;
diff --git a/test/unit/dom.html b/test/unit/dom.html
index fb2438b..1ed5565 100644
--- a/test/unit/dom.html
+++ b/test/unit/dom.html
@@ -319,7 +319,13 @@
content
-
+
@@ -1003,8 +1009,28 @@
testDescendantOf: function() {with(this) {
assert($('child').descendantOf('ancestor'));
assert($('child').descendantOf($('ancestor')));
- assert($('great-grand-child').descendantOf('ancestor'));
- assert(!$('great-grand-child').descendantOf('not-in-the-family'));
+
+ assert(!$('ancestor').descendantOf($('child')));
+
+ assert($('great-grand-child').descendantOf('ancestor'), 'great-grand-child < ancestor');
+ assert($('grand-child').descendantOf('ancestor'), 'grand-child < ancestor');
+ assert($('great-grand-child').descendantOf('grand-child'), 'great-grand-child < grand-child');
+ assert($('grand-child').descendantOf('child'), 'grand-child < child');
+ assert($('great-grand-child').descendantOf('child'), 'great-grand-child < child');
+
+ window.debug = true;
+ assert($('sibling').descendantOf('ancestor'), 'sibling < ancestor');
+ assert($('grand-sibling').descendantOf('sibling'), 'grand-sibling < sibling');
+ assert($('grand-sibling').descendantOf('ancestor'), 'grand-sibling < ancestor');
+
+ assert($('grand-sibling').descendantOf(document.body), 'grand-sibling < body');
+ window.debug = false;
+
+ assert(!$('great-grand-child').descendantOf('great-grand-child'), 'great-grand-child < great-grand-child');
+ assert(!$('great-grand-child').descendantOf('sibling'), 'great-grand-child < sibling');
+ assert(!$('sibling').descendantOf('child'), 'sibling < child');
+ assert(!$('great-grand-child').descendantOf('not-in-the-family'), 'great-grand-child < not-in-the-family');
+ assert(!$('child').descendantOf('not-in-the-family'), 'child < not-in-the-family');
}},
testChildOf: function() {with(this) {