【问题标题】:How to make the element textarea automatically color substrings?如何使元素 textarea 自动为子字符串着色?
【发布时间】:2017-02-03 14:13:47
【问题描述】:

拥有<textarea> 的最佳方式是什么,您可以在其中写字,它会自动将red 的单词涂成红色?

即使它没有被空格包围,它也应该着色。
因此,例如,它应该仍然适用于文本 I ate tha'red-meat 和单词 reddit

This 是查找包含red 的单词的理想场所。

我有以下代码:

body {
  margin: 0;
}
textarea {
  background-color: GhostWhite;
  border: 0;
  height: 100%;
  outline: none;
  resize: none;
  width: 100%;
}
<textarea spellcheck="false"></textarea>

【问题讨论】:

  • 你试过css规则吗:color: red;?
  • @Shilly - 我不想把所有东西都涂成红色,只有子字符串/单词 red
  • 不是一件容易的事:) ...因为你想从reddit得到red
  • AFAIK,你不能自定义textarea里面的文本,但是你可以用contenteditable=true模拟一个带有div的textarea,然后你自定义里面的文本
  • @user7393973 : textarea 不可能

标签: javascript html css


【解决方案1】:

你不能用textarea来做,而是使用contenteditable跨度(建议span以避免输入<时出现问题)并使用跨度包装颜色子字符串。

var div = document.getElementById('div');

div.addEventListener('input', function() {
    // get the current position
    var pos = getCaretCharacterOffsetWithin(this);
  // get all word in the content  
  this.innerHTML = this.innerText.replace(/\w+/g, function(m) {
    // create a temporary span element
    var temp = document.createElement('span');
    // set current word as color
    temp.style.color = m;
    // check color is valid by rechecking the proeprty
    if (temp.style.color) {
      // if valid color then replace with temp elements html after setting content
      temp.innerHTML = m;
      return temp.outerHTML;
    }
    // else return the word itself
    return m;
  })
  // set caret position
  setCaretPosition(this, pos);
})



// following code is copied from following question
// https://stackoverflow.com/questions/26139475/restore-cursor-position-after-changing-contenteditable

function getCaretCharacterOffsetWithin(element) {
  var caretOffset = 0;
  var doc = element.ownerDocument || element.document;
  var win = doc.defaultView || doc.parentWindow;
  var sel;
  if (typeof win.getSelection != "undefined") {
    sel = win.getSelection();
    if (sel.rangeCount > 0) {
      var range = win.getSelection().getRangeAt(0);
      var preCaretRange = range.cloneRange();
      preCaretRange.selectNodeContents(element);
      preCaretRange.setEnd(range.endContainer, range.endOffset);
      caretOffset = preCaretRange.toString().length;
    }
  } else if ((sel = doc.selection) && sel.type != "Control") {
    var textRange = sel.createRange();
    var preCaretTextRange = doc.body.createTextRange();
    preCaretTextRange.moveToElementText(element);
    preCaretTextRange.setEndPoint("EndToEnd", textRange);
    caretOffset = preCaretTextRange.text.length;
  }
  return caretOffset;
}

function setCaretPosition(element, offset) {
  var range = document.createRange();
  var sel = window.getSelection();

  //select appropriate node
  var currentNode = null;
  var previousNode = null;

  for (var i = 0; i < element.childNodes.length; i++) {
    //save previous node
    previousNode = currentNode;

    //get current node
    currentNode = element.childNodes[i];
    //if we get span or something else then we should get child node
    while (currentNode.childNodes.length > 0) {
      currentNode = currentNode.childNodes[0];
    }

    //calc offset in current node
    if (previousNode != null) {
      offset -= previousNode.length;
    }
    //check whether current node has enough length
    if (offset <= currentNode.length) {
      break;
    }
  }
  //move caret to specified offset
  if (currentNode != null) {
    range.setStart(currentNode, offset);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
  }
}
&lt;span contenteditable="true" id="div"&gt;sss&lt;/span&gt;

注意:位置保留代码复制自:Restore cursor position after changing contenteditable


更新:如果您只想在任何单词中替换 red,那么您可以这样做:

var div = document.getElementById('div');

div.addEventListener('input', function() {
  var pos = getCaretCharacterOffsetWithin(this);
  // get all red subtring and wrap it with span
  this.innerHTML = this.innerText.replace(/red/g, '<span style="color:red">$&</span>')
  setCaretPosition(this, pos);
})



