5

我有一些 HTML 文件,我无法控制这些文件。因此我不能改变它们的结构或标记。

对于这些 HTML 文件中的每一个,将根据另一种算法找到一个单词列表。这些词应在 HTML 文本中突出显示。例如,如果 HTML 标记是:

<p>
Monkeys are going to die soon, if we don't stop killing them. 
So, we have to try hard to persuade hunters not to hunt monkeys. 
Monkeys are very intelligent, and they should survive. 
In fact, they deserve to survive.
</p>

单词列表是:

are, we, monkey

结果应该是这样的:

<p>
    <span class='highlight'>Monkeys</span> 
    <span class='highlight'>are</span> 
going to die soon, if 
    <span class='highlight'>we</span> 
don't stop killing them. 
So, 
    <span class='highlight'>we</span> 
have to try hard to persuade hunters 
not to hunt 
    <span class='highlight'>monkeys</span>
. They 
    <span class='highlight'>are</span> 
very intelligent, and they should survive. 
In fact, they deserve to survive.
</p>

突出显示算法应该:

  1. 不区分大小写
  2. 用 JavaScript 编写(这发生在浏览器内部)(欢迎使用 jQuery)
  3. 速度快(适用于近 800 页给定书籍的文本)
  4. 没有显示浏览器著名的“停止脚本”对话框
  5. 适用于脏 HTML 文件(例如支持无效的 HTML 标记,例如未封闭的)

    元素)(其中一些文件是 MS Word 的 HTML 导出文件,我想你明白我所说的脏的意思!!!)

  6. 应该保留原始的 HTML 标记(没有标记删除,没有标记更改,除了将预期的单词包装在元素内,没有嵌套更改。HTML 应该在编辑前后看起来相同,除了突出显示的单词)

到目前为止我所做的:

  1. 我在数组中获取 JavaScript 中的单词列表,例如["are", "we", "monkey"]
  2. 我尝试在浏览器中选择文本节点(现在有问题)
  3. 我遍历每个文本节点,对于每个文本节点,我遍历列表中的每个单词并尝试找到它并将其包装在一个元素中

请注意,您可以在这里在线观看(用户名:demo@phis.ir,密码:demo)。当前脚本也可以在页面源代码的末尾看到。

4

5 回答 5

4

将您的单词连接|成一个字符串,然后将该字符串解释为一个正则表达式,然后用高亮标签包围的完整匹配替换出现。

于 2012-11-05T12:42:23.883 回答
2

以下正则表达式适用于您的示例。也许你可以从那里拿起它:

"Monkeys are going to die soon, if we don't stop killing them. So, we have to try hard to persuade hunters not to hunt monkeys. Monkeys are very intelligent, and they should survive. In fact, they deserve to survive.".replace(/({we|are|monkey[s]?}*)([\s\.,])/gi, "<span class='highlight'>$1</span>$2")
于 2012-11-05T12:30:34.747 回答
2

我发现给定的问题非常有趣。这是我想出的:

  • 使用一些插件(或自己编写一个),以便我们能够在元素进入视图时得到通知
  • 解析该元素 text-nodes 并使用从单词本身派生的 unqiue css-class 名称将每个单词包装到 span 元素中
  • 添加为这些 un​​qiue 类名添加 css-rules 的功能

示例:http: //jsbin.com/welcome/44285/


该代码非常 hacky,并且仅在最新的 Chrome 中测试,但它对我有用,并且肯定可以在此基础上构建。

/**
 * Highlighter factory
 *
 * @return Object
 */
