60

Is there any way to completely disable web page scrolling in an iPhone web app? I've tried numerous things posted on google, but none seem to work.

Here's my current header setup:

<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>

document.body.addEventListener('touchmove', function(e){ e.preventDefault(); });

doesn't seem to work.

Stefan Kendall
  • 66,414
  • 68
  • 253
  • 406
  • kendall, would you mind adding more of your code so I could see what went in... I'm pretty new to this - here's what i have that doesn't work: `` – Andrew Samuelsen Jun 22 '11 at 17:43
  • 4
    nevermind i used this: `document.ontouchmove = function(e){ e.preventDefault(); } ` – Andrew Samuelsen Jun 22 '11 at 17:55

9 Answers9

58

Change to the touchstart event instead of touchmove. Under One Finger Events it says that no events are sent during a pan, so touchmove may be too late.

I added the listener to document, not body.

Example:

document.ontouchstart = function(e){ 
    e.preventDefault(); 
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
drawnonward
  • 53,459
  • 16
  • 107
  • 112
  • 6
    You actually need to use both touchstart AND touchmove, or you can still get scrolling. – Stefan Kendall May 29 '10 at 06:14
  • 3
    @jirapong would you mind adding more of your code so I could see what went in... I'm pretty new to this - here's what i have that doesn't work: `` – Andrew Samuelsen Jun 22 '11 at 17:42
  • Do you testing with iPhone/iPad? If yes, then you can try to open Developer bar on Setting to see more detail. hope it help. – Jirapong Jun 22 '11 at 17:45
  • 2
    @jirapong, thanks. i used this: nevermind i used this: `document.ontouchmove = function(e){ e.preventDefault(); } ` – Andrew Samuelsen Jun 22 '11 at 17:55
  • 44
    this also kills all tap events, so nothing on the page is tappable. – keune Sep 17 '11 at 19:28
  • It works albeit what about enabling touchmove again? what do we do then? I disable it on button click but after the event - it still remains in-scrollable. – foxybagga Nov 14 '11 at 03:05
  • found out how to enable the scrolling again - document.ontouchmove = function(e){ return true; } – foxybagga Nov 14 '11 at 05:19
  • I think they fixed this in iOS 5. You don't need to prevent default on touchstart to prevent scrolling, so now everything can be tapped. Device: iPad 2, iOS version 5.1.1. It has to be on the document. – Bobby Sep 24 '12 at 20:02
  • :D - Works perfectly as advertised in iOS 7! Just one question - is there anyway to hide the navigation and tool bars to give my web app the full screen? – ArtOfWarfare Aug 09 '14 at 21:02
  • This doesn't work for me. I have a modal with an overlay. Once I reach the end of the scroll content of the modal, then the content behind the overlay begins to scroll, even when using `$(document).on('touchstart touchmove', function(e){ e.preventDefault(); });` – Ben Sep 26 '14 at 18:38
  • I had posted similar question here: http://stackoverflow.com/q/25016369/261375 with still no answer as of 09/26/2014 – Ben Sep 26 '14 at 18:49
19
document.addEventListener('touchstart', function (e) {
    e.preventDefault();
});

Do not use the ontouchmove property to register the event handler as you are running at risk of overwriting an existing event handler(s). Use addEventListener instead (see the note about IE on the MDN page).

Beware that preventing default for the touchstart event on the window or document will disable scrolling of the descending areas.

To prevent the scrolling of the document but leave all the other events intact prevent default for the first touchmove event following touchstart:

var firstMove;

window.addEventListener('touchstart', function (e) {
    firstMove = true;
});

window.addEventListener('touchmove', function (e) {
    if (firstMove) {
        e.preventDefault();

        firstMove = false;
    }
});

The reason this works is that mobile Safari is using the first move to determine if body of the document is being scrolled. I have realised this while devising a more sophisticated solution.

In case this would ever stop working, the more sophisticated solution is to inspect the touchTarget element and its parents and make a map of directions that can be scrolled to. Then use the first touchmove event to detect the scroll direction and see if it is going to scroll the document or the target element (or either of the target element parents):

var touchTarget,
    touchScreenX,
    touchScreenY,
    conditionParentUntilTrue,
    disableScroll,
    scrollMap;

conditionParentUntilTrue = function (element, condition) {
    var outcome;

    if (element === document.body) {
        return false;
    }

    outcome = condition(element);

    if (outcome) {
        return true;
    } else {
        return conditionParentUntilTrue(element.parentNode, condition);
    }
};

window.addEventListener('touchstart', function (e) {
    touchTarget = e.targetTouches[0].target;
    // a boolean map indicating if the element (or either of element parents, excluding the document.body) can be scrolled to the X direction.
    scrollMap = {}

    scrollMap.left = conditionParentUntilTrue(touchTarget, function (element) {
        return element.scrollLeft > 0;
    });

    scrollMap.top = conditionParentUntilTrue(touchTarget, function (element) {
        return element.scrollTop > 0;
    });

    scrollMap.right = conditionParentUntilTrue(touchTarget, function (element) {
        return element.scrollWidth > element.clientWidth &&
               element.scrollWidth - element.clientWidth > element.scrollLeft;
    });

    scrollMap.bottom =conditionParentUntilTrue(touchTarget, function (element) {
        return element.scrollHeight > element.clientHeight &&
               element.scrollHeight - element.clientHeight > element.scrollTop;
    });

    touchScreenX = e.targetTouches[0].screenX;
    touchScreenY = e.targetTouches[0].screenY;
    disableScroll = false;
});

window.addEventListener('touchmove', function (e) {
    var moveScreenX,
        moveScreenY;

    if (disableScroll) {
        e.preventDefault();

        return;
    }

    moveScreenX = e.targetTouches[0].screenX;
    moveScreenY = e.targetTouches[0].screenY;

    if (
        moveScreenX > touchScreenX && scrollMap.left ||
        moveScreenY < touchScreenY && scrollMap.bottom ||
        moveScreenX < touchScreenX && scrollMap.right ||
        moveScreenY > touchScreenY && scrollMap.top
    ) {
        // You are scrolling either the element or its parent.
        // This will not affect document.body scroll.
    } else {
        // This will affect document.body scroll.

        e.preventDefault();

        disableScroll = true;
    }
});

The reason this works is that mobile Safari is using the first touch move to determine if the document body is being scrolled or the element (or either of the target element parents) and sticks to this decision.

kim3er
  • 6,306
  • 4
  • 41
  • 69
Gajus
  • 69,002
  • 70
  • 275
  • 438
14

If you are using jquery 1.7+, this works well:

$("donotscrollme").on("touchmove", false);
Rob Lauer
  • 3,075
  • 1
  • 32
  • 44
13

This should work. No more gray areas at the top or bottom:)