// following code is copied from following question
// https://stackoverflow.com/questions/26139475/restore-cursor-position-after-changing-contenteditable

function getCaretCharacterOffsetWithin(element) {
  var caretOffset = 0;
  var doc = element.ownerDocument || element.document;
  var win = doc.defaultView || doc.parentWindow;
  var sel;
  if (typeof win.getSelection != "undefined") {
    sel = win.getSelection();
    if (sel.rangeCount > 0) {
      var range = win.getSelection().getRangeAt(0);
      var preCaretRange = range.cloneRange();
      preCaretRange.selectNodeContents(element);
      preCaretRange.setEnd(range.endContainer, range.endOffset);
      caretOffset = preCaretRange.toString().length;
    }
  } else if ((sel = doc.selection) && sel.type != "Control") {
    var textRange = sel.createRange();
    var preCaretTextRange = doc.body.createTextRange();
    preCaretTextRange.moveToElementText(element);
    preCaretTextRange.setEndPoint("EndToEnd", textRange);
    caretOffset = preCaretTextRange.text.length;
  }
  return caretOffset;
}

function setCaretPosition(element, offset) {
  var range = document.createRange();
  var sel = window.getSelection();

  //select appropriate node
  var currentNode = null;
  var previousNode = null;

  for (var i = 0; i < element.childNodes.length; i++) {
    //save previous node
    previousNode = currentNode;

    //get current node
    currentNode = element.childNodes[i];
    //if we get span or something else then we should get child node
    while (currentNode.childNodes.length > 0) {
      currentNode = currentNode.childNodes[0];
    }

    //calc offset in current node
    if (previousNode != null) {
      offset -= previousNode.length;
    }
    //check whether current node has enough length
    if (offset <= currentNode.length) {
      break;
    }
  }
  //move caret to specified offset
  if (currentNode != null) {
    range.setStart(currentNode, offset);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
  }
}
&lt;span contenteditable="true" id="div" style="width:100%;display:block"&gt;sss&lt;/span&gt;

注意:此解决方案存在与换行相关的一些问题,因为内容是 HTML,并且换行没有任何意义。

【讨论】:

  • 这是一个好的开始,但似乎有一些问题。当red 没有被空格包围并且它不断改变插入符号的位置时,它不起作用。
  • 它会导致与其他一些答案相同的错误。在该代码 sn-p 中,red 必须被空格包围才能工作,并且在写入或选择时会不断改变插入符号的位置(使用键盘上的箭头键和 shift 进行选择)。
  • @user7393973 :但是从字符串中获取颜色对于 ex 来说真的很难:reddit ....
  • 我认为这是目前我想要的最接近的答案。不过,你能不能让它颜色red两边没有被空格包围?
  • 现在我可以看到了。不知道人们如何构建包含多个关键字的代码编辑器。这很难!
【解决方案2】:

因此,您可以使用 contenteditable div,匹配前面没有 &gt; 的任何红色,并将红色替换为:"$1&lt;span class='specialColor'&gt;red&lt;/span&gt;&amp;zwnj;" 末尾的零宽度非连接字符用作跨度的分隔符。

document.getElementById('foo').addEventListener('input', function(e) {
  var re = /([^\>])red/;
  var split = this.innerHTML.replace(re, "$1<span class='specialColor'>red</span>&zwnj;");
  this.innerHTML = split;
  placeCaretAtEnd(this);
})

function placeCaretAtEnd(el) {
  el.focus();
  if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") {
    var range = document.createRange();
    range.selectNodeContents(el);
    range.collapse(false);
    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
  } else if (typeof document.body.createTextRange != "undefined") {
    var textRange = document.body.createTextRange();
    textRange.moveToElementText(el);
    textRange.collapse(false);
    textRange.select();
  }
}
body {
  margin: 0;
}
textarea {
  background-color: GhostWhite;
  border: 0;
  height: 100%;
  outline: none;
  resize: none;
  width: 100%;
}

.specialColor {
  color:red;
}
&lt;div id="foo" contenteditable spellcheck="false"&gt;&lt;/div&gt;

免责声明:

placeCaretAtEnd() 方法取自这个 SO 答案:contenteditable, set caret at the end of the text (cross-browser)

