get word in given coordinate (x, y)

5

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

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

The closest I got was to use the document.elementFromPoint(x,y) function, which returns the entire paragraph.

Below the test code. I'm using the click event of the mouse 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>
    
asked by anonymous 05.08.2015 / 21:34

2 answers

0

I found the solution based on on the SOen question of the @MaiconCarraro response.

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

For my problem it was the best solution because it does not change the DOM (like the solution with <span> ) and it is not necessary to iterate several elements. The downside is the difference between browsers, but in my case just need to meet webkit.

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

In the code snippet below, I have kept the function of getting the whole word, in addition to getting the DOM element and the position of the text (it should work in upgraded 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:

07.08.2015 / 17:02
1

If you do not want to adopt the solution that @ Sanction indicated, there is this code here that worked perfectly here in Chrome.

Source: link

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>
    
06.08.2015 / 17:00