How to prevent document scrolling but allow scroll

2019-01-13 02:17发布

I created a website with jQueryMobile for iOS and Android.

I don't want the document itself to scroll. Instead, just an area (a <div> element) should be scrollable (via css property overflow-y:scroll).

So I disabled document scrolling via:

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

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

But that will also disable scrolling for all other elements in the document, no matter if overflow:scroll is set or not.

How can I solve this?

10条回答
做自己的国王
2楼-- · 2019-01-13 03:10

Here is my implementation which works on touch devices and laptops.

function ScrollManager() {
    let startYCoord;

    function getScrollDiff(event) {
        let delta = 0;

        switch (event.type) {
            case 'mousewheel':
                delta = event.wheelDelta ? event.wheelDelta : -1 * event.deltaY;
                break;
            case 'touchstart':
                startYCoord = event.touches[0].clientY;
                break;
            case 'touchmove': {
                const yCoord = event.touches[0].clientY;

                delta = yCoord - startYCoord;
                startYCoord = yCoord;
                break;
            }
        }

        return delta;
    }

    function getScrollDirection(event) {
        return getScrollDiff(event) >= 0 ? 'UP' : 'DOWN';
    }

    function blockScrollOutside(targetElement, event) {
        const { target } = event;
        const isScrollAllowed = targetElement.contains(target);
        const isTouchStart = event.type === 'touchstart';

        let doScrollBlock = !isTouchStart;

        if (isScrollAllowed) {
            const isScrollingUp = getScrollDirection(event) === 'UP';
            const elementHeight = targetElement.scrollHeight - targetElement.offsetHeight;

            doScrollBlock =
                doScrollBlock &&
                ((isScrollingUp && targetElement.scrollTop <= 0) ||
                    (!isScrollingUp && targetElement.scrollTop >= elementHeight));
        }

        if (doScrollBlock) {
            event.preventDefault();
        }
    }

    return {
        blockScrollOutside,
        getScrollDirection,
    };
}

const scrollManager = ScrollManager();
const testBlock = document.body.querySelector('.test');

function handleScroll(event) {
  scrollManager.blockScrollOutside(testBlock, event);
}

window.addEventListener('scroll', handleScroll);
window.addEventListener('mousewheel', handleScroll);
window.addEventListener('touchstart', handleScroll);
window.addEventListener('touchmove', handleScroll);
.main {
   border: 1px solid red;
   height: 200vh;
 }
 
 .test {
   border: 1px solid green;
   height: 300px;
   width: 300px;
   overflow-y: auto;
   position: absolute;
   top: 100px;
   left: 50%;
 }
 
 .content {
   height: 100vh;
 }
<div class="main">
  <div class="test">
    <div class="content"></div>
  </div>
</div>

查看更多
我命由我不由天
3楼-- · 2019-01-13 03:11

Maybe I misunderstood the question, but if I'm correct:

You want not to be able to scroll except a certain element so you:

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

Prevent everything within the document.


Why don't you just stop the event bubbling on the element where you wish to scroll? (PS: you don't have to prevent touchstart -> if you use touch start for selecting elements instead of clicks that is prevented as well, touch move is only needed because then it is actually tracing the movement)

$('#element').on('touchmove', function (e) {
     e.stopPropagation();
});

Now on the element CSS

#element {
   overflow-y: scroll; // (vertical) 
   overflow-x: hidden; // (horizontal)
}

If you are on a mobile device, you can even go a step further. You can force hardware accelerated scrolling (though not all mobile browsers support this);

Browser Overflow scroll:

Android Browser Yes
Blackberry Browser  Yes
Chrome for Mobile   Yes
Firefox Mobile  Yes
IE Mobile           Yes
Opera Mini          No
Opera Mobile    Kinda
Safari          Yes

#element.nativescroll {
    -webkit-overflow-scrolling: touch;
}

normal:

<div id="element"></div>

native feel:

<div id="element" class="nativescroll"></div>
查看更多
We Are One
4楼-- · 2019-01-13 03:17

Here is a solution I am using:

$scrollElement is the scroll element, $scrollMask is a div with style position: fixed; top: 0; bottom: 0;. The z-index of $scrollMask is smaller than $scrollElement.

$scrollElement.on('touchmove touchstart', function (e) {
    e.stopPropagation();
});
$scrollMask.on('touchmove', function(e) {
    e.stopPropagation();
    e.preventDefault();
});
查看更多
一夜七次
5楼-- · 2019-01-13 03:17

Here is a solution that uses jQuery for the events.

var stuff = {};
$('#scroller').on('touchstart',stuff,function(e){
  e.data.max = this.scrollHeight - this.offsetHeight;
  e.data.y = e.originalEvent.pageY;
}).on('touchmove',stuff,function(e){
  var dy = e.data.y - e.originalEvent.pageY;
  // if scrolling up and at the top, or down and at the bottom
  if((dy < 0 && this.scrollTop < 1)||(dy > 0 && this.scrollTop >= e.data.max)){
    e.preventDefault();
  };
});
查看更多
登录 后发表回答