function highlighter() {
  var
    me = {},
    cssClassNames = {},
    cssClassNamesCount = 0,
    lastAddedRuleIndex,
    cbCount = 0,
    sheet;

  // add a stylesheet if none present
  if (document.styleSheets.length === 0) {
    sheet = document.createElement('style');
    $('head').append(sheet);
  }

  // get reference to the last stylesheet
  sheet = document.styleSheets.item(document.styleSheets.length - 1);

  /**
   * Returns a constant but unique css class name for the given word
   * 
   * @param String word
   * @return String
   */
  function getClassNameForWord(word) {
    var word = word.toLowerCase();
    return cssClassNames[word] = cssClassNames[word] || 'highlight-' + (cssClassNamesCount += 1);
  }

  /**
   * Highlights the given list of words by adding a new css rule to the list of active
   * css rules
   * 
   * @param Array words
   * @param String cssText
   * @return void
   */
  function highlight(words, cssText) {
    var
      i = 0,
      lim = words.length,
      classNames = [];

    // get the needed class names
    for (; i < lim; i += 1) {
      classNames.push('.' + getClassNameForWord(words[i]));
    }

    // remove the previous added rule
    if (lastAddedRuleIndex !== undefined) {
      sheet.deleteRule(lastAddedRuleIndex);
    }

    lastAddedRuleIndex = sheet.insertRule(classNames.join(', ') + ' { ' + cssText + ' }', sheet.cssRules.length);
  }

  /**
   * Calls the given function for each text node under the given parent element
   *
   * @param DomElement parentElement
   * @param Function onLoad
   * @param Function cb
   * @return void
   */
  function forEachTextNode(parentElement, onLoad, cb) {
    var i = parentElement.childNodes.length - 1, childNode;
    for (; i > -1; i -= 1) {
      childNode = parentElement.childNodes[i];
      if (childNode.nodeType === 3) {
        cbCount += 1;

        setTimeout(function (node) {
          return function () {
            cb(node);
            cbCount -= 1;
            if (cbCount === 0 && typeof onLoad === 'Function') {
              onLoad(me);
            }
          };
        }(childNode), 0);

      } else if (childNode.nodeType === 1) {
        forEachTextNode(childNode, cb);
      }
    }
  }

  /**
   * replace each text node by span elements wrapping each word
   *
   * @param DomElement contextNode
   * @param onLoad the parent element
   * @return void
   */
  function add(contextNode, onLoad) {
    forEachTextNode(contextNode, onLoad, function (textNode) {
      var
        doc = textNode.ownerDocument,
        frag = doc.createDocumentFragment(),
        words = textNode.nodeValue.split(/(\W)/g),
        i = 0,
        lim = words.length,
        span;

      for (; i < lim; i += 1) {
        if (/^\s*$/m.test(words[i])) {
          frag.appendChild(doc.createTextNode(words[i]));
        } else {
          span = doc.createElement('span');
          span.setAttribute('class', getClassNameForWord(words[i]));
          span.appendChild(doc.createTextNode(words[i]));
          frag.appendChild(span);
        }
      }

      textNode.parentNode.replaceChild(frag, textNode);
    });
  }

  // set public api and return created object
  me.highlight = highlight;
  me.add = add;

  return me
}

var h = highlighter();
h.highlight(['Lorem', 'magna', 'gubergren'], 'background: yellow;');

// on ready
$(function ($) {
  // using the in-view plugin (see the full code in the link above) here, to only
  // parse elements that are actual visible
  $('#content > *').one('inview', function (evt, visible) {
    if (visible) {
      h.add(this);
    }
  });

  $(window).scroll();
});
于 2012-11-05T20:01:50.480 回答
1

你可以试试我一起破解的名为Linguigi的库

var ling = new Linguigi();

ling.eachToken(/are|we|monkey/g, true, function(text) {
    return '<span class="highlight">' + text + '</span>';
});
于 2012-11-05T12:37:52.243 回答
0

如果你使用 jQuery,试试这个。

$('* :not(:has(*))').html(function(i, v) {
  return v.replace(/searchString/g, '<span class="highlight">searchString</span>');    
});




$('* :not(:has(*))') will search for each node having no child elements and replace the html string in it with given string warapped in your HTML.

我的快速和肮脏的解决方案是基于这个博客中的灵魂:

http://wowmotty.blogspot.in/2011/05/jquery-findreplace-text-without.html

他的解决方案适用于 div 选择器并仅替换文本,我的解决方案尝试替换 innerHTML 字符串。

试试这个,告诉我可以做些什么。看起来很有趣。

于 2012-11-05T12:43:24.053 回答