There have been instances when I needed a way to select DOM elements containing a particular text or string, but couldn’t find any nice solution for it. Though, there is a jQuery method which does almost the same thing:
jQuery( “:contains(text)” )
What the above jQuery method does is returns all the elements containing the text but also returns the elements which do not directly contain the text, for instance the below code will return two elements instead of one:
But that is not what I needed. I needed something which gives me elements directly containing the text. I googled a little more and on SO found this solution; a little improved way of doing the same. But after some time I realized that this also has a limitation. Actually it fails in scenarios like below as mentioned in one of the comments:
So I decided to write my own JS method to do the task. Here is how I proceeded with my solution.
The idea is simply to traverse all the DOM elements and check if it contains the intended text and then repeat the same for all its child nodes. Pretty straight forward, I know. Here is what I did:
The code in above snapshot, traverses through each element and selects elements directly containing the text then repeats same for all its child nodes(if it has any).
First it checks if the element directly contains the text and has no children (Line 13), then in the Else-If block checks if it also has child nodes. Notice the Line 16, is useful when the text is directly present in the current node and it has child nodes(Line 19), which in turn may contain the text in them.
Selecting only visible Elements
The code in above snapshot will work no matter what the state of the element is, i.e, it will return all the elements which are not visible to the users(hidden by CSS like display property set to ‘none‘) but are part of the DOM. To avoid that a condition can be added in the Else-If statement to make sure the element is properly visible and interactive for the user as below:
Putting Everything Together
Putting all the pieces so far together, we get following:
In the above code listing, I have wrapped the traverse function inside a bigger function namely findElementByText, which when called will internally call the traverse function and return the result. Notice here that our new function findElementByText takes two parameters: text(the text to be searched) and node(an optional DOM element). If the second parameter is undefined then the body element of the document is used as the first element to start searching with.
Also notice the If condition at Line 45 where it checks if the element is of type Node.NODE_TEXT then it return the direct parent of it. Finally it returns an array of the elements which directly contain the specified text. The source code for the final function with the above examples can be found at this Fiddle.
Please let me know there is any problem with the code or it can be improved in anyway possible. Later.