最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How can I dynamically highlight strings on a web page? - Stack Overflow

programmeradmin1浏览0评论

I want to create pages with urls such as:

http://xyzcorp/schedules/2015Aug24_Aug28/Jim_Hawkins
http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones
http://xyzcorp/schedules/2015Aug24_Aug28/John_Silver

These particular URLs would all contain the exact same content (the "2015Aug24_Aug28" page), but would highlight all instances of the name tagged on to the end. For example, "http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones" would show every instance of the name "Billy Bones" highlighted, as if a "Find" for that name was executed on the page via the browser.

I imagine something like this is required, client-side:

var employee = getLastURLPortion(); // return "Billy_Bones" (or whatever)
employee = humanifyTheName(employee); // replaces underscores with spaces, so that it's "Billy Bones" (etc.)
Highlight(employee); // this I have no clue how to do

Can this be done in HTML/CSS, or is JavaScript or jQuery also required for this?

I want to create pages with urls such as:

http://xyzcorp/schedules/2015Aug24_Aug28/Jim_Hawkins
http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones
http://xyzcorp/schedules/2015Aug24_Aug28/John_Silver

These particular URLs would all contain the exact same content (the "2015Aug24_Aug28" page), but would highlight all instances of the name tagged on to the end. For example, "http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones" would show every instance of the name "Billy Bones" highlighted, as if a "Find" for that name was executed on the page via the browser.

I imagine something like this is required, client-side:

var employee = getLastURLPortion(); // return "Billy_Bones" (or whatever)
employee = humanifyTheName(employee); // replaces underscores with spaces, so that it's "Billy Bones" (etc.)
Highlight(employee); // this I have no clue how to do

Can this be done in HTML/CSS, or is JavaScript or jQuery also required for this?

Share Improve this question asked Aug 23, 2015 at 12:46 B. Clay Shannon-B. Crow RavenB. Clay Shannon-B. Crow Raven 10.3k157 gold badges502 silver badges897 bronze badges 8
  • 3 Neither is beer, but it's awfully good on a hot day. – B. Clay Shannon-B. Crow Raven Commented Aug 23, 2015 at 13:00
  • 1 Use a highlighter plugin – charlietfl Commented Aug 23, 2015 at 13:04
  • 1 I think the real question is how do the pages get rendered, and can you add something in the template for it? – Kelly J Andrews Commented Aug 23, 2015 at 13:13
  • 2 @Xufox that's why I suggested using a plugin that is already battle tested. Once anyone digs into this they realize it's not a trivial exercise to maintain html structure and not interfere with events – charlietfl Commented Aug 23, 2015 at 13:27
  • 2 It doesn't look like the OP did any research at all before asking this question. OP seems to believe it might be possible using 3 specific technologies, but doesn't bother doing a basic search each on each one (eg. "highlight text with javascript" or "highlight text with css"). You're supposed to research before you ask your question, kiddies! – cimmanon Commented Aug 23, 2015 at 15:28
 |  Show 3 more ments

3 Answers 3

Reset to default 6

If you call the function

highlight(employee);

this is what that function would look like in ECMAScript 2018+:

function highlight(employee){
  Array.from(document.querySelectorAll("body, body *:not(script):not(style):not(noscript)"))
    .flatMap(({childNodes}) => [...childNodes])
    .filter(({nodeType, textContent}) => nodeType === document.TEXT_NODE && textContent.includes(employee))
    .forEach((textNode) => textNode.replaceWith(...textNode.textContent.split(employee).flatMap((part) => [
        document.createTextNode(part),
        Object.assign(document.createElement("mark"), {
          textContent: employee
        })
      ])
      .slice(0, -1))); // The above flatMap creates a [text, employeeName, text, employeeName, text, employeeName]-pattern. We need to remove the last superfluous employeeName.
}

And this is an ECMAScript 5.1 version:

