removeChild sometimes removes entire span and sometimes doesn't

I’m working on a rich text editor for iOS and have most of it working but running into endless problems ensuring that the cursor is visible in the viewport when the user starts typing.

I came up with a novel approach: insert a span at the cursor position, scroll to the span, and then remove it. (I haven’t gotten to only scrolling if the span is on-screen.) Here’s what I wrote:

  • event.touches is always undefined
  • Preventing default iOS webpage drag with javascript and hammerJS
  • ES6 on JavaScriptCore and React Native
  • Javascript - iPad Tab Key Detection w/ Bluetooth Keyboard
  • iOS: Browser crashes due to low memory
  • How to run a background JavaScript in Phonegap on iPhone - for timer/stopwatch?
  • document.addEventListener('keypress', function(e) {            
       jumpToID();
    }, false);
    
    function jumpToID() {
      var id = "jumphere2374657";
      var text = "<span id='" + id + "'>&nbsp;</span>"
      document.execCommand('insertHTML', false, text);
      var element = document.getElementById(id);
      element.scrollIntoView();
      element.parentNode.removeChild(element);
    }
    

    In some cases this works just fine and in some cases it leaves a non-break space between every key press, removing the <span></span> tags only. Any ideas? I’m open to better ways of doing this if someone has suggestions. I’m a little shocked at how hard it is to make the cursor appear but then JS is new to me.

    EDIT

    This is the code that works:

    var viewportHeight = 0;
    
    function setViewportHeight(vph) {
      viewportHeight = vph;
      if(viewportHeight == 0 && vph != 0)
        viewportHeight = window.innerHeight;
    }
    
    function getViewportHeight() {
      if(viewportHeight == 0)
        return window.innerHeight;
      return viewportHeight;
    }
    
    function makeCursorVisible() {
      var sel = document.getSelection();                  // change the selection
      var ran = sel.getRangeAt(0);                        // into a range
      var rec = ran.getClientRects()[0];                  // that we can get coordinates from
      if (rec == null) {
        // Can't get coords at start of blank line, so we
        // insert a char at the cursor, get the coords of that,
        // then delete it again. Happens too fast to see.
        ran.insertNode( document.createTextNode(".") );
        rec = ran.getClientRects()[0];  // try again now that there's text
        ran.deleteContents();
      }
      var top = rec.top;               // Y coord of selection top edge
      var bottom  = rec.bottom;        // Y coord of selection bottom edge
      var vph = getViewportHeight();
      if (top < 0)      // if selection top edge is above viewport top,
        window.scrollBy(0, top);  // scroll up by enough to make the selection top visible
      if (bottom >= vph)   // if selection bottom edge is below viewport bottom,
        window.scrollBy(0, bottom-vph + 1); // scroll down by enough to make the selection bottom visible
    }
    

    The viewportHeight is more complicated than need be for a web app. For a mobile app we need to account for the keyboard so offer a method for setting the viewportHeight manually as well as the automatic setting from the window.innerHeight.

  • How to open a web page inside an iOS app with phonegap?
  • How to change viewport on iPhone/iPad?
  • How to open a HTTP server in react native
  • Phonegap with node.js
  • Hiding keyboard on mobile Chrome doesn't change window height
  • redirect to appstore or google play
  • One Solution collect form web for “removeChild sometimes removes entire span and sometimes doesn't”

    I don’t know if this will work on iOS, but if the position of the cursor means that there is a Selection at that point..

    function moveToSelection(){
        var sel = document.getSelection(), // change the selection
            ran = sel.getRangeAt(0),       // into a range
            rec = ran.getClientRects()[0], // that we can get co-ordinates from
            dy  = rec.top;                 // distance to move down/up
        window.scrollBy( 0, dy );          // actual move
    
        // console.log( sel, ran, rec, y );   // help debug
    }
    
    moveToSelection();
    

    Relevant links

    1. getSelection
    2. getRangeAt
    3. getClientRects
    4. scrollBy