Preventing page from scrolling on focus switching

I am new to mobile web/dev. My app is using jquery-mobile, phonegap and Compass (scss).

I have a problem on my login page :

logo and fields are contained in standard ‘div’ containers (data-role=”content” data-type=”vertical”). Background is colored.

when switching focus from login field to password field, the page slides up, which is what I don-t want to occur. I would like my logo and fields to stay in place, just like the Skype iOS App login page.

here is what happens :

bug description

I have tried several tricks, trying to block scroll events, or forcing page to scroll to 0,0, without success.

I am thinking about a new strategy now, maybe using top relative positioning for logo and fields, and catching focus events to scroll the page myself, on keyboard slide up (by animating top relative positioning coordinates).

Though this seems doable, I am wondering if this is the kind of work around used by the Skype iOS App team…

Any advice on technics used in this particular case is welcome!

cheers,

Fred

I think you want to call focus on the element directly and use the ‘preventScroll’ option.

el.focus({preventScroll: true});

$('#el').focus((e) => {
  e.preventDefault();
  e.target.focus({preventScroll: true});
})

https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus

I haven’t tested this yet, but does e.preventDefault() stop the issue? Generally you use e.preventDefault() to stop default scrolling / dragging behaviour.

$(document).bind('focus', function(e){
  e.preventDefault();
});

or better

$(element).bind('focus', function(e){
  e.preventDefault();
});

or

$(document).bind('touchstart', function(e){
  e.preventDefault();
});

Would an input field work better?

$(":input").live({
  focus: function(e) {
    e.preventDefault();
  }
});

I finally did not succeed in finding any coding solution to handle my problem, but I have noticed that a chirurgical positioning allowed me to frieze the screen when switching between fields.

Read More:   How best to make a link submit a form

The space available for the whole login screen must not exceed the space available once the keyboard is visible, that is 200 pixels. Further more, to prevent scrolling when switching field, the last input field center position must not exceed 100 pixels max. Using CSS it is possible to play on padding and margin to achieve the desired result.

It should be possible to get extra vertical space by removing the keyboard “fields navigation bar” but I don’t know how to do that neither :/

enter image description here

I had a similar issue (with the page scrolling on click/focus) and it took me a while to find the solution so I’m documenting it here.

I had a custom form field that when clicked would scroll the page to the top. Turns out that the input was being hidden using position: absolute; top: -99999999px; (because if you hide it properly then the focus events don’t work). This placed the hidden input at the top of the page. Then when we clicked the label and the input was focussed, the page was scrolled to the top.

I was searching for things like “Scroll to top on focus/click” and all sorts but I couldn’t find anything decent. The solution was to use right: -99999999px instead. I avoided using the typical left: -9999999px because apparently that can affect the site when using direction: rtl;

There is a preventScroll option for focus():
el.focus({preventScroll: true});

And if you have to support Browsers (IE11, Safari<14?) that do not support focus-options, use this polyfill:

!function(){

    let supported = false;
    document.createElement('i').focus({
        get preventScroll() {
            supported = true;
        },
    });
    if (!supported) {
        let original = HTMLElement.prototype.focus;
        Element.prototype.focus = HTMLElement.prototype.focus = function(options){
            if (options.preventScroll) {
                const map = new Map();
                let p = this;
                while (p = p.parentNode) map.set(p, [p.scrollLeft, p.scrollTop]);
                original.apply(this, arguments);
                map.forEach(function(pos, el){
                    // todo: test: only if changed? does it trigger scroll?
                    // IE flickers
                    el.scrollLeft = pos[0]
                    el.scrollTop  = pos[1]
                });
            } else {
                original.apply(this, arguments);
            }
        }
    }

}();

After searching for a few places, I haven’t seen the best solution for this problem. And I tried my own workaround which does work well for preventing scrolling to the focused element, so I’d like to share here.

Read More:   Lexical environment and function scope

The idea is I block the entire body with position: fixed when a user focuses on my targeted elements. And then, immediately, I remove that blocker with setTimeout (I’m leveraging Event Loop)

Here is my code example

Javascript version

function blockScroller(event) {
  const body = document.getElementsByTagName("body")[0]
  body.classList.add("scroller-blocker")

  setTimeout(() => {
    body.classList.remove("scroller-blocker")
  })
}
select {
  width: 200px;
  height: 55em;
  overflow: auto;
}

.scroller-blocker {
  position: fixed;
  height: 100%;
}
<br><br><br><br><br><br><br><br><br>
<select multiple onfocus="blockScroller(event)">
  <option selected="">Lorem</option>
  <option selected="">ipsum</option>
  <option selected="">dolor</option>
  <option selected="">sit</option>
  <option selected="">amet</option>
  <option selected="">Lorem</option>
  <option selected="">ipsum</option>
  <option selected="">dolor</option>
  <option selected="">sit</option>
  <option selected="">amet</option>
</select>

jQuery version

function blockScroller(event) {
  const body = $("body")
  body.addClass("scroller-blocker")

  setTimeout(() => {
    body.removeClass("scroller-blocker")
  })
}
select {
  width: 200px;
  height: 55em;
  overflow: auto;
}

.scroller-blocker {
  position: fixed;
  height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<br><br><br><br><br><br><br><br><br>
<select multiple onfocus="blockScroller(event)">
  <option selected="">Lorem</option>
  <option selected="">ipsum</option>
  <option selected="">dolor</option>
  <option selected="">sit</option>
  <option selected="">amet</option>
  <option selected="">Lorem</option>
  <option selected="">ipsum</option>
  <option selected="">dolor</option>
  <option selected="">sit</option>
  <option selected="">amet</option>
</select>

For the comparison, here is the version without the above workaround

select {
  width: 200px;
  height: 55em;
  overflow: auto;
}

.scroller-blocker {
  position: fixed;
  height: 100%;
}
<br><br><br><br><br><br><br><br><br>
<select multiple>
  <option selected="">Lorem</option>
  <option selected="">ipsum</option>
  <option selected="">dolor</option>
  <option selected="">sit</option>
  <option selected="">amet</option>
  <option selected="">Lorem</option>
  <option selected="">ipsum</option>
  <option selected="">dolor</option>
  <option selected="">sit</option>
  <option selected="">amet</option>
</select>


The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Similar Posts