function highlight(employee){
  Array.prototype.slice.call(document.querySelectorAll("body, body *:not(script):not(style):not(noscript)")) // First, get all regular elements under the `<body>` element
    .map(function(elem){
      return Array.prototype.slice.call(elem.childNodes); // Then extract their child nodes and convert them to an array.
    })
    .reduce(function(nodesA, nodesB){
      return nodesA.concat(nodesB); // Flatten each array into a single array
    })
    .filter(function(node){
      return node.nodeType === document.TEXT_NODE && node.textContent.indexOf(employee) > -1; // Filter only text nodes that contain the employee’s name.
    })
    .forEach(function(node){
      var nextNode = node.nextSibling, // Remember the next node if it exists
        parent = node.parentNode, // Remember the parent node
        content = node.textContent, // Remember the content
        newNodes = []; // Create empty array for new highlighted content

      node.parentNode.removeChild(node); // Remove it for now.
      content.split(employee).forEach(function(part, i, arr){ // Find each occurrence of the employee’s name
        newNodes.push(document.createTextNode(part)); // Create text nodes for everything around it

        if(i < arr.length - 1){
          newNodes.push(document.createElement("mark")); // Create mark element nodes for each occurrence of the employee’s name
          newNodes[newNodes.length - 1].innerHTML = employee;
          // newNodes[newNodes.length - 1].setAttribute("class", "highlighted");
        }
      });

      newNodes.forEach(function(n){ // Append or insert everything back into place
        if(nextNode){
          parent.insertBefore(n, nextNode);
        }
        else{
          parent.appendChild(n);
        }
      });
    });
}

The major benefit of replacing individual text nodes is that event listeners don’t get lost. The site remains intact, only the text changes.

Instead of the mark element you can also use a span and unment the line with the class attribute and specify that in CSS.

This is an example where I used this function and a subsequent highlight("Text"); on the MDN page for Text nodes:

(The one occurrence that isn’t highlighted is an SVG node beyond an <iframe>).

I used the following regex to replace all the matching url to create anchors with highlighted text:

(http://xyzcorp/schedules/(.*?)/)(.*?)( |<|\n|\r|$)

Debuggex Demo

The following code will replace all plain urls. If you don't need them to be replaced to links, just highlight them, remove the tags:

var str = "http://xyzcorp/schedules/2015Aug24_Aug28/Jim_Hawkins http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones http://xyzcorp/schedules/2015Aug24_Aug28/John_Silver ";

var highlighted = str.replace( new RegExp("(http://xyzcorp/schedules/(.*?)/)(.*?)( |<|\n|\r|$)","g"), "<a href='$1$3'>$1<span style='background-color: #d0d0d0'>$3</span></a>" );

The content of the highlighted string will be:

<a href='http://xyzcorp/schedules/2015Aug24_Aug28/Jim_Hawkins'>http://xyzcorp/schedules/2015Aug24_Aug28/<span style='background-color: #d0d0d0'>Jim_Hawkins</span></a>
<a href='http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones'>http://xyzcorp/schedules/2015Aug24_Aug28/<span style='background-color: #d0d0d0'>Billy_Bones</span></a>
<a href='http://xyzcorp/schedules/2015Aug24_Aug28/John_Silver'>http://xyzcorp/schedules/2015Aug24_Aug28/<span style='background-color: #d0d0d0'>John_Silver</span></a>

UPDATE:

This function will replace the matching names from the input text:

function highlight_names( html_in )
{
    var name = location.href.split("/").pop().replace("_"," ");
    return html_in.replace( new RegExp( "("+name+")", "g"), "<span style='background-color: #d0d0d0'>$1</span>" );
}

One solution would be, after window is loaded, to traverse all nodes recursively and wrap search terms in text nodes with a highlight class. This way, original structure and event subscriptions are not preserved.

(Here, using jquery, but could be done without):

Javascript:

$(function() {
  // get term from url
  var term = window.location.href.match(/\/(\w+)\/?$/)[1].replace('_', ' ');
  // search regexp
  var re = new RegExp('(' + term + ')', 'gi');
  // recursive function
  function highlightTerm(elem) {
    var contents = $(elem).contents();
    if(contents.length > 0) {
      contents.each(function() {
        highlightTerm(this);
      });
    } else {
      // text nodes
      if(elem.nodeType === 3) {
        var $elem = $(elem);
        var text = $elem.text();
        if(re.test(text)) {
          $elem.wrap("<span/>").parent().html(text.replace(re, '<span class="highlight">$1</span>'));
        }
      }
    }
  }
  highlightTerm(document.body);
});

CSS:

.highlight {
    background-color: yellow;
}

$(function() {
  // get term from url
  //var term = window.location.href.match(/\/(\w+)\/?$/)[1].replace('_', ' ');
  var term = 'http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones/'.match(/\/(\w+)\/?$/)[1].replace('_', ' ');
  // search regexp
  var re = new RegExp('(' + term + ')', 'gi');
  // recursive function
  function highlightTerm(elem) {
    var contents = $(elem).contents();
    if(contents.length > 0) {
      contents.each(function() {
        highlightTerm(this);
      });
    } else {
      // text nodes
      if(elem.nodeType === 3) {
        var $elem = $(elem);
        var text = $elem.text();
        if(re.test(text)) {
          $elem.wrap("<span/>").parent().html(text.replace(re, '<span class="highlight">$1</span>'));
        }
      }
    }
  }
  highlightTerm(document.body);
});
.highlight  {
  background-color: yellow;
}
<script src="https://ajax.googleapis./ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <div>
      <div class="post-text" itemprop="text">
        <p>I want to create pages with urls such as:</p>
        <pre style="" class="default prettyprint prettyprinted">
          <code>
            <span class="pln">http</span>
            <span class="pun">:</span>
            <span class="">//xyzcorp/schedules/2015Aug24_Aug28/Jim_Hawkins</span>
            <span class="pln">
http</span>
            <span class="pun">:</span>
            <span class="">//xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones</span>
            <span class="pln">
http</span>
            <span class="pun">:</span>
            <span class="">//xyzcorp/schedules/2015Aug24_Aug28/John_Silver</span>
          </code>
        </pre>
        <p>These particular URLs would all contain the exact same content (the "2015Aug24_Aug28" page), but would highlight all instances of the name tagged on to the end. For example, "                    <code>http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones</code>

" would show every instance of the name "Billy Bones" highlighted, as if a "Find" for that name was executed on the page via the browser.</p>
        <p>I imagine something like this is required, client-side:</p>
        <pre style="" class="default prettyprint prettyprinted">
          <code>
            <span class="kwd">var</span>
            <span class="pln"> employee </span>
            <span class="pun">=</span>
            <span class="pln"> getLastURLPortion</span>
            <span class="pun">();</span>
            <span class="pln"></span>
            <span class="">// return "Billy_Bones" (or whatever)</span>
            <span class="pln">
employee </span>
            <span class="pun">=</span>
            <span class="pln"> humanifyTheName</span>
            <span class="pun">(</span>
            <span class="pln">employee</span>
            <span class="pun">);</span>
            <span class="pln"></span>
            <span class="">// replaces underscores with spaces, so that it's "Billy Bones" (etc.)</span>
            <span class="pln"></span>
            <span class="typ">Highlight</span>
            <span class="pun">(</span>
            <span class="pln">employee</span>
            <span class="pun">);</span>
            <span class="pln"></span>
            <span class="">// this I have no clue how to do</span>
          </code>
        </pre>
        <p>Can this be done in HTML/CSS, or is JavaScript or jQuery also required for this?</p>
      </div>

Demo: http://plnkr.co/edit/rhfqzWThLTu9ccBb1Amy?p=preview

发布评论

评论列表(0)

  1. 暂无评论