首页 > 解决方案 > 根据字典值动态突出显示单词

问题描述

我从之前的一篇文章中获得了一些帮助,我想在用户输入文本时动态突出显示单词。

如果前面的条件以“t”开头,则会突出显示一个单词,现在我想更新这个条件以突出显示满足基于字典值的条件的任何单词(我将 JavaScript 的内置对象称为字典)。

例如,如果我有一本字典,我dict = {"test": 5.0, "check": 4.0, "stop": -1.5, "fair": 2.0}将如何让脚本突出显示值大于 2.0 的单词?

失败的代码:

dict = {"test": 5.0, "check": 4.0, "stop": -1.5, "fair": 2.0}

function highlighter(ev) {
  // Get current cursor position
  const currpos = getSelectionDirection(ev) !== 'forward' ? getSelectionStart(ev) : getSelectionEnd(ev);
  // Change innerHTML to innerText, you
  // dont need to parse HTML code here
  var content = ev.innerText;
  var tokens = content.split(" ");
  
  for (var i = 0; i < tokens.length; i++) {
    if (dict[tokens[i][0]] > 2.0) {
      tokens[i] = "<mark style='background-color:red; color:white;'>" + tokens[i] + "</mark>";
    } 
  }
  ev.innerHTML = tokens.join(" ");
  // Set cursor on its proper position
  setSelectionRange(ev, currpos, currpos);
}
/* NOT REQUIRED AT ALL, JUST TO MAKE INTERACTION MORE PLEASANT */
.container {
  outline: none;
  border: 3px solid black;
  height: 100px;
  width: 400px;
}
<div class="container" onkeypress=highlighter(this) contenteditable>
</div>

<script>
// Usage:
// var x = document.querySelector('[contenteditable]');
// var caretPosition = getSelectionDirection(x) !== 'forward' ? getSelectionStart(x) : getSelectionEnd(x);
// setSelectionRange(x, caretPosition + 1, caretPosition + 1);
// var value = getValue(x);

