0

I am trying to create a Google Chrome extension where a user would highlight text on a page, and a happy face will appear at the beginning of the text. I have found this website where it demonstrates how to store and retrieve a range. My question is how do I get the start of the range to insert my < div id="happyFace">?

var selection = storeSelection(window.getSelection());
var selectionRange = restoreSelection(selection);
console.log( selectionRange.startContainer.offsetLeft ); //undefined


function makeXPath (node, currentPath) {
  /* this should suffice in HTML documents for selectable nodes, XML with namespaces needs more code */
  currentPath = currentPath || '';
  switch (node.nodeType) {
    case 3:
    case 4:
      return makeXPath(node.parentNode, 'text()[' + (document.evaluate('preceding-sibling::text()', node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength + 1) + ']');
    case 1:
      return makeXPath(node.parentNode, node.nodeName + '[' + (document.evaluate('preceding-sibling::' + node.nodeName, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength + 1) + ']' + (currentPath ? '/' + currentPath : ''));
    case 9:
      return '/' + currentPath;
    default:
      return '';
  }
}

function storeSelection (selectionObject) {
  if (typeof window.getSelection != 'undefined') {
    var selection = selectionObject;
    var range = selection.getRangeAt(0);
    if (range != null) {
       selectionObject = makeXPath(range.startContainer) + '|' + range.startOffset + '|' + makeXPath(range.endContainer) + '|' + range.endOffset;
       return selectionObject
    }
  }
}

function restoreSelection (selectionObject) {
  var selectionDetails = selectionObject;
  if (selectionDetails != null) {
    selectionDetails = selectionDetails.split(/\|/g);
    if (typeof window.getSelection != 'undefined') {
      var range = document.createRange();
      range.setStart(document.evaluate(selectionDetails[0], document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue, Number(selectionDetails[1]));
      range.setEnd(document.evaluate(selectionDetails[2], document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue, Number(selectionDetails[3]));
      return range;
    }
  }
}
Jon
  • 2,249
  • 6
  • 26
  • 30

1 Answers1

1

Pretty simple since you're only worrying about Chrome:

// Get the range
var range = document.getSelection().getRangeAt(0);
// Collapse it to the start
range.collapse(true);
// insert your node
range.insertNode(node);

Here is a working example: http://jsfiddle.net/L5Qqk/. Note that it keeps moving the happy face because I'm always adding the same node. If you create a new node each time then multiple happy faces would be added.

Hemlock
  • 6,130
  • 1
  • 27
  • 37
  • Is there a way to get the x/y position of the node? I plan to lay a
    over top of the existing content of the website.
    – Jon Mar 19 '12 at 03:43
  • `node.offsetLeft` and `node.offsetTop` – Hemlock Mar 19 '12 at 11:31
  • I tried console.log( selectionRange.startContainer.offsetLeft ), but I kept on getting the error 'undefined'. I have included my code. – Jon Mar 19 '12 at 13:57
  • 1
    @user1277607: Most likely the range start is inside a text node, which does not have `offsetLeft` and `offsetTop` properties. In Chrome you can use the `getClientRects()` method of the selection range to get the coordinates. See http://stackoverflow.com/a/6847328/96100 – Tim Down Mar 19 '12 at 17:20
  • That is why you should use `node.offsetLeft`. You can do that once it has been inserted at the start of the selection. – Hemlock Mar 19 '12 at 17:21
  • Yes, so long as the OP is actually inserting the image in the DOM at the start of the selection range, but it sounds as though he/she is planning to overlay an absolutely positioned element instead. – Tim Down Mar 19 '12 at 17:25
  • @Tim You are right, it does seem that way now. This question is a moving target. – Hemlock Mar 19 '12 at 18:09
  • Sorry that I was unclear in my question. Thank you very much for your help though :) – Jon Mar 22 '12 at 15:06