mehdok
mehdok

Reputation: 1608

Javascript search in innerHtml Exclude html tags

I have written a custom search in javascript for highlighting texts.

the scenario is to get innerHtml and search for the text and highlight them.

the problem: if the user search for i the i in the <div> tag were found and everything messed up.

var textBlock=document.body.innerHTML;
searchIndex = textBlock.toLowerCase().indexOf(what.toLowerCase(), 0);
while(searchIndex >= 0)
{
    ++counter;
    ID = "result" + counter;
    replacement = '<span id='+ID+' style="background-color:#f0da1e">'+what+'</span>';
    textBlock = textBlock.substring(0, searchIndex) + replacement + textBlock.substring(searchIndex + what.length, textBlock.length);
    searchIndex = textBlock.toLowerCase().indexOf(what.toLowerCase(), (searchIndex + replacement.length));
}
document.body.innerHTML=textBlock;

what can i do to skip founded index in tags?

something like this:

if(isTag(searchIndex))
    //do nothing

UPDATE:

if i use innerText instead of innerHtml then all of my text format and style will be ruind.

var textBlock=document.body.innerText;
document.body.innerHTML=textBlock;

Upvotes: 1

Views: 2595

Answers (3)

Alvaro Montoro
Alvaro Montoro

Reputation: 29645

One possible solution would be to work with nodes:

  • Get the body child nodes (instead of the innerHTML)
  • Traverse the nodes:
    • If it's a text node (leaf): search for the string to replace.
    • If it's an element node: get and traverse the children.

Here is a sample function that will highlight the text that you specify:

function highlightText(nodeList, what) {

  // traverse all the children nodes
  for (var x = 0; x < nodeList.length; x++) {

    // text node, search directly
    if (nodeList[x].nodeType == 3) {

      // if it contains the text that you are looking for, proceed with the replacement
      if (nodeList[x].textContent.indexOf(what) >= 0) {

        // your code (mostly :P)
        var ID = "result" + counter;
        var replacement = '<span id="'+ID+'" style="background-color:#f0da1e">'+what+'</span>';
        var textBlock = nodeList[x].textContent;
        var searchIndex = nodeList[x].textContent.indexOf(what);
        while(searchIndex >= 0)
        {
          ++counter;
          ID = "result" + counter;
          replacement = '<span id="'+ID+'" style="background-color:#f0da1e">'+what+'</span>';           
          textBlock = textBlock.substring(0, searchIndex) + replacement + textBlock.substring(searchIndex + what.length, textBlock.length);
          searchIndex = textBlock.toLowerCase().indexOf(what.toLowerCase(), (searchIndex + replacement.length));
        }

        // create a new element with the replacement text
        var replacementNode = document.createElement("span");
        replacementNode.innerHTML = textBlock;

        // replace the old node with the new one
        var parentN = nodeList[x].parentNode;
        parentN.replaceChild(replacementNode, parentN.childNodes[x]);

      }
    } else {
      // element node --> search in its children nodes
      highlightText(nodeList[x].childNodes, what);
    }
  }
}

And here is a sample demo (also available on this JSFiddle):

var counter = 0;

function highlightText(nodeList, what) {

  // traverse all the children nodes
  for (var x = 0; x < nodeList.length; x++) {

    // text node, search directly
    if (nodeList[x].nodeType == 3) {

      // if it contains the text that you are looking for, proceed with the replacement
      if (nodeList[x].textContent.indexOf(what) >= 0) {

        // your code (mostly :P)
        var ID = "result" + counter;
        var replacement = '<span id="'+ID+'" style="background-color:#f0da1e">'+what+'</span>';
        var textBlock = nodeList[x].textContent;
        var searchIndex = nodeList[x].textContent.indexOf(what);
        while(searchIndex >= 0)
        {
          ++counter;
          ID = "result" + counter;
          replacement = '<span id="'+ID+'" style="background-color:#f0da1e">'+what+'</span>';			
          textBlock = textBlock.substring(0, searchIndex) + replacement + textBlock.substring(searchIndex + what.length, textBlock.length);
          searchIndex = textBlock.toLowerCase().indexOf(what.toLowerCase(), (searchIndex + replacement.length));
        }

        // create a new element with the replacement text
        var replacementNode = document.createElement("span");
        replacementNode.innerHTML = textBlock;

        // replace the old node with the new one
        var parentN = nodeList[x].parentNode;
        parentN.replaceChild(replacementNode, parentN.childNodes[x]);

      }
    } else {
      // element node --> search in its children nodes
      highlightText(nodeList[x].childNodes, what);
    }
  }
}

