Raushan Setthi
Raushan Setthi

Reputation: 79

Search DOM elements and navigate at them using javascript

I want to search DOM elements matching by some text. Wanted to implement feature similar to Ctrl +F . Here is what I tried so far:

var elems = document.querySelectorAll("DIV");
elems.forEach(function(e){
     if(e.textContent.includes('Foo Bar')){
          domEl.push(e);
     }
});

but domEl returns 52 elements but Ctrl +F showing only 14 records.

Upvotes: 0

Views: 744

Answers (3)

Cat
Cat

Reputation: 4226

This solution searches children of a container element recursively to collect matching results in a collection array.

Each result object has two properties: the element containing the textNode where the match occurred and the text node itself. This allows us to later extract whatever information we want about the element (such as its id property) and/or about the text node (such as its full text).

const
  myCollection = [],
  searchIn = document.getElementById("container"),
  searchFor = "Stack";

collectMatchingDescendants(searchIn, searchFor, myCollection);
printCollectionInfo();

function collectMatchingDescendants(node, text, collection){
  if (node.hasChildNodes()) {
    let children = node.childNodes;
    for (node of children){
      if(node.nodeType == 3 && node.nodeValue.includes(text)){
        const element = node.parentElement;
        const myObject = { element: element, node: node };
        collection.push(myObject);
      }
      else{ collectMatchingDescendants(node, text, collection); }
    }
  }  
}

function printCollectionInfo(){
  for(let obj of myCollection){
    console.log(`${obj.element.id} matched (text: '${obj.node.nodeValue.trim() }')` );
  }
}
*{ border: 1px solid lightgrey; }
<div id="container">
  <div id="div1">
    Stack Overflow 1
    <p id="p1">Stack Overflow 2</p>
    <p id="p2">Nothing interesting here</p>
  </div>
  <div id="div2">
    Stack Overflow 3
    <p id="p3">Stack Overflow 4
      <span id="span1">Stack Overflow 5</span>
    </p>
  </div>
</div>

Upvotes: 0

I wrestled a bear once.
I wrestled a bear once.

Reputation: 23379

Use NodeType to determine the type of node and ignore anything that isn't a text node. Read the comments:

const getElementsContainingText = (query, selector = '*') =>
  // Get the list of all elements and filter it
  [...document.querySelectorAll(selector)].filter(element =>
    // Get a list of all the current elements childNodes
    [...element.childNodes]
    // Filter out all child nodes that are not plain text
    .filter(ele => ele.nodeType === Node.TEXT_NODE)
    // Get the content of the text node
    .map(ele => ele.textContent)
    // Filter out anything that doesn't match the query
    .filter(text => text.includes(query))
    // Return true if there are any matches
    .length > 0
  );

console.log(getElementsContainingText('foo', 'div'));
console.log(getElementsContainingText('bar', 'div'));
<div>
  bar
  <div>
    foo
  </div>
</div>

You could easily combine the two filters and get rid of the map, but I did it this way so it was easier to explain in the comments, and to keep the lines short.

Upvotes: 2

smr
smr

Reputation: 71

Filter the children of your DIVs to only check #text leafs:

const elems = document.querySelectorAll("DIV");
elems.forEach(function(e) {
    for(const node of e.childNodes) {
        if(node.nodeName==="#text"&&node.textContent.includes("Foo Bar")) {
            domE1.push(e);
            return;
        }

Keep in mind that this will only select the DIVS that directly contain the searched text:

<div id="a">Foo Bar</div>                // a will be selected
<div id="b"><span>Foo Bar</span></div>   // b will NOT be selected
<div id="c"><div id="d">Foo Bar</div></div> // c will NOT be selected, but d will

Upvotes: 0

Related Questions