32
Implementing the DOM contains() method
As per MDN,
The Node.contains() method returns a Boolean value indicating whether a node is a descendant of a given node, i.e. the node itself, one of its direct children (childNodes), one of the children's direct children, and so on.
But wait,
Node.prototype.contains(...)
already exists. I want another name for our custom function. Let's google synonym of contains
coz 

Certainly we ain't going with swallow. I think includes would be cool as both
Array
and String
have includes as well in their prototypes. 
Before we proceed one important thing to know is that when adding new method to
prototype
and expecting to use it like so :-document.includes(document.body)
, the method should not be an arrow function so that
document
can be accessed inside the includes
function via this
keyword. Alright then, let's implement
Node.prototype.includes
in 4 different ways :-
1 Node.prototype.includes = function(node){
2 const currentNode = this;
3 if(!currentNode)
4 return false;
5 if(currentNode===node)
6 return true;
7 let isNodeFound = false;
8 for(let index = 0;index<currentNode.childNodes.length;index++){
9 isNodeFound = isNodeFound || currentNode.childNodes[index].includes(node);
10 if(isNodeFound) return true;
11 }
12 return false;
13 }
Explanation :-
currentNode
to this
and If currentNode
doesn't exist, simply return false
.currentNode
is equal to node
return true
.isNodeFound
to false
. Then loop over childNodes
of the currentNode
and on each child, call the includes
method again to check if they include the node
element. If they do, isNodeFound
will ultimately become true
since it is being Orrrrrrd with the results coming from respective childNodes
and reassigned to itself. Once isNodeFound
is true
, we don't need to loop over rest of the childNodes
of currentNode
and exit early by returning true
else ultimately return false
.1 Node.prototype.includes = function (node) {
2 const queue = [];
3 let currentNode = this;
4 queue.push(currentNode);
5 while (queue.length) {
6 currentNode = queue.shift();
7 if (currentNode === node) return true;
8 if (currentNode.hasChildNodes()) {
9 queue.push(...currentNode.childNodes);
10 }
11 }
12 return false;
13 };
Explanation :-
queue
. Set currentNode
to this
and push
(or enqueue to be specific) it. queue
is not empty, dequeue the currentNode
from front of the queue
(using shift
here). If currentNode
is equal to node
then return true
. Otherwise enqueue the childNodes
of currentNode
(using push
here). Once we are out of the while
loop, we have traversed all the nodes and can safely say that we couldn't find the node
and return false
.Note - The above can be transformed to iterative DFS by using
pop
instead of shift
and obviously for the sake of consistency, rename queue
to stack
.
Till now both the approaches followed the classic DS/Algo traversal with DFS and BFS.
We are now going to see 2 more approaches which take benefit of certain properties that are specifically applicable to DOM nodes.
We are now going to see 2 more approaches which take benefit of certain properties that are specifically applicable to DOM nodes.
LCRS (Left Child Right Sibling) form
1 Node.prototype.includes = function (node) {
2 const currentNode = this;
3 if (!currentNode)
4 return false;
5 if (currentNode === node) return true;
6 return !!(currentNode.firstChild?.includes(node) || currentNode.nextSibling?.includes(node))
7 };
Explanation :-
- Initialize
currentNode
tothis
and ifcurrentNode
doesn't exist, returnfalse
. - If
currentNode
is equal tonode
returntrue
firstChild
includes the node
OR current node's nextSibling
includes the node
. Also notice the !!
. That's because I have used the ?
operator due to which we can end up with undefined || undefined
condition or false || undefined
condition where both evaluate to undefined
which is a falsy value and so !!
will ensure undefined
coerces to false
. 
1 Node.prototype.includes = function(node){
2 const currentNode = this;
3 while(node){
4 if(currentNode===node) return true;
5 node = node.parentNode;
6 }
7 return false;
8 }
Explanation :-
node
exists, we check if currentNode
is equal to node
and if it is we return true
, else the node
is made to point to it's parentNode
for further comparisons. If we exit the while
loop, it's safe to say that the node
isn't a contained within the currentNode
and thus, return false
.
And here is a working codepen with all 4 implementations. Comment the rest for any one to reflect ✨.
Have more ways to implement the same ? Feel free to share your approach in the comment section 👇.
32