Javascript search with includes for each DIV

I am trying to develop a filter function with includes. As for now, I have 3 main DIV, and each main DIV has its own DIV. The current script I have now only worked on main DIV.

Instead of highlighting main DIV, I only want to highlight matched char DIV.

For example, when key in 'inner', Inner First and Inner Sec will be highlighted. When key in 'Inner First', only DIV for Inner First will be highlighted.

Would appreciate if anyone of you can help me. Thanks in advance.

function myFunction() {
  var input = document.getElementById("Search");
  var filter = input.value.toLowerCase();
  var nodes = document.getElementsByClassName('target');
  for (i = 0; i < nodes.length; i++) {
    nodes[i].style.backgroundColor = "";
    if (input.value !== '') {
      if (nodes[i].innerText.toLowerCase().includes(filter)) {
        nodes[i].style.backgroundColor = "blue";
        for (j = 0; j < nodes[i].length; j++) {
          nodes[j].style.backgroundColor = "";
          if (input.value !== '') {
            if (nodes[j].innerText.toLowerCase().includes(filter)) {
              nodes[j].style.backgroundColor = "grey";
              for (k = 0; k < nodes[j].length; k++) {
                nodes[k].style.backgroundColor = "";
                if (input.value !== '') {
                  if (nodes[k].innerText.toLowerCase().includes(filter)) {
                    nodes[k].style.backgroundColor = "yellow";
                  } else {
                    nodes[k].style.backgroundColor = "red";
                  }
                }
              }
            } else {
              nodes[j].style.backgroundColor = "red";
            }
          }
        }
      } else {
        nodes[i].style.backgroundColor = "red";
      }
    }
  }

}
<table align="center" width="20%">
  <tr>
    <td style="padding-right: 10px">
      <input type="text" id="Search" title="Type in a name">
      <button onclick="myFunction()">
        Click to search
      </button>
    </td>
  </tr>
</table>
<br>
<div class="target">
  This is my DIV element.
  <div class="target">
    Inner First
    <div class="target">
      Inner Sec
    </div>
  </div>
</div>
<div class="target">
  This is another Div element.
</div>
<div class="target">
  Can you find me?
</div>

2 answers

  • answered 2020-05-22 13:55 Geoman Yabes

    The problem with your code is that node.innerText also gives the text of the child elements. To fix this, you should use node.childNodes[0].nodeValue which will only give the node's text.

    Moreover, you are doing nested loops but referencing incorrectly:

    for (j = 0; j < nodes[i].length; j++).

    nodes[i].length here is undefined. Maybe you mean nodes[i].children.length?

    Also, your code is hard to follow with all the nested loops which does the same thing and just differ in color. I suggest you make a recursive function.

    Please see below function if I what I'm thinking is correct. I guess you wanted to put different colors depending of the level of the node in the heirarchy. (Open your developer tool to see console.log outputs)

    var input;
    var filter;
    var nodes;
    var colors;
    
    function myFunction() {
      //initialize variables
      input = document.getElementById("Search");
      filter = input.value.toLowerCase();
      nodes = document.getElementsByClassName('target');
      // store colors here for accessing via index
      colors = ['blue', 'grey', 'yellow', 'green'];
    
      if (filter !== '') {
        updateNodesBg(nodes); //neat
      }
    }
    
    function updateNodesBg(lNodes, colorIdx) {
      colorIdx = colorIdx || 0; // this will be the index of the color
    
      for (var i = 0; i < lNodes.length; i++) {
        var currentNode = lNodes[i];
        var currentText = currentNode.childNodes[0].nodeValue;
        var innerText = currentNode.innerText;
        console.log('currentText and innerText EQUAL?', currentText === innerText);
        if (currentText.toLowerCase().includes(filter)) {
          currentNode.style.backgroundColor = colors[colorIdx]; //pass in the index to get the color
        } else {
          currentNode.style.backgroundColor = 'red'; //else, we should color red
        }
        if (currentNode.children && currentNode.children.length > 0) {
          updateNodesBg(currentNode.children, colorIdx + 1); //if the node has children, call `updateNodesBg` recursively
        }
      }
    }
    <table align="center" width="20%">
      <tr>
        <td style="padding-right: 10px">
          <input type="text" id="Search" title="Type in a name">
          <button onclick="myFunction()">
            Click to search
          </button>
        </td>
      </tr>
    </table>
    <br>
    <div class="target">
      This is my DIV element.
      <div class="target">
        Inner First 1
        <div class="target">
          Inner Sec 1
          <div>Inner Third 1</div>
        </div>
        <div class="target">
          Inner Sec 2
          <div>Inner Third 1</div>
          <div>Inner Third 2
            <div>Inner Fourth 1</div>
          </div>
        </div>
      </div>
      <div class="target">
        Inner First 2
        <div class="target">
          Inner Sec 2
          <div>Inner Third 2</div>
        </div>
      </div>
    </div>
    <div class="target">
      This is another Div element.
    </div>
    <div class="target">
      Can you find me?
    </div>

    Note that .nodeValue has different returns depending on the type of the node:

    Node                    Value of nodeValue
      CDATASection              Content of the CDATA section
      Comment                   Content of the comment
      Document                  null
      DocumentFragment          null
      DocumentType              null
      Element                   null
      NamedNodeMap              null
      EntityReference           null
      Notation                  null
      ProcessingInstruction     Entire content excluding the target
      Text                      Content of the text node
    

  • answered 2020-05-22 14:19 Matt Ellen

    You need to check if the text is in the current div, then in any child divs.

    InnerText and textContent both concatenate text of the parent and all children, so you'll need to figure out where the text is actually coming from.

    I do this recursively, because I don't think you'll have that many layers of divs. If there is a lot of nesting, then you'd need to take an iterative approach

    function myFunction()
    {
      let targets = document.querySelectorAll('.target');
      targets.forEach(target => target.style.background = 'white');
      let filterText = document.getElementById('Search').value;
      if(filterText != '')
      {
        let result = [...targets].filter(target => target.textContent.includes(filterText));
        result.forEach(r => { checkSelf(r, filterText); checkKids(r, filterText); });
      }
    }
    
    function checkSelf(element, filterText)
    {
      let selfText = element.textContent;
      for(let child of element.children)
      { 
        selfText = selfText.replace(child.textContent, '');
      }
      if(selfText.includes(filterText))
      {
        element.style.background = 'limegreen';
      }
      else
      {
        element.style.background = 'white';
      }
    }
    
    function checkKids(element, filterText)
    {
      for(let child of element.children)
      {
        if(!child.textContent.includes(filterText))
        {
          child.style.background = 'white';
        }
        else
        {
          checkKids(child, filterText);
        }
      }
    }
    <table align="center" width="20%">
      <tr>
        <td style="padding-right: 10px">
          <input type="text" id="Search" title="Type in a name">
          <button onclick="myFunction()">
            Click to search
          </button>
        </td>
      </tr>
    </table>
    <br>
    <div class="target">
      This is my DIV element.
      <div class="target">
        Inner First
        <div class="target">
          Inner Sec
        </div>
      </div>
    </div>
    <div class="target">
      This is another Div element.
    </div>
    <div class="target">
      Can you find me?
    </div>