1

I have this html: <span>some words here</span>. When that span is clicked it turns into a textbox with that string in it. Is there a way to find out which character the click occurred at and put the cursor at that point in the string once the textbox with the string appears?

chromedude
  • 4,246
  • 16
  • 65
  • 96
  • You could programmatically surround each character with `span`s of their own, then either apply event listeners to each or use `document.getElementFromPoint`. – Waleed Khan Jun 07 '12 at 01:42
  • What you're looking for is a variation of [setting a cursor position in Javascript](http://stackoverflow.com/questions/512528/set-cursor-position-in-html-textbox) combined with an [introduction to `range`s](http://www.quirksmode.org/dom/range_intro.html). Note, the Quirksmode link is a bit old, but it gives a good overview. Refer to MDN for more updated information. – Jared Farrish Jun 07 '12 at 02:01
  • 1
    Here is a simple demo I've only tested in Firefox 12: http://jsfiddle.net/sQduu/ Browser support varies, so unless you are only targeting Firefox, you should only view that as a demo of what you could do. – Jared Farrish Jun 07 '12 at 02:30

1 Answers1

2

Here's a quick Fiddle (tested in Chrome) that works for a <span> that includes line breaks, which the above solution choked on:

http://jsfiddle.net/x2dLW/1/

Summarized below:

function swapArea(baseEl) {
  // Display <textarea> element, hide the <span>
}

function getCursorLoc (nodes, range) {
  var prevChars = 0;
  var clickContent = range.startContainer.textContent;
  var clickLine;

  //iterate backwards through nodes constituting <span> element contents
  for (x = nodes.length - 1; x >= 0; x--) {
    node = nodes[x];

    if (clickContent == node.textContent) {
      clickLine = node; //this is the line user clicked in
    }
    else if (clickLine && (node.nodeName == "#text") ) {
      //sum up length of text lines prior, +1 for the NewLine
      prevChars += node.textContent.length + 1;
    }
  }

  //return offset in line clicked + sum length of all previous lines' content
  return range.startOffset + prevChars;
}

function replaceAndSet(e) {
    //Capture the click target as Selection(), convert to Range();
    var userRange = window.getSelection().getRangeAt();

    newArea = swapArea(source);

    //spanLines holds siblings (nodeName #Text or <BR>) constituting <span> contents
    var spanLines = userRange.startContainer.parentElement.childNodes;
    var clickChar = getCursorLoc(spanLines, userRange);

    newArea.focus();
    newArea.setSelectionRange(clickChar, clickChar);    
}

var source = someSpan; //the span user clicks on
source.onclick = replaceAndSet;
Jared Farrish
  • 48,585
  • 17
  • 95
  • 104
BooDooPerson
  • 316
  • 1
  • 9
  • 1
    Keep in mind, you don't have to and should not ask for upvotes in your answer. Keep it straight-forward and just about the answer (within reason). You'll earn the privilege to comment soon, which will also help. But do not ask for upvotes. `:)` You'll get them if it's a quality answer (in most cases). – Jared Farrish Jun 07 '12 at 22:28