The Range instance returned by various Selection methods – most commonly sindow.getSelection().getRangeAt(0) – does not provide enough information to determine the total offset of the caret (or selection start/end). Here’s how:

var SelectionUtils = {};

/**
 * Returns an object with 2 properties: a boolean value indicating if #child was found in the currently iterating
 * set of child nodes, and an integer value representing how many characters of textContent were examined in that same
 * iteration.
 * @private
 * @param {Node} child
 * @param {Node} container
 * @returns {{offset: number, found: boolean}}
 */
SelectionUtils.getCharacterCountToNodeRecursive = function(child, container){
  var response = {
    offset : 0,
    found : false
  };
  var current = container.firstChild;
  loop: while(current){
    if(current == child){
      response.found = true;
      break;
    }
    switch(current.nodeType){
      case 3 :
        response.offset += current.textContent.length;
        break;
      case 1 :
        var childResponse = arguments.callee(child, current);
        response.offset += childResponse.offset;
        if(childResponse.found){
          response.found = true;
          break loop;
        }
        break;
    }
    current = current.nextSibling;
  }
  return response;
};

/**
 * Returns the number of characters of text content preceding #child, in #container.
 * @param {Node} child
 * @param {Node} container
 * @returns {number}
 */
SelectionUtils.getCharacterCountToNode = function(child, container){
  container = container || document.body;
  if(!container.contains(child)){
    throw new Error('child must be descendant of container');
  }
  return SelectionUtils.getCharacterCountToNodeRecursive(child, container).offset;
};

/**
 * Convenience method to count the number of characters of text content to the start of the current selection.
 * @param range
 * @param container
 * @returns {number}
 */
SelectionUtils.getSelectionStartOffset = function(range, container){
  return range.startOffset + SelectionUtils.getCharacterCountToNode(range.startContainer, container);
};

/**
 * Convenience method to count the number of characters of text content to the end of the current selection.
 * @param range
 * @param container
 * @returns {number}
 */
SelectionUtils.getSelectionEndOffset = function(range, container){
  return range.endOffset + SelectionUtils.getCharacterCountToNode(range.endContainer, container);
};

POC: https://jsfiddle.net/moagrius/L1pwhmae/