javascripthtmldomcontenteditablerangy

Contenteditable DIV - how can I determine if the cursor is at the start or end of the content


I have a contenteditable div which contains typical wysiwyg editor html (bold, anchors, lists).

I need to determine if the current cursor is, onKeyDown, at the start and at the end of the div. The reason for this is, based on the cursor position, and the key pressed, I might want to merge this div with the previous div on a backspace, or create a new following div on enter.

I've been fiddling around with ranges, but when you're working with html inside the element, things get pretty complicated.

I'm hoping I must be overlooking some simple solution.

Is there a relatively simple way to determine this - I'm open to using a library like Rangy.

Thanks!

Edit: I'm thinking something along these lines:

$('.mycontenteditable').bind('keydown', handle_keydown)

handle_keydown = function(e) {

  range = window.getSelection().getRangeAt(0)
  start_range = document.createRange()
  start_range.selectNodeContents(this.firstChild)
  start_range.collapse(true) // collapse to start
  is_start = start_range.compareBoundaryPoints(Range.START_TO_START,range)
  end_range = document.createRange()
  end_range.selectNodeContents(this.lastChild)
  end_range.collapse(false)
  is_end = end_range.compareBoundaryPoints(Range.END_TO_END,range)
}

Am I going to run into any odd issues with something like this?


Solution

  • This is how I ended up solving this. My proposed solution above worked sometimes but there were way to many edge cases, so I ended up considering how much text was before or after the cursor, and if that was 0 characters, then I was at the start or end:

    handle_keydown = function(e) {
      // Get the current cusor position
      range = window.getSelection().getRangeAt(0)
      // Create a new range to deal with text before the cursor
      pre_range = document.createRange();
      // Have this range select the entire contents of the editable div
      pre_range.selectNodeContents(this);
      // Set the end point of this range to the start point of the cursor
      pre_range.setEnd(range.startContainer, range.startOffset);
      // Fetch the contents of this range (text before the cursor)
      this_text = pre_range.cloneContents();
      // If the text's length is 0, we're at the start of the div.
      at_start = this_text.textContent.length === 0;
      // Rinse and repeat for text after the cursor to determine if we're at the end.
      post_range = document.createRange();
      post_range.selectNodeContents(this);
      post_range.setStart(range.endContainer, range.endOffset);
      next_text = post_range.cloneContents();
      at_end = next_text.textContent.length === 0;
    }
    

    Still not entirely sure there are any other edge cases, as I'm not entirely sure how to unit test this, since it requires mouse interaction - there's probably a library to deal with this somewhere.