get word in given coordinate (x,y)

Asked

Viewed 433 times

5

I am formatting a document for printing and need to add a page break + header and footer at certain coordinates, if the coordinate is in the middle of the paragraph I need to divide it. I can already make the insertions by manually entering the position where the paragraph divides.

In my initial approach I tried to get the word in the coordinate (x,y) and then the indexOf() of that word in relation to the paragraph. With "Indice" I can integrate with the existing code.

The closest I could get was to use the function document.elementFromPoint(x,y), that returns the entire paragraph.

Below the test code. I am using the mouse click event to simulate the coordinates. In my implementation only calculated coordinates will be used.

document.addEventListener('click', function(event) {
  alert(document.elementFromPoint(event.clientX, event.clientY).textContent);
}, false);
div {margin: 15px 0;}
p {margin: 0;}
<div>
  <p>Cras vel erat sit amet eros posuere volutpat nec in massa. Quisque dignissim mollis aliquet.</p>
  <p>Fusce suscipit rhoncus mi a dapibus. Donec nisl augue, molestie sed porttitor id, pulvinar tempor neque.</p>
  <p>Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec et enim eleifend velit faucibus consequat vel at risus.</p>
</div>
<div>
  <p>Aliquam erat volutpat.</p>
  <p>Phasellus ornare feugiat convallis. Aenean tincidunt tristique sem eu porttitor. Aliquam convallis eu purus et venenatis.</p>
  <p>Suspendisse euismod ullamcorper odio, ac sollicitudin quam pharetra sed. Vestibulum dictum cursus sollicitudin.</p>
  <p>Praesent at odio nisi. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris sodales vehicula neque quis imperdiet. Morbi lacinia libero in posuere porttitor. Curabitur pretium vel tortor in aliquet.</p>
</div>
<div>
  <p>Integer eu sapien odio. Morbi blandit nibh leo, in dapibus sem malesuada vitae. Etiam sit amet tristique sem.</p>
  <p>Sed mattis lectus lorem, at dapibus leo suscipit quis. Nunc in massa quis mauris suscipit gravida. Sed at nunc mauris. Duis et lectus ex.</p>
</div>

  • 2

    What if you wrap each word in a span? Then the element returned by that code of yours would be the word. You can calmly do this "package" by JS, I think you already have a question here on the site about this. This one can be a starting point: http://answall.com/questions/41477/comordestrinchar-um-texto-qualquer-no-momento-do-echo-transform-cada-pal/41479#41479

  • @bfavaretto what I need is the position of the word in the paragraph and not the word itself. Use <span> seems a good alternative, really can be a starting point

  • With each word in a span, you get the coordinates of any of them with Element.getBoundingClientRect().

  • Another answer in Soen suggests create a span for each word, half the wave the bfavaretto suggested. Would that be an option? What do you intend to do? color what was clicked?

  • @bfavaretto the function getBoundingClienteRect does the opposite of what I need, it does element->coordinate. @Sergio added the expected result to the question

  • Ah, I had misunderstood your previous comment. So that’s the method you’re using, elementFromPoint. As long as the word is alone inside an element (the span I suggested).

Show 1 more comment

2 answers

1

If you don’t want to adopt the solution @Sanction indicated, have this code here that worked perfectly here in Chrome.

Source: https://stackoverflow.com/a/3710561/3257568

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);
}
document.body.addEventListener("click", function(e) {
  alert(getWordAtPoint(e.target, e.x, e.y)); 
});
div {margin: 15px 0;}
p {margin: 0;}
<div>
  <p>Cras vel erat sit amet eros posuere volutpat nec in massa. Quisque dignissim mollis aliquet.</p>
  <p>Fusce suscipit rhoncus mi a dapibus. Donec nisl augue, molestie sed porttitor id, pulvinar tempor neque.</p>
  <p>Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec et enim eleifend velit faucibus consequat vel at risus.</p>
</div>
<div>
  <p>Aliquam erat volutpat.</p>
  <p>Phasellus ornare feugiat convallis. Aenean tincidunt tristique sem eu porttitor. Aliquam convallis eu purus et venenatis.</p>
  <p>Suspendisse euismod ullamcorper odio, ac sollicitudin quam pharetra sed. Vestibulum dictum cursus sollicitudin.</p>
  <p>Praesent at odio nisi. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris sodales vehicula neque quis imperdiet. Morbi lacinia libero in posuere porttitor. Curabitur pretium vel tortor in aliquet.</p>
