为了账号安全,请及时绑定邮箱和手机立即绑定

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

/ 猿问

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

富国沪深 2019-10-21 16:12:26

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

如果我有

<p> some long text </p>

在我的HTML页面上,我如何知道鼠标的光标就在‘text’这个词的上方?


查看完整描述

3 回答

?
海绵宝宝撒

除了另外两个答案之外,您还可以使用jQuery(或一般的javascript)将您的段落划分为多个部分。

这样的话,你就不需要考虑把你的文字输出到字里行间。让你的javascript为你做这件事。

G.

<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示例



查看完整回答
反对 回复 2019-10-22
?
慕粉4167745

我的另一个答案只适用于Firefox。这个答案适用于Chrome。(我不知道火狐也能用。)

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, e.x, e.y);



查看完整回答
反对 回复 2019-10-22
?
天涯尽头无女友

序言:

如果您有多个跨和嵌套的HTML来分隔单词(甚至是单词中的字符),那么上述所有解决方案都将难以返回完整和正确的单词。

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


解决方案1-带内部标签,环绕每个完整的单词:

一种解决方案是去掉段落中的SPAN标记,但保留它们的文本。因此,分裂词和短语作为常规文本重新连接在一起。每个单词都是由空格划分(而不仅仅是一个空格)找到的,这些单词被包装在可以单独访问的跨空间中。

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

代码:

$(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>

解决方案1全文演示


解决方案2-卡雷特检查和DOM遍历:

这里有一个更复杂的解决方案。这是一种使用节点遍历的算法解决方案,它准确地捕获文本节点中光标下的完整和正确的单词。

通过检查插入符号位置(使用caretPositionFromPointcaretRangeFromPoint,这一想法归功于@chrisv)。这可能是,也可能不是完整的词,现在。

然后对其进行分析,以查看它是否位于文本节点(开始或结束)的任一边缘。如果是的话,将检查前一个文本节点或下面的文本节点,以确定是否应该将其连接起来,以使这个单词片段更长。

例子:

Х</span>rт0съ必须回来Хrт0съ,不是Х也不rт0съ.

遍历DOM树以获得下一个无障碍文本节点。如果两个单词片段被<p>或其他障碍标签,则它们不相邻,因此不属于同一个单词。

例子:

њб.)</p><p>Во不应该回来њб.)Во


在演示中,左浮动div是光标下的单词。右边浮动div(如果可见)显示边界上的单词是如何形成的。其他标记可以安全地与此解决方案中的文本内联。

代码:

