Get contentEditable caret index position

后端 未结 10 669
暗喜
暗喜 2020-11-22 05:48

I\'m finding tons of good, crossbrowser anwers on how to SET the cursor or caret index position in a contentEditable element, but none on how to GET or find its

相关标签:
10条回答
  • 2020-11-22 06:15

    A straight forward way, that iterates through all the chidren of the contenteditable div until it hits the endContainer. Then I add the end container offset and we have the character index. Should work with any number of nestings. uses recursion.

    Note: requires a poly fill for ie to support Element.closest('div[contenteditable]')

    https://codepen.io/alockwood05/pen/vMpdmZ

    function caretPositionIndex() {
        const range = window.getSelection().getRangeAt(0);
        const { endContainer, endOffset } = range;
    
        // get contenteditableDiv from our endContainer node
        let contenteditableDiv;
        const contenteditableSelector = "div[contenteditable]";
        switch (endContainer.nodeType) {
          case Node.TEXT_NODE:
            contenteditableDiv = endContainer.parentElement.closest(contenteditableSelector);
            break;
          case Node.ELEMENT_NODE:
            contenteditableDiv = endContainer.closest(contenteditableSelector);
            break;
        }
        if (!contenteditableDiv) return '';
    
    
        const countBeforeEnd = countUntilEndContainer(contenteditableDiv, endContainer);
        if (countBeforeEnd.error ) return null;
        return countBeforeEnd.count + endOffset;
    
        function countUntilEndContainer(parent, endNode, countingState = {count: 0}) {
          for (let node of parent.childNodes) {
            if (countingState.done) break;
            if (node === endNode) {
              countingState.done = true;
              return countingState;
            }
            if (node.nodeType === Node.TEXT_NODE) {
              countingState.count += node.length;
            } else if (node.nodeType === Node.ELEMENT_NODE) {
              countUntilEndContainer(node, endNode, countingState);
            } else {
              countingState.error = true;
            }
          }
          return countingState;
        }
      }
    
    0 讨论(0)
  • 2020-11-22 06:16
    function getCaretPosition() {
        var x = 0;
        var y = 0;
        var sel = window.getSelection();
        if(sel.rangeCount) {
            var range = sel.getRangeAt(0).cloneRange();
            if(range.getClientRects()) {
            range.collapse(true);
            var rect = range.getClientRects()[0];
            if(rect) {
                y = rect.top;
                x = rect.left;
            }
            }
        }
        return {
            x: x,
            y: y
        };
    }
    
    0 讨论(0)
  • 2020-11-22 06:18

    $("#editable").on('keydown keyup mousedown mouseup',function(e){
    		   
           if($(window.getSelection().anchorNode).is($(this))){
        	  $('#position').html('0')
           }else{
             $('#position').html(window.getSelection().anchorOffset);
           }
     });
    body{
      padding:40px;
    }
    #editable{
      height:50px;
      width:400px;
      border:1px solid #000;
    }
    #editable p{
      margin:0;
      padding:0;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
    <div contenteditable="true" id="editable">move the cursor to see position</div>
    <div>
    position : <span id="position"></span>
    </div>

    0 讨论(0)
  • 2020-11-22 06:21

    Try this:

    Caret.js Get caret postion and offset from text field

    https://github.com/ichord/Caret.js

    demo: http://ichord.github.com/Caret.js

    0 讨论(0)
  • 2020-11-22 06:25

    The following code assumes:

    • There is always a single text node within the editable <div> and no other nodes
    • The editable div does not have the CSS white-space property set to pre

    If you need a more general approach that will work content with nested elements, try this answer:

    https://stackoverflow.com/a/4812022/96100

    Code:

    function getCaretPosition(editableDiv) {
      var caretPos = 0,
        sel, range;
      if (window.getSelection) {
        sel = window.getSelection();
        if (sel.rangeCount) {
          range = sel.getRangeAt(0);
          if (range.commonAncestorContainer.parentNode == editableDiv) {
            caretPos = range.endOffset;
          }
        }
      } else if (document.selection && document.selection.createRange) {
        range = document.selection.createRange();
        if (range.parentElement() == editableDiv) {
          var tempEl = document.createElement("span");
          editableDiv.insertBefore(tempEl, editableDiv.firstChild);
          var tempRange = range.duplicate();
          tempRange.moveToElementText(tempEl);
          tempRange.setEndPoint("EndToEnd", range);
          caretPos = tempRange.text.length;
        }
      }
      return caretPos;
    }
    #caretposition {
      font-weight: bold;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <div id="contentbox" contenteditable="true">Click me and move cursor with keys or mouse</div>
    <div id="caretposition">0</div>
    <script>
      var update = function() {
        $('#caretposition').html(getCaretPosition(this));
      };
      $('#contentbox').on("mousedown mouseup keydown keyup", update);
    </script>

    0 讨论(0)
  • 2020-11-22 06:29

    Kinda late to the party, but in case anyone else is struggling. None of the Google searches I've found for the past two days have come up with anything that works, but I came up with a concise and elegant solution that will always work no matter how many nested tags you have:

    function cursor_position() {
        var sel = document.getSelection();
        sel.modify("extend", "backward", "paragraphboundary");
        var pos = sel.toString().length;
        if(sel.anchorNode != undefined) sel.collapseToEnd();
    
        return pos;
    }
    
    // Demo:
    var elm = document.querySelector('[contenteditable]');
    elm.addEventListener('click', printCaretPosition)
    elm.addEventListener('keydown', printCaretPosition)
    
    function printCaretPosition(){
      console.log( cursor_position(), 'length:', this.textContent.trim().length )
    }
    <div contenteditable>some text here <i>italic text here</i> some other text here <b>bold text here</b> end of text</div>

    It selects all the way back to the beginning of the paragraph and then counts the length of the string to get the current position and then undoes the selection to return the cursor to the current position. If you want to do this for an entire document (more than one paragraph), then change paragraphboundary to documentboundary or whatever granularity for your case. Check out the API for more details. Cheers! :)

    0 讨论(0)
提交回复
热议问题