144

I'm working on a browser based app, currently I'm developing and styling for the ipad safari browser.

I'm looking for two things on the ipad: How can I disable vertical scrolling for pages that don't require it? & how can I disable the elastic bounce effect?

Whitewall
  • 597
  • 1
  • 7
  • 18
Adam
  • 2,632
  • 8
  • 34
  • 60
  • refer to the following answer only one that worked for me and is current (https://stackoverflow.com/a/51176339/1979180) – frage Jan 10 '19 at 00:41

23 Answers23

158

This answer is no longer state of the art, unless you are developing for a very old iOS device... Please see other solutions


2011 answer: For a web/html app running inside iOS Safari you want something like

document.ontouchmove = function(event){
    event.preventDefault();
}

For iOS 5 you may want to take the following into account: document.ontouchmove and scrolling on iOS 5

Update September 2014: A more thorough approach can be found here: https://github.com/luster-io/prevent-overscroll. For that and a whole lot of useful webapp advice, see http://www.luster.io/blog/9-29-14-mobile-web-checklist.html

Update March 2016: That last link is no longer active - see https://web.archive.org/web/20151103001838/http://www.luster.io/blog/9-29-14-mobile-web-checklist.html for the archived version instead. Thanks @falsarella for pointing that out.

Community
  • 1
  • 1
Oskar Austegard
  • 4,599
  • 4
  • 36
  • 50
  • 1
    I have webapp running inside iOS Safari and I have tried disabling ontouchmove event but it's still possible to see bounce effect if done carefully. (iOS 5) – Nilesh Mar 02 '12 at 10:18
  • @Nilesh: document.ontouchmove is *the* way to do this in iOS Safari. It is possible, however that some element in your dom is stopping the event propagation from reaching the document element - see http://stackoverflow.com/questions/7798201/document-ontouchmove-and-scrolling-on-ios-5 – Oskar Austegard Mar 13 '12 at 14:19
  • @Nilesh: You might give the ontouchcancel event a go: – mikermcneil Nov 19 '12 at 10:06
  • 7
    What about just disabling the bounce effect...? – Yuval A. Nov 29 '12 at 18:14
  • 8
    The problem with this approach is, that scrollable divs in your dom won't scroll anymore. Depending on your dom setup, there are ways around this. – huesforalice Mar 04 '14 at 14:31
  • @huesforalice What are these ways around this that you mention? Is there some way to trigger at which element you want to re-enable the listening? – Michael Ridgway Mar 16 '14 at 06:52
  • Basically what I did was to have a prevent default on the main div inside the body tag. Then I still could have the events on child divs with overflow:scroll correctly scroll. It's a bit fiddly at times to get it completely right, but that approach should work. – huesforalice Mar 17 '14 at 08:06
  • @huesforalice, yes. But scrollable divs are troublesome in iOS in any case - or were at least. We used iScroll (http://iscrolljs.com/) to solve this, which worked great. (Caveat - this was 2011, contents may have shifted during flight) – Oskar Austegard Mar 17 '14 at 17:54
  • @OskarAustegard thanks for the tip Oskar. I'll look into it the next time I face that problem. – huesforalice Mar 18 '14 at 11:02
  • Good one but it completely disables scrolling. what if you have a text box and user touches the text box to open the keyboard (which covers half of the screen height)? you cannot navigate anywhere unless you close the keyboard. – Bakhshi Jul 30 '14 at 07:02
  • 4
    There's a bunch of people here asking about scrolling divs while using this so.. to make those divs scrollable, add `onTouchMove: function(e) { e.stopPropagation(); e.stopImmediatePropagation(); }` It'll prevent the event from hitting the body, but the body will remain non-bouncy. Edit: This is assuming you set $('div').on('touchmove', ...onTouchMove); – Matt Kenefick Aug 08 '14 at 06:56
  • Hi @MattKenefick could you please provide jsfiddle link for the same? – Satyam Saxena Nov 16 '15 at 08:09
  • 1
    @OskarAustegard The last link didn't work for me, but I could find the content on web archive: http://web.archive.org/web/20151103001838/http://www.luster.io/blog/9-29-14-mobile-web-checklist.html – falsarella Mar 02 '16 at 14:17
  • @OskarAustegard It's considered a bad practice to solely provide links in SO answers. Please consider adding the solution itself to the answer. – Slava Fomin II Mar 21 '17 at 13:55
  • @SlavaFominII - really - you're gonna harp on a 3 year old answer that addresses functionality that is long since changed? – Oskar Austegard Mar 21 '17 at 17:10
  • You should see the [Christopher Vickers answer]( https://stackoverflow.com/a/49853392/2294168) ! You need to use `addEventListener('touchmove', (e) => {e.preventDefault()}, { passive: false })` – Ser Apr 18 '18 at 16:17
  • I've no idea why you consider this no longer applicable - it's 2023, tested on iPhone X (Latest iOS version) - works like a charm. – Glenn Carver May 05 '23 at 12:40
127

You can also change the position of the body/html to fixed:

body,
html {
  position: fixed;
}
Ben Bos
  • 2,351
  • 1
  • 19
  • 13
  • 2
    This actually worked very well on iPad iOS 8.2 Safari; no bounce effect anymore. – Akseli Palén Jun 05 '15 at 22:19
  • 3
    Best way to do that i've seen so far – hildende Jun 12 '15 at 16:48
  • 9
    Doesn't work if you want to place something with position: absolute and all sides set to 0 (to make a fullscreen div) - your whole document shrinks to nothing. – riv Oct 19 '15 at 18:44
  • This seems so logical after all because its the way it should work. I wonder how people come to such solitions haha. Upvoted! – David Fariña Oct 13 '16 at 09:41
  • I'm using a lot of flex boxes, and a lot of other div with position fixed inside the body, and this solution works very well, yet still allow scrolling on div with overflow: scroll effect. – Someone Special Nov 12 '16 at 10:46
  • 4
    The page jumps back to the top of the page for me when I use this and then I focus on an input. Do you know a way around this? – Julie Oct 18 '17 at 14:44
  • Works great on iOS 11 on iPhone and iPad. I love these little perfect fixes. One line! Ya can't beat that! Thanks. – B-Money Jan 18 '18 at 21:29
  • works on safari but on chrome iOS 66 there's the issue of "pull to refresh" [which you have to prevent](https://stackoverflow.com/a/42509310/128511) – gman May 15 '18 at 11:03
  • what is the work around for the problem of jumping back to the top of the page? – Renat Gatin Aug 10 '18 at 22:27
  • 3
    @riv adding width: 100%; height: 100% to both body and html helped me. – Tim Dec 13 '18 at 21:25
  • there seems to be an issue, if you scroll outside a scrollable inner window, then ios handles this as you scrolling the page (just you can't tell, as your fixed body is on top of that so you won't see the scrollbars). then there's a timeout until ios thinks you're done scrolling the whole page, until ios thinks that any inner scrollable items aren't scrollable as its still scrolling the whole page in the background. for that reason this isn't usable for me with an inner scrollable window. – fredrivett Nov 13 '20 at 17:44
  • Page can still scroll, you just don't see it happen. Touch move events will be randomly offset – Mingwei Samuel Mar 15 '21 at 04:13
  • This doesn't work with PWA – Maje Oct 07 '21 at 10:33
80

To prevent scrolling on modern mobile browsers you need to add the passive: false. I had been pulling my hair out getting this to work until I found this solution. I have only found this mentioned in one other place on the internet.

function preventDefault(e){
    e.preventDefault();
}

function disableScroll(){
    document.body.addEventListener('touchmove', preventDefault, { passive: false });
}
function enableScroll(){
    document.body.removeEventListener('touchmove', preventDefault);
}
Christopher Vickers
  • 1,773
  • 1
  • 14
  • 18
  • 7
    Without `{ passive: false }` it's definitely not working !!! Welcome on StackOverflow dude – Ser Apr 18 '18 at 16:14
  • 2
    I appreciate you for providing this solution! – Paul Z. May 29 '18 at 18:07
  • 6
    This is the correct answer. You can see the explanation here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener after Chrome 54 touchmove defaulted passive to true which means preventDefault calls would be ignored. That's why you must pass {passive: false}, so the preventDefault call is not ignored. – derickito Jun 07 '18 at 21:30
  • 2
    You’re declaring the function in JS - But how do you call this function on an element so it works as suggested? – ikwyl6 Jul 13 '19 at 18:30
  • 10
    This works great if you want to disable all kinds of scrolling. But what if I only want to disable scroll on body and keep possibility to scroll on an element with `overflow-y: scroll`? For example, having a modal with a taller view height than body. – Calsal Oct 09 '19 at 10:27
  • Correct, this solution does stop the rubber band effect but also kills a scroll of the content when it has overflow. Did anyone solve that? – Mattijs Oct 12 '21 at 23:42
7

You can use this jQuery code snippet to do this:

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

This will block the vertical scrolling and also any bounce back effect occurring on your pages.

Alessandro Incarnati
  • 7,018
  • 3
  • 38
  • 54
  • 13
    This is not Javascript, it's jQuery. You should mention that, not everybody is using that framework. – inta Feb 06 '14 at 09:08
  • how can i stop horizontal bounce or scroll – fidel castro Sep 13 '14 at 11:25
  • Horizontal scroll you could even disable it with just css, applying overflow-x:hidden; to your main container. I noticed in the latest versions of safari you cannot disable the bounce effect though. It's a native feature of the browser on mobile devices. – Alessandro Incarnati Sep 13 '14 at 16:11
  • 6
    @inta Just saying this actually is javascript. It's just not pure javascript. – Ben Lorantfy May 16 '17 at 23:59
7
overflow: scroll;
-webkit-overflow-scrolling: touch;

On container you can set bounce effect inside element

Source: http://www.kylejlarson.com/blog/2011/fixed-elements-and-scrolling-divs-in-ios-5/

user956584
  • 5,316
  • 3
  • 40
  • 50
5

css overscroll-behavior is now supported in iOS 16. If you are targeting > iOS 16 devices, to prevent elastic bounce effect, add the following CSS to the html root

html {
  overscroll-behavior: none;
}

Please note, the solution provided only disables elastic bounce effect when content is larger than viewport.

If you also want to completely disable scrolling in main page on iOS devices, use

html body {
   overflow: hidden;
}
Dr. DS
  • 984
  • 1
  • 13
  • 31
4

I know this is slightly off-piste but I've been using Swiffy to convert Flash into an interactive HTML5 game and came across the same scrolling issue but found no solutions that worked.

The problem I had was that the Swiffy stage was taking up the whole screen, so as soon as it had loaded, the document touchmove event was never triggered.

If I tried to add the same event to the Swiffy container, it was replaced as soon as the stage had loaded.

In the end I solved it (rather messily) by applying the touchmove event to every DIV within the stage. As these divs were also ever-changing, I needed to keep checking them.

This was my solution, which seems to work well. I hope it's helpful for anyone else trying to find the same solution as me.

var divInterval = setInterval(updateDivs,50);
function updateDivs(){
$("#swiffycontainer > div").bind(
    'touchmove',
     function(e) {
        e.preventDefault();
    }
);}
Tom
  • 41
  • 2
4

Code to To remove ipad safari: disable scrolling, and bounce effect

   document.addEventListener("touchmove", function (e) {
        e.preventDefault();
    }, { passive: false });

If you have canvas tag inside document, sometime it will affect the usability of object inside Canvas(example: movement of object); so add below code to fix it.

    document.getElementById("canvasId").addEventListener("touchmove", function (e) {
        e.stopPropagation();
    }, { passive: false });
3

none of the solutions works for me. This is how I do it.

  html,body {
      position: fixed;
      overflow: hidden;
    }
  .the_element_that_you_want_to_have_scrolling{
      -webkit-overflow-scrolling: touch;
  }
angry kiwi
  • 10,730
  • 26
  • 115
  • 161
  • I would do .the_element_that_you_want_to_have_scrolling{ scrolling: touch; } – Roy Segall Feb 19 '18 at 06:40
  • Your answer worked for me for the html,body but I cannot get "the_element_that_you_want_to_have_scrolling" to scroll. This element is nested inside a few divs that are under body. Does it matter what any of the typical element tags would be for this nested div that I want to have it scroll? I have position: relative. – ikwyl6 Feb 21 '20 at 02:41
  • This position fixed actually worked for me whilst the JS solution didn't because that killed the scroll of my stages are (sticky header and footer). Having an overflow-y: auto on my stage area allows for scroll so I don't need that webkit directive. – Mattijs Oct 12 '21 at 23:54
2

Try this JS sollutuion:

var xStart, yStart = 0; 

document.addEventListener('touchstart', function(e) {
    xStart = e.touches[0].screenX;
    yStart = e.touches[0].screenY;
}); 

document.addEventListener('touchmove', function(e) {
    var xMovement = Math.abs(e.touches[0].screenX - xStart);
    var yMovement = Math.abs(e.touches[0].screenY - yStart);
    if((yMovement * 3) > xMovement) {
        e.preventDefault();
    }
});

Prevents default Safari scrolling and bounce gestures without detaching your touch event listeners.

Andrii Verbytskyi
  • 7,155
  • 3
  • 47
  • 38
2

Tested in iphone. Just use this css on target element container and it will change the scrolling behaviour, which stops when finger leaves the screen.

-webkit-overflow-scrolling: auto

https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling

2

improved answer @Ben Bos and commented by @Tim

This css will help prevent scrolling and performance issue with css re-render because position changed / little lagging without width and height

html,
body {
  position: fixed;
  width: 100%; 
  height: 100%
}

auronsan zenx
  • 57
  • 1
  • 4
1

For those who are using MyScript the Web App and are struggling with the body scrolling/dragging (on iPad and Tablets) instead of actually writing:

<body touch-action="none" unresolved>

That fixed it for me.

Miro
  • 8,402
  • 3
  • 34
  • 72
1

You can use js for prevent scroll:

let body = document.body;

let hideScroll = function(e) {
  e.preventDefault();
};

function toggleScroll (bool) {

  if (bool === true) {
    body.addEventListener("touchmove", hideScroll);
  } else {
    body.removeEventListener("touchmove", hideScroll);
  }
}

And than just run/stop toggleScroll func when you opnen/close modal.

Like this toggleScroll(true) / toggleScroll(false)

(This is only for iOS, on Android not working)

Vlad Novikov
  • 369
  • 3
  • 11
1

Try this JS solution that toggles webkitOverflowScrolling style. The trick here is that this style is off, mobile Safari goes to ordinary scrolling and prevents over-bounce — alas, it is not able to cancel ongoing drag. This complex solution also tracks onscroll as bounce over the top makes scrollTop negative that may be tracked. This solution was tested on iOS 12.1.1 and has single drawback: while accelerating the scroll single over-bounce still happens as resetting the style may not cancel it immediately.

function preventScrollVerticalBounceEffect(container) {
  setTouchScroll(true) //!: enable before the first scroll attempt

  container.addEventListener("touchstart", onTouchStart)
  container.addEventListener("touchmove", onTouch, { passive: false })
  container.addEventListener("touchend", onTouchEnd)
  container.addEventListener("scroll", onScroll)

  function isTouchScroll() {
    return !!container.style.webkitOverflowScrolling
  }

  let prevScrollTop = 0, prevTouchY, opid = 0

  function setTouchScroll(on) {
    container.style.webkitOverflowScrolling = on ? "touch" : null

    //Hint: auto-enabling after a small pause makes the start
    // smoothly accelerated as required. After the pause the
    // scroll position is settled, and there is no delta to
    // make over-bounce by dragging the finger. But still,
    // accelerated content makes short single over-bounce
    // as acceleration may not be off instantly.

    const xopid = ++opid
    !on && setTimeout(() => (xopid === opid) && setTouchScroll(true), 250)

    if(!on && container.scrollTop < 16)
      container.scrollTop = 0
    prevScrollTop = container.scrollTop
  }

  function isBounceOverTop() {
    const dY = container.scrollTop - prevScrollTop
    return dY < 0 && container.scrollTop < 16
  }

  function isBounceOverBottom(touchY) {
    const dY = touchY - prevTouchY

    //Hint: trying to bounce over the bottom, the finger moves
    // up the screen, thus Y becomes smaller. We prevent this.

    return dY < 0 && container.scrollHeight - 16 <=
      container.scrollTop + container.offsetHeight
  }

  function onTouchStart(e) {
    prevTouchY = e.touches[0].pageY
  }

  function onTouch(e) {
    const touchY = e.touches[0].pageY

    if(isBounceOverBottom(touchY)) {
      if(isTouchScroll())
        setTouchScroll(false)
      e.preventDefault()
    }

    prevTouchY = touchY
  }

  function onTouchEnd() {
    prevTouchY = undefined
  }

  function onScroll() {
    if(isTouchScroll() && isBounceOverTop()) {
      setTouchScroll(false)
    }
  }
}
1

Consider the following architecture:

<body> <div id="root"></div> </body>

this css will work:

#root { position: fixed; height: 100%; overflow: auto; }
Shahriar
  • 1,855
  • 2
  • 21
  • 45
0

For those of you who don't want to get rid of the bouncing but just to know when it stops (for example to start some calculation of screen distances), you can do the following (container is the overflowing container element):

    const isBouncing = this.container.scrollTop < 0 ||
    this.container.scrollTop + this.container.offsetHeight >
        this.container.scrollHeight
Phil
  • 7,065
  • 8
  • 49
  • 91
0

Disable safari bounce scrolling effect:

html,
body {
  height: 100%;
  width: 100%;
  overflow: auto;
  position: fixed;
}  
0

I had an issue with grabbing the html element in the background, when a menu with scroll was open and either at the top or at the bottom at the scroll height. I tried lots of things. Setting html position to fixed was the closest I got to lock the screen, but in the PWA it resulted in a white area at the bottom, that I couldn't fix. Finally I've found a solution, that worked for me :

html {
    width: 100%;
    height: 100%;
    overflow: hidden;
}

body {
    margin: 0;
    height: calc(100vh - 1px)
    overflow: hidden;
    background-color: 'Whatever color you need to hide the 1px at the bottom'
}

Because it only seems to be an issue on iOS, I have targeted the devices from iPhone X to 12 Pro Max:

body {
    margin: 0;
    overflow: hidden;
    background-color: '#TIP: You can use the color picker from the inspector';

    @media only screen and (min-width: 375px) and (max-height: 926px) and (-webkit-device-pixel-ratio: 3) {
        height: calc(100vh - 1px);
    }
}

This is preventing any kind of scroll, touch or grab in the html or body elements, and scroll is still working in the menu or where else specified. Cheers.

Maje
  • 596
  • 4
  • 12
0
body {
   touch-action:none;
}

Using JQuery

// Disable
$("body").css({ "touch-action": "none" })

// Enable
$("body").css({ "touch-action": "auto" })
Synchro
  • 1,105
  • 3
  • 14
  • 44
0

This CSS solution worked for me (iOS-only, where I found it most annoying):

@supports (-webkit-touch-callout: none) {
    html {
        overscroll-behavior-y: none;
    }
}
Pere
  • 1,647
  • 3
  • 27
  • 52
-1

Similar to angry kiwi I got it to work using height rather than position:

html,body {
  height: 100%;
  overflow: hidden;
}

.the_element_that_you_want_to_have_scrolling{
  -webkit-overflow-scrolling: touch;
}
-2

Solution tested, works on iOS 12.x

This is problem I was encountering :

<body> <!-- the whole body can be scroll vertically -->
  <article>

    <my_gallery> <!-- some picture gallery, can be scroll horizontally -->
    </my_gallery>

  </article>
</body>

While I scrolling my gallery, the body always scrolling itself (human swipe aren't really horizontal), that makes my gallery useless.

Here's what I did while my gallery start scrolling

var html=jQuery('html');
html.css('overflow-y', 'hidden');
//above code works on mobile Chrome/Edge/Firefox
document.ontouchmove=function(e){e.preventDefault();} //Add this only for mobile Safari

And when my gallery end its scrolling...

var html=jQuery('html');
html.css('overflow-y', 'scroll');
document.ontouchmove=function(e){return true;}

Hope this helps~

RRTW
  • 3,160
  • 1
  • 35
  • 54