$(function() {

  // Get the HTML in #hoverText - just a wrapper for convenience

  var $hoverText = $("#hoverText");


  // Get the full word the cursor is over regardless of span breaks

  function getFullWord(event) {

     var i, begin, end, range, textNode, offset;

    

    // Internet Explorer

    if (document.body.createTextRange) {

       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 ""; // Sigh, IE

       }

    }

    

    // Firefox, Safari

    // REF: https://developer.mozilla.org/en-US/docs/Web/API/Document/caretPositionFromPoint

    else if (document.caretPositionFromPoint) {

      range = document.caretPositionFromPoint(event.clientX, event.clientY);

      textNode = range.offsetNode;

      offset = range.offset;


      // Chrome

      // REF: https://developer.mozilla.org/en-US/docs/Web/API/document/caretRangeFromPoint

    } else if (document.caretRangeFromPoint) {

      range = document.caretRangeFromPoint(event.clientX, event.clientY);

      textNode = range.startContainer;

      offset = range.startOffset;

    }


    // Only act on text nodes

    if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {

      return "";

    }


    var data = textNode.textContent;


    // Sometimes the offset can be at the 'length' of the data.

    // It might be a bug with this 'experimental' feature

    // Compensate for this below

    if (offset >= data.length) {

      offset = data.length - 1;

    }


    // Ignore the cursor on spaces - these aren't words

    if (isW(data[offset])) {

      return "";

    }


    // Scan behind the current character until whitespace is found, or beginning

    i = begin = end = offset;

    while (i > 0 && !isW(data[i - 1])) {

      i--;

    }

    begin = i;


    // Scan ahead of the current character until whitespace is found, or end

    i = offset;

    while (i < data.length - 1 && !isW(data[i + 1])) {

      i++;

    }

    end = i;


    // This is our temporary word

    var word = data.substring(begin, end + 1);


    // Demo only

    showBridge(null, null, null);


    // If at a node boundary, cross over and see what 

    // the next word is and check if this should be added to our temp word

    if (end === data.length - 1 || begin === 0) {


      var nextNode = getNextNode(textNode);

      var prevNode = getPrevNode(textNode);


      // Get the next node text

      if (end == data.length - 1 && nextNode) {

        var nextText = nextNode.textContent;


        // Demo only

        showBridge(word, nextText, null);


        // Add the letters from the next text block until a whitespace, or end

        i = 0;

        while (i < nextText.length && !isW(nextText[i])) {

          word += nextText[i++];

        }


      } else if (begin === 0 && prevNode) {

        // Get the previous node text

        var prevText = prevNode.textContent;


        // Demo only

        showBridge(word, null, prevText);


        // Add the letters from the next text block until a whitespace, or end

        i = prevText.length - 1;

        while (i >= 0 && !isW(prevText[i])) {

          word = prevText[i--] + word;

        }

      }

    }

    return word;

  }


  // Return the word the cursor is over

  $hoverText.mousemove(function(e) {

    var word = getFullWord(e);

    if (word !== "") {

      $("#result").text(word);

    }

  });

});


// Helper functions


// Whitespace checker

function isW(s) {

  return /[ \f\n\r\t\v\u00A0\u2028\u2029]/.test(s);

}


// Barrier nodes are BR, DIV, P, PRE, TD, TR, ... 

function isBarrierNode(node) {

  return node ? /^(BR|DIV|P|PRE|TD|TR|TABLE)$/i.test(node.nodeName) : true;

}


// Try to find the next adjacent node

function getNextNode(node) {

  var n = null;

  // Does this node have a sibling?

  if (node.nextSibling) {

    n = node.nextSibling;


    // Doe this node's container have a sibling?

  } else if (node.parentNode && node.parentNode.nextSibling) {

    n = node.parentNode.nextSibling;

  }

  return isBarrierNode(n) ? null : n;

}


// Try to find the prev adjacent node

function getPrevNode(node) {

  var n = null;


  // Does this node have a sibling?

  if (node.previousSibling) {

    n = node.previousSibling;


    // Doe this node's container have a sibling?

  } else if (node.parentNode && node.parentNode.previousSibling) {

    n = node.parentNode.previousSibling;

  }

  return isBarrierNode(n) ? null : n;

}


// 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;

}


// DEMO-ONLY code - this shows how the word is recombined across boundaries

function showBridge(word, nextText, prevText) {

  if (nextText) {

    $("#bridge").html("<span class=\"word\">" + word + "</span>  |  " + nextText.substring(0, 20) + "...").show();

  } else if (prevText) {

    $("#bridge").html("..." + prevText.substring(prevText.length - 20, prevText.length) + "  |  <span class=\"word\">" + word + "</span>").show();

  } else {

    $("#bridge").hide();

  }

}

.kinovar { color:red; font-size:20px;}.slavic { color: blue;}#result {top:10px;left:10px;}#bridge { top:10px; right:80px;}.floater { position: fixed; background-color:white; border:2px solid black; padding:4px;}.word { color:blue;}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <div id="bridge" class="floater"></div> <div id="result" class="floater"></div> <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"> <input value="Works around other tags!"><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.<input value="Works around inline tags too"></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></div>

(注:我冒昧地将样式应用于示例HTML中的SPAN标记,以说明文本节点边框的位置。)

解决方案2全文演示

(目前在Chrome和IE上工作。对于IE,艾兰格为了实现跨浏览器兼容性,必须将其用作垫片)



查看完整回答
反对 回复 2019-10-22

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信