动态突出显示符合特定条件的单词
Dynamically Highlight Words That Fit a Specific Criteria
我已经发帖了,但是太静态了。现在我想进行更改,使代码是动态的。
OBJECTIVE
我希望以“t”开头的单词在用户键入时突出显示。如果单词不以“t”开头,则什么也不做。基本上,用户会有正常的打字体验,但“t”字将被突出显示。
版本详情
- 我有一个版本可以在 mousemove 上“运行”,但这对
用户。我不希望他们必须移动鼠标才能获取文本
突出显示。
- 我有一个按键“有效”的版本,但问题是
光标始终 returns 到初始位置(这会导致文本反向输入)并且一旦突出显示开始,它就不会停止。
版本 1:事件 = onmousemove
//highlight ANY word that starts with t
function highlighter(ev) {
var content = ev.innerHTML;
var tokens = content.split(" ");
for (var i = 0; i < tokens.length; i++) {
if (tokens[i][0] == 't') {
tokens[i] = "<mark style='background-color:red; color:white;'>" + tokens[i] + "</mark>";
}
}
ev.innerHTML = tokens.join(" ");
}
/* NOT REQUIRED AT ALL, JUST TO MAKE INTERACTION MORE PLEASANT */
.container {
outline: none;
border: 3px solid black;
height: 100px;
width: 400px;
}
<div class="container" onmousemove=highlighter(this) contenteditable>
</div>
版本 2:事件 = onkeypress
//highlight ANY word that starts with t
function highlighter(ev) {
var content = ev.innerHTML;
var tokens = content.split(" ");
for (var i = 0; i < tokens.length; i++) {
if (tokens[i][0] == 't') {
tokens[i] = "<mark style='background-color:red; color:white;'>" + tokens[i] + "</mark>";
}
}
ev.innerHTML = tokens.join(" ");
}
/* 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>
这里是草稿示例。我使用了来自 this gist 的插入符号 get/set 片段。基本上想法很简单 - 获取插入位置,进行修改,将其设置回去。还将您的 innerHTML
方法交换为 innerText
,因为您不需要在您的 t-finder 逻辑中解析 HTML 代码。
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 (tokens[i][0] == 't') {
tokens[i] = "<mark style='background-color:red; color:white;'>" + tokens[i] + "</mark>";
}
}
ev.innerHTML = tokens.join(" ");
// Set cursor on it's 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>
我已经发帖了
OBJECTIVE
我希望以“t”开头的单词在用户键入时突出显示。如果单词不以“t”开头,则什么也不做。基本上,用户会有正常的打字体验,但“t”字将被突出显示。
版本详情
- 我有一个版本可以在 mousemove 上“运行”,但这对 用户。我不希望他们必须移动鼠标才能获取文本 突出显示。
- 我有一个按键“有效”的版本,但问题是 光标始终 returns 到初始位置(这会导致文本反向输入)并且一旦突出显示开始,它就不会停止。
版本 1:事件 = onmousemove
//highlight ANY word that starts with t
function highlighter(ev) {
var content = ev.innerHTML;
var tokens = content.split(" ");
for (var i = 0; i < tokens.length; i++) {
if (tokens[i][0] == 't') {
tokens[i] = "<mark style='background-color:red; color:white;'>" + tokens[i] + "</mark>";
}
}
ev.innerHTML = tokens.join(" ");
}
/* NOT REQUIRED AT ALL, JUST TO MAKE INTERACTION MORE PLEASANT */
.container {
outline: none;
border: 3px solid black;
height: 100px;
width: 400px;
}
<div class="container" onmousemove=highlighter(this) contenteditable>
</div>
版本 2:事件 = onkeypress
//highlight ANY word that starts with t
function highlighter(ev) {
var content = ev.innerHTML;
var tokens = content.split(" ");
for (var i = 0; i < tokens.length; i++) {
if (tokens[i][0] == 't') {
tokens[i] = "<mark style='background-color:red; color:white;'>" + tokens[i] + "</mark>";
}
}
ev.innerHTML = tokens.join(" ");
}
/* 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>
这里是草稿示例。我使用了来自 this gist 的插入符号 get/set 片段。基本上想法很简单 - 获取插入位置,进行修改,将其设置回去。还将您的 innerHTML
方法交换为 innerText
,因为您不需要在您的 t-finder 逻辑中解析 HTML 代码。
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 (tokens[i][0] == 't') {
tokens[i] = "<mark style='background-color:red; color:white;'>" + tokens[i] + "</mark>";
}
}
ev.innerHTML = tokens.join(" ");
// Set cursor on it's 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>