// it will not work with "<img /><img />" and, perhaps, in many other cases.

  function isAfter(container, offset, node) {
    var c = node;
    while (c.parentNode != container) {
      c = c.parentNode;
    }
    var i = offset;
    while (c != null && i > 0) {
      c = c.previousSibling;
      i -= 1;
    }
    return i > 0;
  }
  function compareCaretPositons(node1, offset1, node2, offset2) {
    if (node1 === node2) {
      return offset1 - offset2;
    }
    var c = node1.compareDocumentPosition(node2);
    if ((c & Node.DOCUMENT_POSITION_CONTAINED_BY) !== 0) {
      return isAfter(node1, offset1, node2) ? +1 : -1;
    } else if ((c & Node.DOCUMENT_POSITION_CONTAINS) !== 0) {
      return isAfter(node2, offset2, node1) ? -1 : +1;
    } else if ((c & Node.DOCUMENT_POSITION_FOLLOWING) !== 0) {
      return -1;
    } else if ((c & Node.DOCUMENT_POSITION_PRECEDING) !== 0) {
      return +1;
    }
  }

  function stringifyElementStart(node, isLineStart) {
    if (node.tagName.toLowerCase() === 'br') {
      if (true) {
        return '\n';
      }
    }
    if (node.tagName.toLowerCase() === 'div') { // Is a block-level element?
      if (!isLineStart) { //TODO: Is not at start of a line?
        return '\n';
      }
    }
    return '';
  }
  function* positions(node, isLineStart = true) {
    console.assert(node.nodeType === Node.ELEMENT_NODE);
    var child = node.firstChild;
    var offset = 0;
    yield {node: node, offset: offset, text: stringifyElementStart(node, isLineStart)};
    while (child != null) {
      if (child.nodeType === Node.TEXT_NODE) {
        yield {node: child, offset: 0/0, text: child.data};
        isLineStart = false;
      } else {
        isLineStart = yield* positions(child, isLineStart);
      }
      child = child.nextSibling;
      offset += 1;
      yield {node: node, offset: offset, text: ''};
    }
    return isLineStart;
  }
  function getCaretPosition(contenteditable, textPosition) {
    var textOffset = 0;
    var lastNode = null;
    var lastOffset = 0;
    for (var p of positions(contenteditable)) {
      if (p.text.length > textPosition - textOffset) {
        return {node: p.node, offset: p.node.nodeType === Node.TEXT_NODE ? textPosition - textOffset : p.offset};
      }
      textOffset += p.text.length;
      lastNode = p.node;
      lastOffset = p.node.nodeType === Node.TEXT_NODE ? p.text.length : p.offset;
    }
    return {node: lastNode, offset: lastOffset};
  }
  function getTextOffset(contenteditable, selectionNode, selectionOffset) {
    var textOffset = 0;
    for (var p of positions(contenteditable)) {
      if (selectionNode.nodeType !== Node.TEXT_NODE && selectionNode === p.node && selectionOffset === p.offset) {
        return textOffset;
      }
      if (selectionNode.nodeType === Node.TEXT_NODE && selectionNode === p.node) {
        return textOffset + selectionOffset;
      }
      textOffset += p.text.length;
    }
    return compareCaretPositons(selectionNode, selectionOffset, contenteditable, 0) < 0 ? 0 : textOffset;
  }
  function getValue(contenteditable) {
    var value = '';
    for (var p of positions(contenteditable)) {
      value += p.text;
    }
    return value;
  }
  function setSelectionRange(contenteditable, start, end) {
    var selection = window.getSelection();
    var s = getCaretPosition(contenteditable, start);
    var e = getCaretPosition(contenteditable, end);
    selection.setBaseAndExtent(s.node, s.offset, e.node, e.offset);
  }
  //TODO: Ctrl+A - rangeCount is 2
  function getSelectionDirection(contenteditable) {
    var selection = window.getSelection();
    var c = compareCaretPositons(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset);
    return c < 0 ? 'forward' : 'none';
  }
  function getSelectionStart(contenteditable) {
    var selection = window.getSelection();
    var c = compareCaretPositons(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset);
    return c < 0 ? getTextOffset(contenteditable, selection.anchorNode, selection.anchorOffset) : getTextOffset(contenteditable, selection.focusNode, selection.focusOffset);
  }
  function getSelectionEnd(contenteditable) {
    var selection = window.getSelection();
    var c = compareCaretPositons(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset);
    return c < 0 ? getTextOffset(contenteditable, selection.focusNode, selection.focusOffset) : getTextOffset(contenteditable, selection.anchorNode, selection.anchorOffset);
  }
</script>

标签: javascripthtml

解决方案


检查下面的代码,我使用了这个 Cool js lib jQuery highlightTextarea,根据您的要求,我循环遍历您的dict对象并仅推送那些值大于 2.0 的单词。

dict = {"test": 5.0, "check": 4.0, "stop": -1.5, "fair": 2.0}

var words = [];

Object.keys(dict).forEach(function(key) { 
  if( dict[key] > 2 ){
    words.push(key);
  }
});

$('textarea').highlightTextarea({
  words: words,
  caseSensitive: false
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="http://garysieling.github.io/jquery-highlighttextarea/dist/jquery-ui/themes/smoothness/jquery-ui.min.css">
<link rel="stylesheet" href="http://garysieling.github.io/jquery-highlighttextarea/dist/jquery-highlighttextarea/jquery.highlighttextarea.min.css">
<script src="http://garysieling.github.io/jquery-highlighttextarea/dist/jquery-ui/ui/minified/jquery-ui.min.js"></script>
<script src="http://garysieling.github.io/jquery-highlighttextarea/dist/jquery-highlighttextarea/jquery.highlighttextarea.min.js"></script>

<textarea id="demo-case" cols="50" rows="3" style="background: none;" spellcheck="true">This is a test you can check or stop it will be fair enough.</textarea>

更多选项在这里http://garysieling.github.io/jquery-highlighttextarea/#options

此处的其他示例http://garysieling.github.io/jquery-highlighttextarea/examples.html


推荐阅读