<script type="text/javascript">
   function blockMove() {
      event.preventDefault() ;
}
</script>

<body ontouchmove="blockMove()">

But this also disables any scrollable areas. If you want to keep your scrollable areas and still remove the rubber band effect at the top and bottom, see here: https://github.com/joelambert/ScrollFix.

Philipp Kyeck
  • 18,402
  • 15
  • 86
  • 123
kaleazy
  • 5,922
  • 2
  • 47
  • 51
10

Disable:

document.ontouchstart = function(e){ e.preventDefault(); }

Enable:

document.ontouchstart = function(e){ return true; }
Brynner Ferreira
  • 1,527
  • 1
  • 21
  • 21
9

'self.webView.scrollView.bounces = NO;'

Just add this one line in the 'viewDidLoad' of the mainViewController.m file of your application. you can open it in the Xcode and add it .

This should make the page without any rubberband bounces still enabling the scroll in the app view.

Chait
  • 511
  • 7
  • 15
  • It's been awhile but this answer doesn't really seem to be for this question as it is describing how to avoid scrolling inside a scrollView of a native iOS app. – TheBen Sep 20 '17 at 21:00
4

The page has to be launched from the Home screen for the meta tag to work.

Freebie
  • 41
  • 1
2

I tried above answers and particularly Gajus's but none works. Finally I found the answer below to solve the problem such that only the main body doesn't scroll but other scrolling sections inside my web app all work fine. Simply set position fixed for your body:

body {

height: 100%;
overflow: hidden;
width: 100%;
position: fixed;
}
TheBen
  • 3,410
  • 3
  • 26
  • 51
  • This works well, especially if you never change it in the experience. If this is something you'll toggle on/off, perhaps for when a modal is displayed, then you may want to account for users' scroll position, as it would get reset every time. For a large e-commerce site, I've used a library: https://www.npmjs.com/package/body-scroll-lock – Kalnode Mar 13 '23 at 23:18
1
document.ontouchmove = function(e){ 
    e.preventDefault(); 
}

is actually the best choice i found out it allows you to still be able to tap on input fields as well as drag things using jQuery UI draggable but it stops the page from scrolling.

Josh Bedo
  • 3,410
  • 3
  • 21
  • 33