dragula icon indicating copy to clipboard operation
dragula copied to clipboard

Best way to prevent page scrolling on drag (mobile)

Open davedbase opened this issue 7 years ago • 42 comments

Whenever I drag on a long page the whole page moves along with the scroll. On mobile and tablet this can be annoying and a bit unusable. Does anyone have a good suggestion for nixing the scroll while drag?

davedbase avatar Apr 21 '17 20:04 davedbase

Any idea?

matte00 avatar May 18 '17 14:05 matte00

You could make the page non-scalable and non-scrollable for mobile browsers,

something like this in the HEAD section: <META name="viewport" content="initial-scale=0.66, user-scalable=no">

You can also disable android/chrome browser from refreshing when you drag downward by changing this setting on the browser itself:

chrome://flags/#disable-pull-to-refresh-effect

Of course these aren't fixes, but they may buy you some time until you find a fix.

bhwithun avatar Jun 01 '17 03:06 bhwithun

This worked for me - I hope it helps someone else:

drake = dragula({
    revertOnSpill: true,
    mirrorContainer: document.querySelector('.movecontainer')
}).on('drag', function(el, source) {
    // On mobile this prevents the default page scrolling while dragging an item.
    $(document).on('touchstart', function(e) {
        e.preventDefault();
    });
}).on('drop', function(el, target, source, sibling) {
    // On mobile this turns on default page scrolling after the end of a drag drop.
    $(document).off('touchstart');
});

It requires jQuery of course.

Beedell avatar Jun 07 '17 16:06 Beedell

Hello

I'm not sure this is the correct place to ask this, so feel free to move my comment if it is not.

I have the same issue, however I do not want to simply disable the scrolling while dragging, I want to be able to make the page scroll down by dragging an item to the bottom, like the Google Drive app does when moving files. That way, I would be able to put an item from the very top of the list to the bottom in one drag, as opposed to multiple drags right now.

Is it possible by modifying the solution given here or is there any other way?

AlbaVenzal avatar Aug 03 '17 08:08 AlbaVenzal

@AlbaVenzal I'll start working on this. @bevacqua could you assign me this issue?

andresarpi avatar Sep 13 '17 12:09 andresarpi

Meanwhile this solution simple solution/hack works Flickity issue 457 comment

window.addEventListener( 'touchmove', function() {})

But since this disables scrolling totally, you're not able to make the page scroll down by dragging an item to the bottom.

snobojohan avatar Oct 11 '17 12:10 snobojohan

@andresarpi / @bevacqua have you resolved this issue? Thanks!!!

tmk1991 avatar Oct 25 '17 01:10 tmk1991

Hello guys, what about this issue?

HZooly avatar Apr 04 '18 12:04 HZooly

hey @snobojohan the solution window.addEventListener( 'touchmove', function() {}) dont work for me on iphone with ios 11. do you know what am i doing wrong? Thanks

yosigolan avatar Apr 09 '18 10:04 yosigolan

@yosigolan please try

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

Somethingideally avatar Apr 24 '18 08:04 Somethingideally

@Somethingideally based on your solution, I came up with (to avoid completely preventing scrolling on the page):

dragula([dndContainer], {
      direction: 'horizontal'
    })
      .on('drop', function(element, targetContainer, sourceContainer, sibling){
        $(document).off('touchmove');
      })
      .on('drag', function(el, source) {
        $(document).on('touchmove', function(e) { e.preventDefault(); }, { passive:false });
      });

It improved things on iOS: the horizontal scrolling issue is gone, but we still have vertical scrolling issues. Any idea what the problem might be?

Jenselme avatar Apr 24 '18 09:04 Jenselme

Hello, @Jenselme, you can try this solution:

var scrollable = true;

var listener = function(e) {
    if (! scrollable) {
        e.preventDefault();
    }
}

document.addEventListener('touchmove', listener, { passive:false });

dragula([dndContainer], {
    direction: 'horizontal'
}).on('drag', function(el, source) {
    scrollable = false;
}).on('drop', function(el, source) {
    scrollable = true;
});

Somethingideally avatar Apr 24 '18 09:04 Somethingideally

