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:
document.addEventListener('keypress', function(e) {
jumpToID();
}, false);
function jumpToID() {
var id = "jumphere2374657";
var text = "<span id='" + id + "'> </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.
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
来源:https://stackoverflow.com/questions/12737272/removechild-sometimes-removes-entire-span-and-sometimes-doesnt