【讨论】:

  • 这很接近,但在某些时候,它只会使所有文本变成红色。还会不断更改插入符号在 div 中的位置,这几乎会阻止写入(您可以用鼠标将其更改回来)。如果您一开始只输入red,它不会为它着色。
  • 你使用的是哪个浏览器@user7393973
  • 你所描述的不会在这里发生...@user7393973,使用 chrome latest
  • 我使用的是谷歌浏览器。
  • 你能给我一个你正在写的例子吗? @user7393973
【解决方案3】:

您不能在inputtextarea 等可编辑输入区域内向文本添加部分样式。它被视为单个文本块,只能作为一个整体进行样式设置。

但是,可以通过巧妙定位样式化的span 元素来模拟所需的效果。

有一个这样的项目可以提供即时文本突出显示。看看他们:Code Mirror Project

我在我的一个个人项目中使用了 codemirror。你也应该看看它。 See the editable pane on the left

【讨论】:

  • 我目前无法检查该外部链接,因为我的互联网连接不允许,因为它有安全限制。您能否努力为我提供一个工作示例?我真的很感激!
  • @user7393973 这里有一个工作示例coderpad-clone.herokuapp.com/ad/vish
  • 我知道有多个工作代码编辑器可以解决问题。我要问的是最简单的代码来做到这一点,仅此而已。
【解决方案4】:

我唯一不满意的是它如何处理换行符和制表符。 它比只替换红色稍微复杂一点,但这样你可以用任何颜色突出显示任何单词,甚至可能在同一个元素上混合其中的几个,所以使用的函数是可重用的。

<html>
<head>
    <style>
    #myTextarea {
        border: 1px solid black;
        border-radius: 5px;
        min-height: 50px;
        width: 50%;
    }
    .hl-red {
        color: red;
    }
    </style>
</head>
<body>
<div id="myTextarea" contenteditable>Test string</div>
<script>
var createHighlighter = function createHighlighter( word ) {
    // Let's create a reusable function that can highlight any word.
    // You call this function providing the word you want highlighted.
    // Then this returns a function that will insert some html spans around each instance of the word.
    // You can the call the returned function with any string to get the string saturated with the span tags.
    return function highlight( str ) {
        // We split on the word we want to highlight, so our array contains all the non-highlighted chunks.
        // Since we can call the function multiple times in a row, we will usually want to remove the previous highlights, so we don't double-highlight it.
        // This can be easily done with a simple global replace regex.
        var chunks;
        if (str === '<br>') return '';
        else {
            chunks = str.replace(/\<span\sclass="hl-red"\>/g, '').replace(/\<\/span\>/g, '').split(word);
            // Then we add the non-highlighted text + the <span> + the highlighted word.
            return chunks.reduce(function( html, chunk, index ) {
                html += chunk;
                // Add the highlighted word, except for the last chunk, since that's just the end of the string.
                if ((index + 1) !== chunks.length) html += '<span class="hl-red">' + word + '</span>';
                return html;
            }, '');
        }
    };
};

var wordToHighlight = 'red';

var highlightRed = createHighlighter( wordToHighlight );

var highlightArea = function highlightArea( event ) {
    event.target.innerHTML = highlightRed( event.target.innerHTML );
};

document.querySelector('#myTextarea').addEventListener('keyup', highlightArea);

</script>
</body>
</html>

【讨论】:

  • 是否可以让它不改变插入符号的位置?
  • 这比突出显示更简单,所以我会参考 baao 关于插入符号的回答。
  • baao 的回答目前不起作用。如果你写red,然后删除中间的erd 将保持红色。
  • 如果你结合所有你得到的答案中最好的部分,你就会让它充分发挥作用。我更喜欢我的突出显示版本,因为它允许任何不是&lt;/span&gt;&lt;span class="hl-red"&gt; 的字符序列,并且它不会遇到突然突出显示的单词的退格问题。但是我无法让我的插入符号位置版本起作用,所以我要放弃了。是一个很好的分心。 :)
  • 你给了它一个很好的尝试,我必须承认这一点。这引起了很多关注和竞争,我会接受谁让它充分发挥作用的答案,但现在,我认为大多数人都放弃了,因为它太复杂了。我会看看我能做什么......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-02
  • 1970-01-01
  • 1970-01-01
  • 2011-03-02
  • 2014-11-27
  • 1970-01-01
相关资源
最近更新 更多