0

I'm new to HTML/JS and I'm trying to make a text editor as a small project. Please forgive me if I'm not explaining my thoughts clearly.

Let's say I have the following text in my contenteditable div environment, and let | represent the cursor (as it would look in most editors):

hi this is some text
here is some mor|e text
hello, world!

How would I be able to return the text "here is some more text"?

I'm using jQuery and I was thinking I want to use the onClick handler, but that doesn't respond to the arrow keys being used to navigate. What kind of event handler would I need? So far, I've parsed the text to replace the div separators, but I'm a bit lost on how to proceed. What would you suggest doing? (General links/advice also work, I'm trying to learn more through this project)

Edit, here's my html:

<div id="editor" class="editor" contenteditable="true"></div>

here's the JS:

$(document).on('keydown', '.editor', function(e){
    //detect 'tab' key
    if(e.keyCode == 9){
        //add tab
        document.execCommand('insertHTML', false, '&#009');
        //prevent focusing on next element
        e.preventDefault()
    }
    var text = $("#editor").html();

    console.log("MYLITERAL:" + text);
    // parse the string :)
    // for the div tags, replacing them with \n
    var tmp = text.replace(/<div>/g, "");
    tmp = tmp.replace(/<\/div>/g, "");
    tmp = tmp.replace(/<br>/g, "\n");
    console.log(tmp);
    document.getElementById("demo").innerHTML = tmp;
});
Froggos
  • 103
  • 6
  • 2
    you should display your html.code. – Frenchy Feb 27 '21 at 08:15
  • 1
    There seem to be plenty of relevant questions right here on SO, so I'd suggest try searching harder :-) Some examples: https://stackoverflow.com/questions/7745867/how-do-you-get-the-cursor-position-in-a-textarea, https://stackoverflow.com/questions/12553025/getting-line-number-in-text-area, https://stackoverflow.com/questions/9185630/find-out-the-line-row-number-of-the-cursor-in-a-textarea, https://stackoverflow.com/questions/3153995/find-value-of-current-line-of-a-textarea-using-javascript – Don't Panic Feb 27 '21 at 12:44
  • @Frenchy did that! – Froggos Feb 27 '21 at 23:05

1 Answers1

0

You can try the following:

var strO, endO, lstEle;

function selectRange(start, end, this_){
    var el = this_,sPos = start,ePos = end;
    var charIndex = 0, range = document.createRange();
    range.setStart(el, 0);
    range.collapse(true);
    var nodeStack = [el], node, foundStart = false, stop = false;
    while (!stop && (node = nodeStack.pop())) {
        if (node.nodeType == 3) {
            var nextCharIndex = charIndex + node.length;
            if (!foundStart && sPos >= charIndex && sPos <= nextCharIndex) {
                range.setStart(node, sPos - charIndex);
                foundStart = true;
            }
            if (foundStart && ePos >= charIndex && ePos <= nextCharIndex) {
                range.setEnd(node, ePos - charIndex);
                stop = true;
            }
            charIndex = nextCharIndex;
        } else {
            var i = node.childNodes.length;
            while (i--) {
                nodeStack.push(node.childNodes[i]);
            }
        }
    }
    var s = (window.getSelection) ? window.getSelection() : document.selection;
    if(window.getSelection) {
        s.removeAllRanges();
        s.addRange(range);
    } else {
        range.select();
    }
}

// node_walk: walk the element tree, stop when func(node) returns false
function node_walk(node, func) {
  var result = func(node);
  for(node = node.firstChild; result !== false && node; node = node.nextSibling){
    result = node_walk(node, func);
    lstEle = node;
  }
  return result;
};

// getCaretPosition: return [start, end] as offsets to elem.textContent that
//   correspond to the selected portion of text
//   (if start == end, caret is at given position and no text is selected)
function getCaretPosition(elem) {
    
    strO = 0, endO = 0, lstEle = elem;
    
  var sel = window.getSelection();
  var cum_length = [0, 0];

  if(sel.anchorNode == elem)
    cum_length = [sel.anchorOffset, sel.extentOffset];
  else {
    var nodes_to_find = [sel.anchorNode, sel.extentNode];
    if(!elem.contains(sel.anchorNode) || !elem.contains(sel.extentNode))
      return undefined;
    else {
      var found = [0,0];
      var i;
      node_walk(elem, function(node) {
        for(i = 0; i < 2; i++) {
          if(node == nodes_to_find[i]) {
            found[i] = true;
            if(found[i == 0 ? 1 : 0])
              return false; // all done
          }
        }

        if(node.textContent && !node.firstChild) {
          for(i = 0; i < 2; i++) {
            if(!found[i])
              cum_length[i] += node.textContent.length;
          }
        }
      });
      strO = cum_length[0];
      endO = strO + lstEle.textContent.length;
      cum_length[0] += sel.anchorOffset;
      cum_length[1] += sel.extentOffset;
    }
  }
  if(cum_length[0] <= cum_length[1])
    return cum_length;
  return [cum_length[1], cum_length[0]];
}

var update = function() {
    $('#test').html(getCaretPosition(this)+' '+strO+' '+endO);
    selectRange(strO, endO, this);
    $('#test').append('<br>'+window.getSelection().toString());
};
$('#editor').on("mouseup keydown keyup", update);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div id="editor" class="editor" contenteditable="true">hi this is some text<br>here is some more text<br>hello, world!</div>
<div id="test">test</div>