如何使用JavaScript获取光标下的单词?

如果我例如有

<p> some long text </p> 

在我的HTML页面上,我怎么能知道鼠标的光标在“文本”这个单词的上面?

除了另外两个答案之外,您可以使用jQuery(或通常的JavaScript)将段落分成多个段落。

这样,你就不需要考虑在文字周围输出你的文字了。 让你的JavaScript为你做。

例如

 <p>Each word will be wrapped in a span.</p> <p>A second paragraph here.</p> Word: <span id="word"></span> <script type="text/javascript"> $(function() { // wrap words in spans $('p').each(function() { var $this = $(this); $this.html($this.text().replace(/\b(\w+)\b/g, "<span>$1</span>")); }); // bind to each span $('p span').hover( function() { $('#word').text($(this).css('background-color','#ffff66').text()); }, function() { $('#word').text(''); $(this).css('background-color',''); } ); }); </script> 

请注意,上面的代码,虽然它的作品,将剥离你的段落标签内的任何HTML。

jsFiddle的例子

我的其他答案只适用于Firefox。 这个答案适用于Chrome。 (也可能在Firefox上工作,我不知道。)

 function getWordAtPoint(elem, x, y) { if(elem.nodeType == elem.TEXT_NODE) { var range = elem.ownerDocument.createRange(); range.selectNodeContents(elem); var currentPos = 0; var endPos = range.endOffset; while(currentPos+1 < endPos) { range.setStart(elem, currentPos); range.setEnd(elem, currentPos+1); if(range.getBoundingClientRect().left <= x && range.getBoundingClientRect().right >= x && range.getBoundingClientRect().top <= y && range.getBoundingClientRect().bottom >= y) { range.expand("word"); var ret = range.toString(); range.detach(); return(ret); } currentPos += 1; } } else { for(var i = 0; i < elem.childNodes.length; i++) { var range = elem.childNodes[i].ownerDocument.createRange(); range.selectNodeContents(elem.childNodes[i]); if(range.getBoundingClientRect().left <= x && range.getBoundingClientRect().right >= x && range.getBoundingClientRect().top <= y && range.getBoundingClientRect().bottom >= y) { range.detach(); return(getWordAtPoint(elem.childNodes[i], x, y)); } else { range.detach(); } } } return(null); } 

在您的mousemove处理程序中,调用getWordAtPoint(e.target, ex, ey);

前言:

如果你有多个跨度和嵌套的HTML来区分单词(或甚至单词),那么上述所有的解决scheme将无法返回完整和正确的单词。

这是一个来自赏金问题的例子: Х</span>rт0съ 。 如何正确返回Хrт0съ ? 这些问题在2010年没有得到解决,因此我现在提出两个解决scheme(2015年)。


解决scheme1 ​​ – 剥去内部标签,在每个完整的单词周围包裹跨度:

一种解决方法是去除段落内的span标签,但保留文本。 分词和短语因此作为普通文本重新组合在一起。 每个单词都是通过空格分隔(不仅仅是一个空格)来find的,而且这些单词被包裹在可以单独访问的跨度中。

在演示中,您可以突出显示整个单词,从而得到整个单词的文本。


图片0

码:

 $(function() { // Get the HTML in #hoverText - just a wrapper for convenience var $hoverText = $("#hoverText"); // Replace all spans inside paragraphs with their text $("p span", $hoverText).each(function() { var $this = $(this); var text = $this.text(); // get span content $this.replaceWith(text); // replace all span with just content }); // Wrap words in spans AND preserve the whitespace $("p", $hoverText).each(function() { var $this = $(this); var newText = $this.text().replace(/([\s])([^\s]+)/g, "$1<span>$2</span>"); newText = newText.replace(/^([^\s]+)/g, "<span>$1</span>"); $this.empty().append(newText); }); // Demo - bind hover to each span $('#hoverText span').hover( function() { $(this).css('background-color', '#ffff66'); }, function() { $(this).css('background-color', ''); } ); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <div id="hoverText"> <p><span class="kinovar"><span id="selection_index3337" class="selection_index"></span>По f7-мъ часЁ твори1тъ сщ7eнникъ начaло съ кади1ломъ и3 со свэщeю, цrкимъ двeремъ tвeрзєннымъ, и3 поeтъ: Х</span>rт0съ воскRсе: <span class="kinovar">со стіхи2. И# по стісёхъ pал0мъ: Б</span>лгcви2 душE моS гDа: <span class="kinovar">И# є3ктеніA. Тaже каfjсма nбhчнаz.</span> </p> </div> 

据我所知,你不能。

只有我能想到的是把每个单词放在他们自己的元素中,然后将鼠标移到事件上去。

 <p><span>Some</span> <span>long</span> <span>text</span></p> <script> $(document).ready(function () { $('p span').bind('mouseenter', function () { alert($(this).html() + " is what you're currently hovering over!"); }); }); </script> 

在当前CSSOM查看草稿中有一个这样的API: document.caretPositionFromPoint(x,y)

不过,你必须检查哪个浏览器支持这个。 Firefox 7似乎不支持它,而错误报告显示Firefox 9会。 Chrome 14支持caretRangeFromPoint(x,y) ,它基本上是相同的,但是来自旧的CSSOM草案。

这是赏金的解决scheme。

正如chrisv所build议的,你可以使用document.caretRangeFromPoint (chrome)或document.caretPositionFromPoint (Firefox)。 我认为这个解决scheme更好地回答你的问题,因为它不会改变你的文本或DOM。

该函数返回鼠标光标下的单词而不改变DOM:

document.caretRangeFromPoint 文档 :

Document接口的caretRangeFromPoint()方法返回指定坐标下的文档片段的Range对象。

document.caretPositionFromPoint 文档 :

此方法用于根据两个坐标检索文档中的插入位置。 返回一个CaretPosition,它包含find的DOM节点和该节点中的字符偏移量。

这两个函数略有不同,但它们都返回包含文本的节点以及此文本中光标的偏移量。 所以很容易在鼠标下面find这个词。

看完整的例子:

 $(function () { function getWordUnderCursor(event) { var range, textNode, offset; if (document.body.createTextRange) { // Internet Explorer try { range = document.body.createTextRange(); range.moveToPoint(event.clientX, event.clientY); range.select(); range = getTextRangeBoundaryPosition(range, true); textNode = range.node; offset = range.offset; } catch(e) { return ""; } } else if (document.caretPositionFromPoint) { // Firefox range = document.caretPositionFromPoint(event.clientX, event.clientY); textNode = range.offsetNode; offset = range.offset; } else if (document.caretRangeFromPoint) { // Chrome range = document.caretRangeFromPoint(event.clientX, event.clientY); textNode = range.startContainer; offset = range.startOffset; } //data contains a full sentence //offset represent the cursor position in this sentence var data = textNode.data, i = offset, begin, end; //Find the begin of the word (space) while (i > 0 && data[i] !== " ") { --i; }; begin = i; //Find the end of the word i = offset; while (i < data.length && data[i] !== " ") { ++i; }; end = i; //Return the word under the mouse cursor return data.substring(begin, end); } //Get the HTML in a div #hoverText and detect mouse move on it var $hoverText = $("#hoverText"); $hoverText.mousemove(function (e) { var word = getWordUnderCursor(e); //Show the word in a div so we can test the result if (word !== "") $("#testResult").text(word); }); }); // This code make it works with IE // REF: https://stackoverflow.com/questions/3127369/how-to-get-selected-textnode-in-contenteditable-div-in-ie function getTextRangeBoundaryPosition(textRange, isStart) { var workingRange = textRange.duplicate(); workingRange.collapse(isStart); var containerElement = workingRange.parentElement(); var workingNode = document.createElement("span"); var comparison, workingComparisonType = isStart ? "StartToStart" : "StartToEnd"; var boundaryPosition, boundaryNode; // Move the working range through the container's children, starting at // the end and working backwards, until the working range reaches or goes // past the boundary we're interested in do { containerElement.insertBefore(workingNode, workingNode.previousSibling); workingRange.moveToElementText(workingNode); } while ( (comparison = workingRange.compareEndPoints( workingComparisonType, textRange)) > 0 && workingNode.previousSibling); // We've now reached or gone past the boundary of the text range we're // interested in so have identified the node we want boundaryNode = workingNode.nextSibling; if (comparison == -1 && boundaryNode) { // This must be a data node (text, comment, cdata) since we've overshot. // The working range is collapsed at the start of the node containing // the text range's boundary, so we move the end of the working range // to the boundary point and measure the length of its text to get // the boundary's offset within the node workingRange.setEndPoint(isStart ? "EndToStart" : "EndToEnd", textRange); boundaryPosition = { node: boundaryNode, offset: workingRange.text.length }; } else { // We've hit the boundary exactly, so this must be an element boundaryPosition = { node: containerElement, offset: getChildIndex(workingNode) }; } // Clean up workingNode.parentNode.removeChild(workingNode); return boundaryPosition; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <b><div id="testResult"></div></b> <div id="hoverText"> <p><span class="kinovar"><span id="selection_index3337" class="selection_index"></span>По f7-мъ часЁ твори1тъ сщ7eнникъ начaло съ кади1ломъ и3 со свэщeю, цrкимъ двeремъ tвeрзєннымъ, и3 поeтъ: Х</span>rт0съ воскRсе: <span class="kinovar">со стіхи2. И# по стісёхъ pал0мъ: Б</span>лгcви2 душE моS гDа: <span class="kinovar">И# є3ктеніA. Тaже каfjсма nбhчнаz.</span> </p> <div class="slavic"><p><span id="selection_index3737" class="selection_index"></span>(л. рo7з њб.)</p> <p><span class="kinovar"><span id="selection_index3738" class="selection_index"></span>Во вт0рникъ вeчера</span></p> <p><span class="kinovar"><span id="selection_index3739" class="selection_index"></span>tдaніе прaздника пaсхи.</span></p><p><span class="kinovar"><span id="selection_index3740" class="selection_index"></span>По f7-мъ часЁ твори1тъ сщ7eнникъ начaло съ кади1ломъ и3 со свэщeю, цrкимъ двeремъ tвeрзєннымъ, и3 поeтъ: Х</span>rт0съ воскRсе: <span class="kinovar">состіхи2. И# по стісёхъ pал0мъ: Б</span>лгcви2 душE моS гDа: <span class="kinovar">И# є3ктеніA. Тaже каfjсма nбhчнаz.</span> </p><p><span class="kinovar"><span id="selection_index3741" class="selection_index"></span>На ГDи воззвaхъ: поeмъ стіхи6ры самоглaсны, слэпaгw, на ѕ7. Глaсъ в7:</span> </p><p><span class="kinovar"><span id="selection_index3742" class="selection_index"></span>С</span>лэпhй роди1выйсz, въ своeмъ п0мыслэ глаг0лаше: є3дA ѓзъ грBхъ рaди роди1тельныхъ роди1хсz без8 џчію; (л. рo7и) є3дA ѓзъ за невёріе kзhкwвъ роди1хсz во њбличeніе; не домышлsюсz вопрошaти: когдA н0щь, когдA дeнь; не терпи1та ми2 н0зэ кaменнагw претыкaніz, не ви1дэхъ сlнца сіsюща, нижE во џбразэ менE создaвшагw. но молю1 ти сz хrтE б9е, при1зри на мS, и3 поми1луй мS.</p></div></div> 

您可能不得不分解段落,以便每个单词都包含在其自己单独的<span>元素中,然后将onmouseover事件属性添加到每个单词中。

我认为你的意思是“<一些长文本</ p>”; 反斜杠不是HTML的一部分。

噢! 这是喔!

简单,因为它是没有JQuery或任何其他框架小提琴: https : //jsfiddle.net/703c96dr/

它将把每个单词的跨度,并添加onmouseover和onomouseoutfunction。 我可以创build一个简单的类,使其更加可用,但代码非常简单,任何人都可以编辑和使用。

 <p>This is my text example of word highlighting or, if you want, word hovering</p> <p>This is another text example of word highlighting or, if you want, word hovering</p> 

简单的代码

 function onmouseoverspan(){ this.style.backgroundColor = "red"; } function onmouseoutspan(){ this.style.backgroundColor = "transparent"; } var spans,p = document.getElementsByTagName("p"); for(var i=0;i<p.length;i++) { if(p[i]==undefined) continue; p[i].innerHTML = p[i].innerHTML.replace(/\b(\w+)\b/g, "<span>$1</span>"); spans = p[i].getElementsByTagName("span") for(var a=0;a<spans.length;a++) { spans[a].onmouseover = onmouseoverspan; spans[a].onmouseout = onmouseoutspan; } } 

在Firefox中,您可以挂接mousemove事件。 callback有一个参数,例如, 在callback中,执行以下操作:

 var range = HTTparent.ownerDocument.createRange(); range.selectNode(e.rangeParent); var str = range.toString(); range.detach(); 

现在str有鼠标所有的文本。 e.rangeOffset是该string中鼠标指针的位置。 在你的情况下,str将是“一些长文本”,如果你在“文本”中的“e”,e.rangeOffset将是11。

如果您处于边缘,则此代码会有点困惑,例如当鼠标指针与文本位于同一行时,但在文本结束之后。 要解决这个问题,你需要检查你是否真的在文本之上。 这是testing:

 if(e && e.rangeParent && e.rangeParent.nodeType == e.rangeParent.TEXT_NODE && e.rangeParent.parentNode == e.target) 

这项技术适用于Firefox。 在Chrome中无法使用。

 function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&amp;") .replace(/</g, "&lt;") .replace(/>/g, "&gt;") .replace(/"/g, "&quot;") .replace(/'/g, "&#039;"); } // REF: http://stackoverflow.com/questions/3127369/how-to-get-selected-textnode-in-contenteditable-div-in-ie function getChildIndex(node) { var i = 0; while( (node = node.previousSibling) ) { i++; } return i; } // All this code just to make this work with IE, OTL // REF: http://stackoverflow.com/questions/3127369/how-to-get-selected-textnode-in-contenteditable-div-in-ie function getTextRangeBoundaryPosition(textRange, isStart) { var workingRange = textRange.duplicate(); workingRange.collapse(isStart); var containerElement = workingRange.parentElement(); var workingNode = document.createElement("span"); var comparison, workingComparisonType = isStart ? "StartToStart" : "StartToEnd"; var boundaryPosition, boundaryNode; // Move the working range through the container's children, starting at // the end and working backwards, until the working range reaches or goes // past the boundary we're interested in do { containerElement.insertBefore(workingNode, workingNode.previousSibling); workingRange.moveToElementText(workingNode); } while ( (comparison = workingRange.compareEndPoints( workingComparisonType, textRange)) > 0 && workingNode.previousSibling); // We've now reached or gone past the boundary of the text range we're // interested in so have identified the node we want boundaryNode = workingNode.nextSibling; if (comparison == -1 && boundaryNode) { // This must be a data node (text, comment, cdata) since we've overshot. // The working range is collapsed at the start of the node containing // the text range's boundary, so we move the end of the working range // to the boundary point and measure the length of its text to get // the boundary's offset within the node workingRange.setEndPoint(isStart ? "EndToStart" : "EndToEnd", textRange); boundaryPosition = { node: boundaryNode, offset: workingRange.text.length }; } else { // We've hit the boundary exactly, so this must be an element boundaryPosition = { node: containerElement, offset: getChildIndex(workingNode) }; } // Clean up workingNode.parentNode.removeChild(workingNode); return boundaryPosition; } function onClick(event) { var elt = document.getElementById('info'); elt.innerHTML = ""; var textNode; var offset; // Internet Explorer if (document.body.createTextRange) { elt.innerHTML = elt.innerHTML+("*************** IE **************<br/>"); range = document.body.createTextRange(); range.moveToPoint(event.clientX, event.clientY); range.select(); range = getTextRangeBoundaryPosition(range, true); textNode = range.node; offset = range.offset; elt.innerHTML = elt.innerHTML + "IE ok, result: [" + escapeHtml(textNode.nodeName) + "]/[" + escapeHtml(textNode.textContent) + "] @" + offset + "</br>"; } // Internet Explorer method 2 if (document.body.createTextRange) { elt.innerHTML = elt.innerHTML+("*************** IE, Method 2 **************<br/>"); range = document.body.createTextRange(); range.moveToPoint(event.clientX, event.clientY); range.select(); var sel = document.getSelection(); textNode = sel.anchorNode; offset = sel.anchorOffset; elt.innerHTML = elt.innerHTML + "IE M2 ok, result: [" + escapeHtml(textNode.nodeName) + "]/[" + escapeHtml(textNode.textContent) + "] @" + offset + "</br>"; } // Firefox, Safari // REF: https://developer.mozilla.org/en-US/docs/Web/API/Document/caretPositionFromPoint if (document.caretPositionFromPoint) { elt.innerHTML = elt.innerHTML+("*************** Firefox, Safari **************<br/>"); range = document.caretPositionFromPoint(event.clientX, event.clientY); textNode = range.offsetNode; offset = range.offset; elt.innerHTML = elt.innerHTML + "caretPositionFromPoint ok, result: [" + escapeHtml(textNode.nodeName) + "]/[" + escapeHtml(textNode.textContent) + "] @" + offset + "</br>"; // Chrome // REF: https://developer.mozilla.org/en-US/docs/Web/API/document/caretRangeFromPoint } if (document.caretRangeFromPoint) { elt.innerHTML = elt.innerHTML+("*************** Chrome **************<br/>"); range = document.caretRangeFromPoint(event.clientX, event.clientY); textNode = range.startContainer; offset = range.startOffset; elt.innerHTML = elt.innerHTML + "caretRangeFromPoint ok, result: [" + escapeHtml(textNode.nodeName) + "]/[" + escapeHtml(textNode.textContent) + "] @" + offset + "</br>"; } } document.addEventListener('click', onClick); 
 #info { position: absolute; bottom: 0; background-color: cyan; } 
 <div class="parent"> <div class="child">SPACE&nbsp;SPACE Bacon ipsum dolor amet <span>SPAN SPANTT SPOOR</span> meatball bresaola t-bone tri-tip brisket. Jowl pig picanha cupim SPAXE landjaeger, frankfurter spare ribs chicken. Porchetta jowl pancetta drumstick shankle cow spare ribs jerky tail kevin biltong capicola brisket venison bresaola. Flank sirloin jowl andouille meatball venison salami ground round rump boudin turkey capicola t-bone. Sirloin filet mignon tenderloin beef, biltong doner bresaola brisket shoulder pork loin shankle turducken shank cow. Bacon ball tip sirloin ham. </div> <div id="info">Click somewhere in the paragraph above</div> </div> 
Interesting Posts