Javascript 如何更快、更智能地突出显示单词/术语?
我有一些文字:Javascript 如何更快、更智能地突出显示单词/术语?,javascript,jquery,Javascript,Jquery,我有一些文字: <p class="drag">Hello world, Attack on Titan season two!</p> 你好,世界,攻击泰坦第二季 目前,如果用户想用光标突出显示一个单词/术语,他们将逐个字母单击并拖动 我希望这个过程更快。例如,如果用户在处开始突出显示,则应自动突出显示单词的其余部分,攻击。因此,空的空间是分隔符 我知道这可以通过将单词划分为div来实现,但我希望在一个标记中包含纯文本的解决方案。您不能将事件设置为文本,但可以将事件
<p class="drag">Hello world, Attack on Titan season two!</p>
你好,世界,攻击泰坦第二季
目前,如果用户想用光标突出显示一个单词/术语,他们将逐个字母单击并拖动
我希望这个过程更快。例如,如果用户在处开始突出显示,则应自动突出显示单词的其余部分,攻击
。因此,空的空间是分隔符
我知道这可以通过将单词划分为div来实现,但我希望在一个标记中包含纯文本的解决方案。您不能将事件设置为文本,但可以将事件设置为Html元素。将每个单词放入div元素中,并在mouseover上添加一个事件,使用css将div更改为新的高亮显示状态
步骤:
使用split将单词放入数组
迭代单词并将其放入div中
迭代div并将其设置为将div更改为css类的事件。高亮显示
就是这样。此文本是一个文本节点,文本节点不会触发大多数事件。但它们可以触发,例如DOMCharacterDataModified
,用于检测文本节点文本的更改:
var textNode = document.getElementsByClassName("drag")[0].firstChild;
textNode.addEventListener("DOMCharacterDataModified", function(e) {
console.log("Text changed from '" + e.prevValue + "' to '" + evt.newValue +"'");
}, false);
然而,《你好,世界,攻击泰坦》第二季中的文字
是单个文本节点,您需要将每个单词都作为单独的节点
我看到的唯一解决办法是将每个单词都放在span
标记中。纯文本无法做到这一点
编辑
下面是一个如何使用span
标记执行此操作的示例(我在这里使用jQuery只是为了减少代码量,没有必要):
这是一个
更新
我编辑了,因此选择的行为符合您的要求(目前它仅适用于从左到右的选择,而不适用于反向选择):
您可以在此处阅读有关HTML范围API的更多信息:Concepts
要选择每个单词,您必须首先记住以下几点:
textNode
是一个包含所有单词的单一sting,您可以
无法选择每个“单词”,因为它不是DOM节点
“拖放”时,浏览器中不会触发任何特定事件
选择“一个单词”。但是,当您拖动并选择时,有两个事件
被解雇:mouseover
在您移动鼠标时触发,
单击
会在释放鼠标按钮时触发。(这是
即使在Mac的触摸板上也是如此)
当您选择一个选项时,“突出显示”有不同的实现
字
台阶
根据这些概念,您必须按顺序执行以下步骤以实现您的目标:
获取段落中的单词,并用标记(例如,
)将它们包装起来,以便进行DOM选择
当单击事件触发时(表示您的选择已结束),突出显示您刚刚选择的单词
实现类似这样(使用jQuery
)。
您可以在这里看到现场演示:
$(函数(){
//1.鼠标悬停在段落上方时,用
$('p').one('mouseover',函数(事件){
$('p').html(函数(索引,文本){
var wordsArray=text.split(“”);
var wrappedArray=wordsArray.map(函数(val,索引){
val=''+val+'';
返回val;
});
var wrappedString=wrappedArray.join(“”);
//2.将该段替换为换行文本
$(this.html(wrappedString);
//3.选择单词时,突出显示该单词
$(this.children('span')。在('click',function()上){
变量选择器='.+$(this.attr('class');
选择文本(选择器);
});
});
});
});
函数SelectText(元素){
var doc=文件,
text=文档查询选择器(元素),
范围、选择;
if(doc.body.createTextRange){
range=document.body.createTextRange();
范围。移动到元素文本(文本);
range.select();
}else if(window.getSelection){
selection=window.getSelection();
range=document.createRange();
范围。选择节点内容(文本);
selection.removeAllRanges();
选择。添加范围(范围);
}
}
Lorem ipsum dolor sit amet,奉献精英。这句话的意思是:“我们在听写、清晰的自由、时间等方面都有优势。”!作为一种必要的工具,我们必须。建筑理性的耳廓卷曲
你的工作是什么?在非自愿的情况下,在自愿的情况下,在自愿的情况下,必须在自愿的情况下以最低限度的积累来履行职责。多洛雷姆格
工作场所必须有适当的法定代表人,即临时性的最低法定代表人减去法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人的法定代表人。大盲肠出血,同侧产道出血
您可以使用纯JS使用Range
和selectionRange
对象来实现这一点
HTML:
<div id="selectable">
<p>Hello world, <b>Attack on Titan</b> season two!</p>
<p>Another paragraph with sample text.</p>
</div>
<div id="notSelectable">
<p>The selection will behave normally on this div.</p>
</div>
(function(el){
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);
/* } */
} else {
// Fallback for Internet Explorer 8 and earlier
// (if you think it still is worth the effort of course)
}
// Stop Moz user select
el.style.MozUserSelect = '-moz-none';
});
/* This part is added 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)
}
// Add Moz user select back
el.style.MozUserSelect = 'text';
});
})(document.getElementById('selectable'));
你好,世界,攻击泰坦第二季
另一段,带示例文本
选择将在此分区上正常运行
JS:
<div id="selectable">
<p>Hello world, <b>Attack on Titan</b> season two!</p>
<p>Another paragraph with sample text.</p>
</div>
<div id="notSelectable">
<p>The selection will behave normally on this div.</p>
</div>
(function(el){
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);
/* } */
} else {
// Fallback for Internet Explorer 8 and earlier
// (if you think it still is worth the effort of course)
}
// Stop Moz user select
el.style.MozUserSelect = '-moz-none';
});
/* This part is added 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)
}
// Add Moz user select back
el.style.MozUserSelect = 'text';
});
})(document.getElementById('selectable'));
(功能(el){
el.addEventListener('mouseup',函数(evt){
if(document.createRange){//适用于所有浏览器,包括IE 9+
var selected=window.getSelection();
/*if(selected.toString().length){*/
var d=文件,
nA=选定的.anchorNode,
oA=选定的。锚定偏移,
nF=选定的.focusNode,
oF=所选。焦点偏移,
range=d.createRange();
范围。设置开始(nA,oA);
设置范围(nF,oF);
//检查选择方向是否从右向左
如果(range.startContainer!==nA | |(nA==nF&
<p>New season of <span class="redtext">Attack on Titan!</span></p>
var WordJumpSelection = (function() {
var watchList = [];
var WordJumpSelection = {
stopWatching: function(elem) {
var wlIdx = watchList.indexOf(elem);
if(wlIdx > -1) watchList.splice(wlIdx,1);
},
watch: function(elem) {
var elems = Array.prototype.slice.call(typeof elem.length === "number" ? elem : arguments);
if(watchList.length === 0)
{
WordJumpSelection.init();
}
elems.forEach(function(elem) {
if(watchList.indexOf(elem) === -1)
{
watchList.push(elem);
}
});
},
init: function() {
function handleSelectionChange() {
if(watchList.length === 0) return;
var selection = window.getSelection();
var selDir = getSelectionDir(selection);
var startNode,endNode,startPos,endPos;
if(selDir === 1)
{
startNode = selection.anchorNode;
endNode = selection.focusNode;
startPos = selection.anchorOffset;
endPos = selection.focusOffset;
}
else
{
startNode = selection.focusNode;
endNode = selection.anchorNode;
startPos = selection.focusOffset;
endPos = selection.anchorOffset;
}
var rangeStart = textNodeIsWatched(startNode) ? roundSelectionIndex(startNode,0,startPos) : startPos-1;
var rangeEnd = textNodeIsWatched(endNode) ? roundSelectionIndex(endNode,1,endPos) : endPos;
var r = document.createRange();
r.setStart(startNode,rangeStart+1)
r.setEnd(endNode,rangeEnd)
selection.removeAllRanges();
selection.addRange(r);
}
document.documentElement.addEventListener('mouseup', handleSelectionChange);
document.documentElement.addEventListener('keyup', function(e) {
if(e.keyCode === 16)
{
handleSelectionChange();
}
});
WordJumpSelection.init = function(){};
}
};
return WordJumpSelection;
function getSelectionDir(sel) {
var range = document.createRange();
range.setStart(sel.anchorNode,sel.anchorOffset);
range.setEnd(sel.focusNode,sel.focusOffset);
if(range.startContainer !== sel.anchorNode || (sel.anchorNode === sel.focusNode && sel.focusOffset < sel.anchorOffset)) return -1;
else return 1;
}
function roundSelectionIndex(textNode,nodeId,idx) {
var isStart = nodeId === 0;
var contents = textNode.textContent;
var nearestSpaceIdx = -1;
if(isStart)
{
nearestSpaceIdx = contents.lastIndexOf(' ',idx);
if(nearestSpaceIdx === -1) nearestSpaceIdx = -1;
}
else
{
nearestSpaceIdx = contents.indexOf(' ',idx);
if(nearestSpaceIdx === -1) nearestSpaceIdx = contents.length;
}
return nearestSpaceIdx;
}
function textNodeIsWatched(textNode) {
return watchList.indexOf(textNode.parentElement) > -1;
}
})();