rangy icon indicating copy to clipboard operation
rangy copied to clipboard

loading of TextRange module can cause scrolling on mobile devices

Open jonbrooks opened this issue 9 years ago • 7 comments

In our iOS app, the loading of the TextRange module causes the page to scroll all the way to the end. This doesn't seem to happen on the first load of the page (perhaps the dom isn't ready yet), but will happen on subsequent page loads within the webview.

The reason is the games TextRange is playing at init time to detect browser behavior regarding collapsing of spaces. In particular, adding a contentEditable element to the end of the body and selecting it:

var el = document.createElement("div");
    el.contentEditable = "true";
    el.innerHTML = "<p>1 </p><p></p>";
    var body = getBody(document);
    var p = el.firstChild;
    var sel = api.getSelection();

    body.appendChild(el);
    sel.collapse(p.lastChild, 2);

At the linesel.collapse(p.lastChild, 2); iOS will scroll its webview to the end and show the on-screen keyboard. Since it immediately removes focus, the keyboard doesn't show, but the scroll has happened.

Would it achieve the same result to do these test with just ranges rather than the browser's selection API?

jonbrooks avatar Mar 13 '15 23:03 jonbrooks

Sadly not. Range behaviour is well-defined and pretty much consistent between browsers; selection behaviour varies significantly, and the behaviour being tested affects how the TextRange module moves the caret when Selection.move() is called, for which it needs to know how the native browser selection behaves.

I will give this some thought and get back to you.

timdown avatar Mar 19 '15 00:03 timdown

I've changed the feature test to work on an element inserted at the start of the body rather than the end. Hopefully this will fix the issue.

timdown avatar Mar 19 '15 23:03 timdown

Thanks. That will certainly improve the situation. Probably potentially still a problem if a url with a mid-page anchor is loaded, which unfortunately is a use case in our app. I'll see how this fix performs. Thanks again for the patch and for the library in general.

jonbrooks avatar Mar 20 '15 03:03 jonbrooks

This change does indeed seem to fix the scrolling issue in all cases, even when loading an anchor in the middle of the page. However, I'm now seeing the keyboard begin to animate in then back out. I don't know if it's due to this change or if I just hadn't noticed it before.

This may not be relevant to your typical web developer, but as a native mobile developer, a solution that would work well for me would be an option to supply TextRange with info on how the browser will behave rather than making it retest on every page load. Then I could just test once (even with a hidden webview), and from then on, just supply rangy with the results of that test. I'll think about this some more.

Thanks again for the library, and for this patch. It's definitely an improvement.

jonbrooks avatar Mar 23 '15 16:03 jonbrooks

I think that's a good idea and I will implement it soon. However, I'm intending to release 1.3 before doing that. I could keep tinkering for ever otherwise.

timdown avatar Apr 05 '15 10:04 timdown

Looks like the scrolling issue also happens in Edge/IE - inserting the test elements at the start of the body is causing the browser to scroll to the top while inserting them at the end was causing a scroll to the bottom.

claire-lee avatar Jun 06 '16 14:06 claire-lee

This also happens now in Firefox. We added a workaround to avoid this, but it is a hack...

         var trailingSpaceBeforeLineBreakInPreLineCollapses = true;

         (function() {
+            // HACK: Explicitly setting values for IE/Edge/Firefox to prevent window
+            //       from scrolling to the top on testing collapsing - remove if possible
+            //       https://github.com/timdown/rangy/issues/292
+            var excludedBrowsers = new RegExp("MSIE |Trident/|Edge|Firefox/");
+            if (excludedBrowsers.test(window.navigator.userAgent)){
+              trailingSpaceInBlockCollapses = false;
+              trailingSpaceBeforeBrCollapses = false;
+              trailingSpaceBeforeBlockCollapses = false;
+              return;
+            }
+
             var el = dom.createTestElement(document, "<p>1 </p><p></p>", true);
             var p = el.firstChild;
             var sel = api.getSelection();

francis avatar Mar 01 '18 16:03 francis