如果突出显示某个单词,并且用户单击了连接单词,请突出显示这两个单词
If a word is highlighted and user clicks the connecting word, highlight both
我最近发布了一个问题,询问一种通过以下方式突出显示更聪明的单词的方法:
-
单击突出显示整个单词(默认行为为双击(。
-
单击拖动将仅突出显示完整的单词/术语。
美丽的解决方案由阿尔曼发布。
用于测试的 jsFiddle。
我提出这个问题的目的是允许用户单击两个或多个连接词并突出显示它们(扩展突出显示的范围(。
来演示。如果光标选择了world,
:
你好
world,
洛伦伊普苏姆攻击泰坦。
并且用户点击lorem
,它应该像这样选择两个词:
你好
world, lorem
ipsum攻击泰坦。
如果用户单击Hello
。
因此,它仅在单词连接时扩展突出显示。例如,如果选择了worlds,
,并且用户单击ipsum
,则只需选择ipsum
。
扩大高光覆盖面的方法是什么?
jsFiddle 中的代码是:
jQuery(document).ready(function(e){
(function(els){
for(var i=0;i<els.length;i++){
var el = els[i];
el.addEventListener('mouseup',function(evt){
if (document.createRange) { // Works on all browsers, including IE 9+
var selected = window.getSelection();
/* if(selected.toString().length){ */
var d = document,
nA = selected.anchorNode,
oA = selected.anchorOffset,
nF = selected.focusNode,
oF = selected.focusOffset,
range = d.createRange();
range.setStart(nA,oA);
range.setEnd(nF,oF);
// Check if direction of selection is right to left
if(range.startContainer !== nA || (nA === nF && oF < oA)){
range.setStart(nF,oF);
range.setEnd(nA,oA);
}
// Extend range to the next space or end of node
while(range.endOffset < range.endContainer.textContent.length && !/'s$/.test(range.toString())){
range.setEnd(range.endContainer, range.endOffset + 1);
}
// Extend range to the previous space or start of node
while(range.startOffset > 0 && !/^'s/.test(range.toString())){
range.setStart(range.startContainer, range.startOffset - 1);
}
// Remove spaces
if(/'s$/.test(range.toString()) && range.endOffset > 0)
range.setEnd(range.endContainer, range.endOffset - 1);
if(/^'s/.test(range.toString()))
range.setStart(range.startContainer, range.startOffset + 1);
// Assign range to selection
selected.addRange(range);
el.style.MozUserSelect = '-moz-none';
/* } */
} else {
// Fallback for Internet Explorer 8 and earlier
// (if you think it still is worth the effort of course)
}
});
/* This part is necessary to eliminate a FF specific dragging behavior */
el.addEventListener('mousedown',function(){
if (window.getSelection) { // Works on all browsers, including IE 9+
var selection = window.getSelection ();
selection.collapse (selection.anchorNode, selection.anchorOffset);
} else {
// Fallback for Internet Explorer 8 and earlier
// (if you think it still is worth the effort of course)
}
el.style.MozUserSelect = 'text';
});
}
})(document.getElementsByClassName('taggable'));
});
.HTML:
<p class="taggable">
Hello world, lorem ipsum attack on titan.
</p>
<p>
JS doesn't affect this text.
</p>
赏金信息
奖励现有的答案,因为它非常有用。无需发布更多解决方案,因为此解决方案已尽最大。
升级
好的,我把它放在顶部,因为它是一个重大更新,我相信甚至可以被认为是对先前功能的升级。
请求是使前一个函数反向工作,即当再次单击突出显示的单词时,它将从总选择中删除。
挑战在于,当单击段落内<p>
和</p>
标签边缘或<b>
和</b>
标签边缘突出显示的单词时,必须将范围的startContainer
或endContainer
带入或移出它们所在的当前元素,并且还必须重置startOffset
或endOffset
。我不确定这是否清楚地表达了这个问题,但简而言之,由于Range
对象的工作方式,最接近 HTML 标签的单词被证明是一个相当大的挑战。
解决方案是引入一些新的正则表达式测试、几个if
检查和一个用于查找下一个/上一个兄弟姐妹的本地函数。在这个过程中,我还修复了一些以前没有引起我注意的事情。新功能在下面,更新的小提琴在这里。
(function(el){
// variable declaration for previous range info
// and function for finding the sibling
var prevRangeInfo = {},
findSibling = function(thisNode, direction){
// get the child node list of the parent node
var childNodeList = thisNode.parentNode.childNodes,
children = [];
// convert the child node list to an array
for(var i=0, l=childNodeList.length; i<l; i++) children.push(childNodeList[i]);
return children[children.indexOf(thisNode) + direction];
};
el.addEventListener('mouseup',function(evt){
if (document.createRange) { // Works on all browsers, including IE 9+
var selected = window.getSelection();
// Removing the following line from comments will make the function drag-only
/* if(selected.toString().length){ */
var d = document,
nA = selected.anchorNode,
oA = selected.anchorOffset,
nF = selected.focusNode,
oF = selected.focusOffset,
range = d.createRange(),
rangeLength = 0;
range.setStart(nA,oA);
range.setEnd(nF,oF);
// Check if direction of selection is right to left
if(range.startContainer !== nA || (nA === nF && oF < oA)){
range.setStart(nF,oF);
range.setEnd(nA,oA);
}
// Extend range to the next space or end of node
while(range.endOffset < range.endContainer.textContent.length && !/'s$/.test(range.toString())){
range.setEnd(range.endContainer, range.endOffset + 1);
}
// Extend range to the previous space or start of node
while(range.startOffset > 0 && !/^'s/.test(range.toString())){
range.setStart(range.startContainer, range.startOffset - 1);
}
// Remove spaces
if(/'s$/.test(range.toString()) && range.endOffset > 0)
range.setEnd(range.endContainer, range.endOffset - 1);
if(/^'s/.test(range.toString()))
range.setStart(range.startContainer, range.startOffset + 1);
// Store the length of the range
rangeLength = range.toString().length;
// Check if another range was previously selected
if(prevRangeInfo.startContainer && nA === nF && oA === oF){
var rangeTryContain = d.createRange(),
rangeTryLeft = d.createRange(),
rangeTryRight = d.createRange(),
nAp = prevRangeInfo.startContainer;
oAp = prevRangeInfo.startOffset;
nFp = prevRangeInfo.endContainer;
oFp = prevRangeInfo.endOffset;
rangeTryContain.setStart(nAp, oAp);
rangeTryContain.setEnd(nFp, oFp);
rangeTryLeft.setStart(nFp, oFp-1);
rangeTryLeft.setEnd(range.endContainer, range.endOffset);
rangeTryRight.setStart(range.startContainer, range.startOffset);
rangeTryRight.setEnd(nAp, oAp+1);
// Store range boundary comparisons
// & inner nodes close to the range boundary --> stores null if none
var compareStartPoints = range.compareBoundaryPoints(0, rangeTryContain) === 0,
compareEndPoints = range.compareBoundaryPoints(2, rangeTryContain) === 0,
leftInnerNode = range.endContainer.previousSibling,
rightInnerNode = range.startContainer.nextSibling;
// Do nothing if clicked on the right end of a word
if(range.toString().length < 1){
range.setStart(nAp,oAp);
range.setEnd(nFp,oFp);
}
// Collapse the range if clicked on last highlighted word
else if(compareStartPoints && compareEndPoints)
range.collapse();
// Remove a highlighted word from left side if clicked on
// This part is quite tricky!
else if(compareStartPoints){
range.setEnd(nFp,oFp);
if(range.startOffset + rangeLength + 1 >= range.startContainer.length){
if(rightInnerNode)
// there is a right inner node, set its start point as range start
range.setStart(rightInnerNode.firstChild, 0);
else {
// there is no right inner node
// there must be a text node on the right side of the clicked word
// set start of the next text node as start point of the range
var rightTextNode = findSibling(range.startContainer.parentNode, 1),
rightTextContent = rightTextNode.textContent,
level=1;
// if beginning of paragraph, find the first child of the paragraph
if(/^(?:'r'n|['r'n])|'s{2,}$/.test(rightTextContent)){
rightTextNode = findSibling(rightTextNode, 1).firstChild;
level--;
}
range.setStart(rightTextNode, level);
}
}
else
range.setStart(range.startContainer, range.startOffset + rangeLength + 1);
}
// Remove a hightlighted word from right side if clicked on
// This part is also tricky!
else if (compareEndPoints){
range.setStart(nAp,oAp);
if(range.endOffset - rangeLength - 1 <= 0){
if(leftInnerNode)
// there is a right inner node, set its start point as range start
range.setEnd(leftInnerNode.lastChild, leftInnerNode.lastChild.textContent.length);
else {
// there is no left inner node
// there must be a text node on the left side of the clicked word
// set start of the previous text node as start point of the range
var leftTextNode = findSibling(range.endContainer.parentNode, -1),
leftTextContent = leftTextNode.textContent,
level = 1;
// if end of paragraph, find the last child of the paragraph
if(/^(?:'r'n|['r'n])|'s{2,}$/.test(leftTextContent)){
leftTextNode = findSibling(leftTextNode, -1).lastChild;
level--;
}
range.setEnd(leftTextNode, leftTextNode.length - level);
}
}
else
range.setEnd(range.endContainer, range.endOffset - rangeLength - 1);
}
// Add previously selected range if adjacent
// Upgraded to include previous/next word even in a different paragraph
else if(/^[^'s]*((?:'r'n|['r'n])|'s{1,})[^'s]*$/.test(rangeTryLeft.toString()))
range.setStart(nAp,oAp);
else if(/^[^'s]*((?:'r'n|['r'n])|'s{1,})[^'s]*$/.test(rangeTryRight.toString()))
range.setEnd(nFp,oFp);
// Detach the range objects we are done with, clear memory
rangeTryContain.detach();
rangeTryRight.detach();
rangeTryLeft.detach();
}
// Save the current range --> not the whole Range object but what is neccessary
prevRangeInfo = {
startContainer: range.startContainer,
startOffset: range.startOffset,
endContainer: range.endContainer,
endOffset: range.endOffset
};
// Clear the saved range info if clicked on last highlighted word
if(compareStartPoints && compareEndPoints)
prevRangeInfo = {};
// Remove all ranges from selection --> necessary due to potential removals
selected.removeAllRanges();
// Assign the current range as selection
selected.addRange(range);
// Detach the range object we are done with, clear memory
range.detach();
el.style.MozUserSelect = '-moz-none';
// Removing the following line from comments will make the function drag-only
/* } */
} else {
// Fallback for Internet Explorer 8 and earlier
// (if you think it still is worth the effort of course)
}
});
/* This part is necessary to eliminate a FF specific dragging behavior */
el.addEventListener('mousedown',function(e){
if (window.getSelection) { // Works on all browsers, including IE 9+
var selection = window.getSelection ();
selection.collapse (selection.anchorNode, selection.anchorOffset);
} else {
// Fallback for Internet Explorer 8 and earlier
// (if you think it still is worth the effort of course)
}
el.style.MozUserSelect = 'text';
});
})(document.getElementById('selectable'));
升级前
将最后一个range
存储在object
中,并在每次进行新选择时检查先前选择的range
是否与新range
相邻,可以完成以下工作:
(function(el){
var prevRangeInfo = {};
el.addEventListener('mouseup',function(evt){
if (document.createRange) { // Works on all browsers, including IE 9+
var selected = window.getSelection();
/* if(selected.toString().length){ */
var d = document,
nA = selected.anchorNode,
oA = selected.anchorOffset,
nF = selected.focusNode,
oF = selected.focusOffset,
range = d.createRange();
range.setStart(nA,oA);
range.setEnd(nF,oF);
// Check if direction of selection is right to left
if(range.startContainer !== nA || (nA === nF && oF < oA)){
range.setStart(nF,oF);
range.setEnd(nA,oA);
}
// Extend range to the next space or end of node
while(range.endOffset < range.endContainer.textContent.length && !/'s$/.test(range.toString())){
range.setEnd(range.endContainer, range.endOffset + 1);
}
// Extend range to the previous space or start of node
while(range.startOffset > 0 && !/^'s/.test(range.toString())){
range.setStart(range.startContainer, range.startOffset - 1);
}
// Remove spaces
if(/'s$/.test(range.toString()) && range.endOffset > 0)
range.setEnd(range.endContainer, range.endOffset - 1);
if(/^'s/.test(range.toString()))
range.setStart(range.startContainer, range.startOffset + 1);
// Check if another range was previously selected
if(prevRangeInfo.startContainer){
var rangeTryLeft = d.createRange(),
rangeTryRight = d.createRange(),
nAp = prevRangeInfo.startContainer;
oAp = prevRangeInfo.startOffset;
nFp = prevRangeInfo.endContainer;
oFp = prevRangeInfo.endOffset;
rangeTryLeft.setStart(nFp,oFp-1);
rangeTryLeft.setEnd(range.endContainer,range.endOffset);
rangeTryRight.setStart(range.startContainer,range.startOffset);
rangeTryRight.setEnd(nAp,oAp+1);
// Add previously selected range if adjacent
if(/^[^'s]*'s{1}[^'s]*$/.test(rangeTryLeft.toString())) range.setStart(nAp,oAp);
else if(/^[^'s]*'s{1}[^'s]*$/.test(rangeTryRight.toString())) range.setEnd(nFp,oFp);
}
// Save the current range
prevRangeInfo = {
startContainer: range.startContainer,
startOffset: range.startOffset,
endContainer: range.endContainer,
endOffset: range.endOffset
};
// Assign range to selection
selected.addRange(range);
el.style.MozUserSelect = '-moz-none';
/* } */
} else {
// Fallback for Internet Explorer 8 and earlier
// (if you think it still is worth the effort of course)
}
});
/* This part is necessary to eliminate a FF specific dragging behavior */
el.addEventListener('mousedown',function(e){
if (window.getSelection) { // Works on all browsers, including IE 9+
var selection = window.getSelection ();
selection.collapse (selection.anchorNode, selection.anchorOffset);
} else {
// Fallback for Internet Explorer 8 and earlier
// (if you think it still is worth the effort of course)
}
el.style.MozUserSelect = 'text';
});
})(document.getElementById('selectable'));
JS在这里小提琴。
更新(在升级前完成(:
如果您希望此功能在单击但不拖动时有效,您所要做的就是更改if(prevRangeInfo.startContainer)
条件,如下所示:
if(prevRangeInfo.startContainer && nA === nF && oA === oF){
// rest of the code is the same...
更新后的JS小提琴在这里。
- 当鼠标悬停在文本中的单词上时显示警报
- 如何创建一个“;表单弹出框“;在chrome中右键单击时位于突出显示的单词上方
- 隐藏/显示包含单词的ul li项目.但只能入住李的子女
- 如何在分页事件中突出显示数据表中的单词
- Javascript:当这个单词被点击5次时,下面会显示另一个单词
- 突出显示页面上嵌入的iframe中的单词
- 突出显示jQuery中单词之间的空格
- 在两个位置显示数组中的随机单词
- 文本中的 Javascript 单词突出显示
- localeCompare 显示使用前导变音字符对单词进行排序时不一致的行为
- 有没有一个网站可以生成所有可能的显示单词的方式
- 禁用浏览器自动选项(显示输入元素中保存的单词)
- JQuery在链接的静态页面中搜索,突出显示找到的单词,而不破坏列表
- 显示类似的单词
- 如何从列表中突出显示一个随机单词
- 使用Javascript从列表中键入时高亮显示单词
- 转到单词突出显示的位置
- HTML “标题”属性显示单词“未定义” - 例如.“主页未定义”
- 如何在浏览器中显示单词/ pdf二进制数据
- 输入标签从不显示非数字单词的面