Reputation: 529
I am writing a UserScript that will remove elements from a page that contain a certain string.
If I understand jQuery's contains() function correctly, it seems like the correct tool for the job.
Unfortunately, since the page I'll be running the UserScript on does not use jQuery, I can't use :contains(). Any of you lovely people know what the native way to do this is?
http://codepen.io/coulbourne/pen/olerh
Upvotes: 34
Views: 23912
Reputation: 21193
The original question is from 2013
The TreeWalker API has been around for ages, IE9 was the last browser to implement it... in 2011
All those 'modern' and 'super-modern' querySelectorAll("*")
need to process all nodes and do string comparisons on every node.
The TreeWalker API gives you only the #text
Nodes, and then you do what you want with them.
You could also use the NodeIterator API, but TreeWalker is faster
function textNodesContaining(txt, root = document.body) {
let nodes = [],
node,
tree = document.createTreeWalker(
root,
4, // NodeFilter.SHOW_TEXT
{
node: node => RegExp(txt).test(node.data)
});
while (node = tree.nextNode()) { // only return accepted nodes
nodes.push(node);
}
return nodes;
}
textNodesContaining(/Overflow/);
textNodesContaining("Overflow").map(x=>console.log(x.parentNode.nodeName,x));
// get "Overflow" IN A parent
textNodesContaining("Overflow")
.filter(x=>x.parentNode.nodeName == 'A')
.map(x=>console.log(x));
// get "Overflow" IN A ancestor
textNodesContaining("Overflow")
.filter(x=>x.parentNode.closest('A'))
.map(x=>console.log(x.parentNode.closest('A')));
Upvotes: 5
Reputation: 1299
Super modern one-line approach with optional chaining operator
[...document.querySelectorAll('*')].filter(element => element.childNodes?.[0]?.nodeValue?.match('❤'));
And better way is to search in all child nodes
[...document.querySelectorAll("*")].filter(e => e.childNodes && [...e.childNodes].find(n => n.nodeValue?.match("❤")))
Upvotes: 12
Reputation: 3592
This is the modern approach
function get_nodes_containing_text(selector, text) {
const elements = [...document.querySelectorAll(selector)];
return elements.filter(
(element) =>
element.childNodes[0]
&& element.childNodes[0].nodeValue
&& RegExp(text, "u").test(element.childNodes[0].nodeValue.trim())
);
}
Upvotes: 3
Reputation: 94121
This should do in modern browsers:
function contains(selector, text) {
var elements = document.querySelectorAll(selector);
return [].filter.call(elements, function(element){
return RegExp(text).test(element.textContent);
});
}
Then use it like so:
contains('p', 'world'); // find "p" that contain "world"
contains('p', /^world/); // find "p" that start with "world"
contains('p', /world$/i); // find "p" that end with "world", case-insensitive
...
Upvotes: 41
Reputation: 68440
If you want to implement contains
method exaclty as jQuery does, this is what you need to have
function contains(elem, text) {
return (elem.textContent || elem.innerText || getText(elem)).indexOf(text) > -1;
}
function getText(elem) {
var node,
ret = "",
i = 0,
nodeType = elem.nodeType;
if ( !nodeType ) {
// If no nodeType, this is expected to be an array
for ( ; (node = elem[i]); i++ ) {
// Do not traverse comment nodes
ret += getText( node );
}
} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
// Use textContent for elements
// innerText usage removed for consistency of new lines (see #11153)
if ( typeof elem.textContent === "string" ) {
return elem.textContent;
} else {
// Traverse its children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
ret += getText( elem );
}
}
} else if ( nodeType === 3 || nodeType === 4 ) {
return elem.nodeValue;
}
// Do not include comment or processing instruction nodes
return ret;
};
SOURCE: Sizzle.js
Upvotes: 6
Reputation: 814
Well, jQuery comes equipped with a DOM traversing engine that operates a lot better than the one i'm about to show you, but it will do the trick.
var items = document.getElementsByTagName("*");
for (var i = 0; i < items.length; i++) {
if (items[i].innerHTML.indexOf("word") != -1) {
// Do your magic
}
}
Wrap it in a function if you will, but i would strongly recommend to use jQuery's implementation.
Upvotes: 3