@Somethingideally still broken :-(

Jenselme avatar Apr 24 '18 10:04 Jenselme

Hey @Somethingideally Thank you! it solved it! i do it a little differently to destroy the event subscription when the component is destroyed: @HostListener('touchmove', ['$event']) private onTouchMoveEvent(event:Event): void { event.preventDefault(); }

do you know in any chance how to solve this as well: https://github.com/valor-software/ng2-dragula/issues/517 (this is an issue on android only)

Thanks Yosi

yosigolan avatar Apr 25 '18 09:04 yosigolan

Somethingideally's solution worked immediately for my Dragula-enabled page. Thanks very much for your well thought out and helpful code. Oddly, this only began to happen a couple of days ago. When I run an earlier, formerly-working version on Android now, it breaks.

WollydaKotzah avatar May 15 '18 08:05 WollydaKotzah

@Somethingideally shouldn't this be using dragend to reset scrollable? Otherwise scrolling is not re-enabled after a cancel or removal, only after a successful drop.

chrisdeeming avatar Jun 13 '18 09:06 chrisdeeming

@chrisdeeming, yes, later im added this

dragula([dndContainer], {
    direction: 'horizontal'
}).on('drag', function(el, source) {
    scrollable = false;
}).on('drop', function(el, source) {
    scrollable = true;
}).on('dragend', function(el, source) {
    scrollable = true;
   // your logic on dragend
});

Somethingideally avatar Jun 13 '18 09:06 Somethingideally

I think only this is needed:

dragula([dndContainer], {
    direction: 'horizontal'
}).on('drag', function(el, source) {
    scrollable = false;
}).on('dragend', function(el, source) {
    scrollable = true;
   // your logic on dragend
});

chrisdeeming avatar Jun 13 '18 09:06 chrisdeeming

Hey guys, I wonder nobody found a solution yet. Just prevent the default touchmove events on the container, so it does not bubble up to the window.

const container = document.querySelector(...);
const dragula = window.dragula(
[
	container
]);

/* handle scroll */

container.addEventListener('touchmove', event => event.preventDefault());

Works for me... :-)

henryruhs avatar Oct 07 '18 00:10 henryruhs

@redaxmedia this prevents scrolling the container entirely though.

rohan-deshpande avatar Oct 14 '18 02:10 rohan-deshpande

@rohan-deshpande Nope, it prevents the window to scoll while dragging elements inside the container. It is not perfect but should work for most user interfaces. Feel free to contribute a better solution.

henryruhs avatar Oct 15 '18 19:10 henryruhs

What mobile browsers are you testing on and what version of the operating system? Can you post a fiddle or codepen as an example? I’ve tried this out and it’s prevented scrolling completely in iOS >= 11.3. No need to be defensive mate I’d be very happy if this worked but it hasn’t worked for me so I’m just wondering what exactly is going on.

rohan-deshpande avatar Oct 15 '18 19:10 rohan-deshpande

Unfortunately, I did not test it on iOS as lack of devices. It works on Desktop Chrome (with sensor / responsive view) and Mobile Chrome on Android 6 like shown in the GIF.

No time to create a fiddle at this moment, but you can login to the Redaxscript Demo and browse the Admin Listing to play around with it.

ezgif-4-957ae728a869

henryruhs avatar Oct 15 '18 20:10 henryruhs

Thanks for the explanation and gif. I can’t see anything on that page sadly, I’m assuming it’s behind some authentication?

rohan-deshpande avatar Oct 15 '18 20:10 rohan-deshpande

Please click the first link - it does some kind of auto-login

henryruhs avatar Oct 15 '18 20:10 henryruhs

Thanks for that, this does seem to be working, it’s a bit janky but yeah it works! I’m curious as to how you are handling scrolling while dragging while preventing the default event. Are you scrolling the page with JavaScript at any point?

rohan-deshpande avatar Oct 15 '18 20:10 rohan-deshpande

this prevents scrolling the container entirely though.

Unfortunately, you were right with this statement. I did not realize this, yet.

Finally, I found a solution that allows scrolling over the container but disables scrolling the window while dragging some element inside the container.

  1. Use a drag handle
moves: (element, container, handle) =>
{
	return handle.classList.contains('drag-handle-class');
}
  1. Prevent the default event on the drag handle
const moveList = document.querySelectorAll('div.drag-handle-class');

if (moveList)
{
	moveList.forEach(move =>
	{
		move.addEventListener('touchmove', event => event.preventDefault());
	});
}

henryruhs avatar Oct 15 '18 23:10 henryruhs

Interesting, thanks for this. I spent a chunk of time attempting to migrate to react-beautiful-dnd only to discover that nested scroll containers are not supported. Even though that library is very nice the lack of that support breaks my interface so I’ll try this out and see if it works.

rohan-deshpande avatar Oct 16 '18 21:10 rohan-deshpande

@rohan-deshpande Hopefully, you can get it working... give the drag handle approach a chance :-)

henryruhs avatar Oct 16 '18 22:10 henryruhs

@redaxmedia this works for my implementation. Thanks very much!

rohan-deshpande avatar Nov 04 '18 01:11 rohan-deshpande