diff --git a/Readme.md b/Readme.md
index e0edc46c35..2ae9c1286d 100644
--- a/Readme.md
+++ b/Readme.md
@@ -507,6 +507,9 @@ $.dom()
// }]
```
+#### $.contains( container, contained )
+Checks to see if the `contained` DOM element is a descendent of the `container` DOM element.
+
#### $.isArray( array )
Checks to see the passed argument is an array.
diff --git a/lib/static.js b/lib/static.js
index 080ea1daee..278969bd39 100644
--- a/lib/static.js
+++ b/lib/static.js
@@ -71,3 +71,25 @@ var text = exports.text = function(elems) {
var root = exports.root = function() {
return this(this._root);
};
+
+/**
+ * $.contains()
+ */
+var contains = exports.contains = function(container, contained) {
+
+ // According to the jQuery API, an element does not "contain" itself
+ if (contained === container) {
+ return false;
+ }
+
+ // Step up the descendents, stopping when the root element is reached
+ // (signaled by `.parent` returning a reference to the same object)
+ while (contained && contained !== contained.parent) {
+ contained = contained.parent;
+ if (contained === container) {
+ return true;
+ }
+ }
+
+ return false;
+};
diff --git a/test/fixtures.js b/test/fixtures.js
index e62e68df9e..41bc1fc0ea 100644
--- a/test/fixtures.js
+++ b/test/fixtures.js
@@ -12,3 +12,10 @@ exports.vegetables = [
'
Sweetcorn',
''
].join('');
+
+exports.food = [
+ '',
+ exports.fruits,
+ exports.vegetables,
+ '
'
+].join('');
diff --git a/test/utilities.js b/test/utilities.js
new file mode 100644
index 0000000000..841716652c
--- /dev/null
+++ b/test/utilities.js
@@ -0,0 +1,33 @@
+var expect = require('expect.js'),
+ $ = require('../'),
+ food = require('./fixtures').food;
+
+describe('utility methods', function() {
+
+ describe('.contains', function() {
+
+ it('(container, contained) : should correctly detect the provided element', function() {
+ var $food = $(food);
+ var $fruits = $food.find('#fruits');
+ var $apple = $fruits.find('.apple');
+
+ expect($.contains($food[0], $fruits[0])).to.equal(true);
+ expect($.contains($food[0], $apple[0])).to.equal(true);
+ });
+
+ it('(container, other) : should not detect elements that are not contained', function() {
+ var $food = $(food);
+ var $fruits = $food.find('#fruits');
+ var $vegetables = $food.find('#vegetables');
+ var $apple = $fruits.find('.apple');
+
+ expect($.contains($vegetables[0], $apple[0])).to.equal(false);
+ expect($.contains($fruits[0], $vegetables[0])).to.equal(false);
+ expect($.contains($vegetables[0], $fruits[0])).to.equal(false);
+ expect($.contains($fruits[0], $fruits[0])).to.equal(false);
+ expect($.contains($vegetables[0], $vegetables[0])).to.equal(false);
+ });
+
+ });
+
+});