</div>
<div>
  <p>Integer eu sapien odio. Morbi blandit nibh leo, in dapibus sem malesuada vitae. Etiam sit amet tristique sem.</p>
  <p>Sed mattis lectus lorem, at dapibus leo suscipit quis. Nunc in massa quis mauris suscipit gravida. Sed at nunc mauris. Duis et lectus ex.</p>
</div>

  • I liked the solution, iterate all elements recursively, using crease from selection to pick up the word, I will test the integration with my existing code

0


I found the solution based on in that question from Soen from the @Maiconcarraro response.

I used the function document.caretRangeFromPoint(x, y), which creates a text selection in the infomada coordinate. The returned object contains the information I need: the DOM element (.startElement) and the "Index" of the position (.startOffset).

For my problem was the best solution because it does not change the DOM (as the solution with <span>) and there is no need to iterate several elements. The negative point is the difference between browsers, but in my case just need to meet Webkit.

Note: to specification recommends the implementation of the document.caretPositionFromPoint() but Webkit and IE have created their own alternatives.

In the code section below I kept the function of obtaining the whole word, besides obtaining the DOM element and the position of the text (it should work in updated browsers).

document.addEventListener('click', function(e) {
  e.preventDefault();
  var caret, range;
  if (document.caretRangeFromPoint) { // webkit/chrome
    range = document.caretRangeFromPoint(e.clientX, e.clientY);
    // elemento DOM
    // range.startElement
    // posição
    // range.startOffset
  } else if (document.caretPositionFromPoint) { // gecko/firefox
    caret = document.caretPositionFromPoint(e.clientX, e.clientY);
    range = document.createRange();
    range.setStart(caret.offsetNode, caret.offset); // elemento DOM e posição
  }

  // obter palavra
  if (range) { // comum para chrome e firefox
    selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(range);
    selection.modify('move', 'backward', 'word');
    selection.modify('extend', 'forward', 'word');
    alert(selection.toString());
    selection.collapse(selection.anchorNode, 0);
  } else if (document.body.createTextRange) {
    // tudo diferente para o internet explorer
    // obter palavra
    range = document.body.createTextRange();
    range.moveToPoint(event.clientX, event.clientY);
    range.select();
    range.expand('word');
    alert(range.text);
    // elemento DOM
    // range.parentElement();
    // posição
    // var range_copy = range.duplicate();
    // range_copy.moveToElementText(range.parentElement());
    // range_copy.setEndPoint('EndToEnd', range);
    // range_copy.text.length
  }

}, false);
div {margin: 15px 0;}
p {margin: 0;}
<div>
  <p>Cras vel erat sit amet eros posuere volutpat nec in massa. Quisque dignissim mollis aliquet.</p>
  <p>Fusce suscipit rhoncus mi a dapibus. Donec nisl augue, molestie sed porttitor id, pulvinar tempor neque.</p>
  <p>Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec et enim eleifend velit faucibus consequat vel at risus.</p>
</div>
<div>
  <p>Aliquam erat volutpat.</p>
  <p>Phasellus ornare feugiat convallis. Aenean tincidunt tristique sem eu porttitor. Aliquam convallis eu purus et venenatis.</p>
  <p>Suspendisse euismod ullamcorper odio, ac sollicitudin quam pharetra sed. Vestibulum dictum cursus sollicitudin.</p>
  <p>Praesent at odio nisi. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris sodales vehicula neque quis imperdiet. Morbi lacinia libero in posuere porttitor. Curabitur pretium vel tortor in aliquet.</p>
</div>
<div>
  <p>Integer eu sapien odio. Morbi blandit nibh leo, in dapibus sem malesuada vitae. Etiam sit amet tristique sem.</p>
  <p>Sed mattis lectus lorem, at dapibus leo suscipit quis. Nunc in massa quis mauris suscipit gravida. Sed at nunc mauris. Duis et lectus ex.</p>
</div>

sources (in English):

Browser other questions tagged

You are not signed in. Login or sign up in order to post.