var nodes = document.body.childNodes;
console.log(nodes);
highlightText(nodes, "ar");
<p>Men at some time are masters of their fates: The fault, dear Brutus, is not in our stars, but in ourselves, that we are underlings.</p>
<p><b>William Shakespeare</b>, <em>Julius Caesar</em> (Act I, Scene II)</p>

One issue with this solution is that it adds additional span elements wrapping each text node that contained the searched string (although I don't know how big of an inconvenience that may be for you). It is also recursive, you may want to look into an iterative alternative.


UPDATE. I know you didn't ask for this, but I thought it could be interesting: by reordering the parameters list, and adding some initialization on the first call, you can make the function cleaner for the user, and at the same time, add some interesting functionality:

function highlightText(what, node) {

    // initialize values if first call
    node = node || document.body;
    var nodeList = node.childNodes;

    // traverse all the children nodes
    for (var x = 0; x < nodeList.length; x++) {

        // text node, search directly
        if (nodeList[x].nodeType == 3) {

            // if it contains the text that you are looking for, proceed with the replacement
            if (nodeList[x].textContent.indexOf(what) >= 0) {

                // your code (mostly :P)
                var ID = "result" + counter;
                var replacement = '<span id="'+ID+'" style="background-color:#f0da1e">'+what+'</span>';
                var textBlock = nodeList[x].textContent;
                var searchIndex = nodeList[x].textContent.indexOf(what);
                while(searchIndex >= 0)
                {
                    ++counter;
                    ID = "result" + counter;
                    replacement = '<span id="'+ID+'" style="background-color:#f0da1e">'+what+'</span>';         
                    textBlock = textBlock.substring(0, searchIndex) + replacement + textBlock.substring(searchIndex + what.length, textBlock.length);
                    searchIndex = textBlock.toLowerCase().indexOf(what.toLowerCase(), (searchIndex + replacement.length));
                }

                // create a new element with the replacement text
                var replacementNode = document.createElement("span");
                replacementNode.innerHTML = textBlock;

                // replace the old node with the new one
                var parentN = nodeList[x].parentNode;
                parentN.replaceChild(replacementNode, parentN.childNodes[x]);

            }
        } else {
            // element node --> search in its children nodes
            highlightText(what, nodeList[x]);
        }
    }
}

Now, to search a string within the page, you can simply do:

highlightText("ar");

(No second parameter needed as before)

But if you pass an element as a second parameter to the function, then the search will be performed exclusively within the specified element and not in the whole page:

highlightText("ar", document.getElementById("highlight_only_this"));

You can see a demo working on this JSFiddle: http://jsfiddle.net/tkm5696w/2/

Upvotes: 2

SK.
SK.

Reputation: 4358

Probably you can use innerText instead of innerHTML. You can use element.textContent.

Differences between innerText and textContent can be in the below link.

MDN - textContent

Internet Explorer introduced element.innerText. The intention is similar but with the following differences:

  1. While textContent gets the content of all elements, including and elements, the IE-specific property innerText does not.
  2. innerText is aware of style and will not return the text of hidden elements, whereas textContent will.
  3. As innerText is aware of CSS styling, it will trigger a reflow, whereas textContent will not.

Upvotes: 1

Dejan Biljecki
Dejan Biljecki

Reputation: 605

innerHtml will search for both text and elements inside given element. Use innerText or textContent to only search for text (I understand that is what you want)

Upvotes: 0

Related Questions