如何动态突出显示网页上的字符串?

时间:2015-08-23 12:46:50

标签: javascript jquery html css meteor

我想创建包含以下网址的网页:

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

这些特定的网址都包含完全相同的内容(“2015Aug24_Aug28”页面),但会突出显示标记在最后的所有名称实例。例如,“http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones”会突出显示名称“Billy Bones”的每个实例,就像通过浏览器在页面上执行该名称的“查找”一样。

我想客户端需要这样的东西:

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

这可以在HTML / CSS中完成,还是需要JavaScript或jQuery?

3 个答案:

答案 0 :(得分:4)

我使用以下正则表达式替换所有匹配的url以使用突出显示的文本创建锚点:

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

Regular expression visualization

Debuggex Demo

以下代码将替换所有普通网址。如果您不需要将它们替换为链接,只需突出显示它们,删除标记:

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>" );

突出显示的字符串的内容为:

<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>

<强>更新

此函数将替换输入文本中的匹配名称:

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>" );
}

答案 1 :(得分:2)

在加载窗口之后,一种解决方案是递归遍历所有节点并使用突出显示类在文本节点中包装搜索项。这样,不会保留原始结构和事件订阅。

(这里,使用jquery,但可以不用):

使用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.com/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="com">//xyzcorp/schedules/2015Aug24_Aug28/Jim_Hawkins</span>
            <span class="pln">
http</span>
            <span class="pun">:</span>
            <span class="com">//xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones</span>
            <span class="pln">
http</span>
            <span class="pun">:</span>
            <span class="com">//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="com">// 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="com">// 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="com">// 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>

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

答案 2 :(得分:2)

如果您调用该功能

highlight(employee);

这就是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.
}

这是一个ECMAScript 5.1版本:

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);
        }
      });
    });
}

替换单个文本节点的主要好处是事件侦听器不会丢失。该网站保持不变,只有文本更改。

您可以使用mark代替span元素,并使用class属性取消注释该行,并在CSS中指定该行。

这是我在MDN page for Text nodes上使用此功能和后续highlight("Text");的示例:

A website with all occurrences of “Text” highlighted

(未突出显示的一个事件是超出<iframe>的SVG节点。)