24

I am creating a little web app for the iPad and I've got several elements I am preventing the user from scrolling by preventing default on the touchmove event. However, I have a situation where I need the user to be able to scroll a child element.

I have tried to use e.stopPropagation(); but no luck! I also attempted to detect the element under the finger and put the e.preventDefault(); inside an if statement, but again, no luck. Or maybe I was just getting mixed up...

Any ideas? Removing the .scroll divs from the #fix div is a last resort really as it will cause all sorts of headaches.


EDIT

I managed to sort it. Seems like I didn't understand the use of .stopPropagation(); oops!

The working code:

<div id="fix">

    <h1>Hi there</h1>

    <div class="scroll">
        <ul>
            <li>List item</li>
            <li>List item</li>
            <li>List item</li>
        </ul>
    </div>

    <div class="scroll">
        <ul>
            <li>List item</li>
            <li>List item</li>
            <li>List item</li>
        </ul>
    </div>

</div>

And the Javascript:

$('body').delegate('#fix','touchmove',function(e){

    e.preventDefault();

}).delegate('.scroll','touchmove',function(e){

    e.stopPropagation();

});
Karen Zilles
  • 7,633
  • 3
  • 34
  • 33
will
  • 4,557
  • 6
  • 32
  • 33
  • Try Adding return false; at the end of event handler – jancha Feb 28 '12 at 16:56
  • write **stopPropagation** instead of **stopPropogation** – udidu Feb 28 '12 at 17:07
  • Ha, yea, thankfully I was actually typing it correctly in my code ;) – will Feb 28 '12 at 17:11
  • You should add your solution as an answer and accept, it worked for me. Thank you! – dSquared Sep 21 '12 at 14:12
  • 3
    I'm confused, how exactly did you solve the problem? Can you please point out which is the working code, for numbskulls like me? haha thanks :) – Phil Nov 09 '12 at 01:27
  • Nailed it for me. I had a full screen form, with form inputs which triggered the document to scroll. I add stopPropagation to one of the wrapping elements and it was fixed. – BradGreens Apr 25 '13 at 04:18
  • @Phil, you can see the edit history by clicking on the edited link near the original poster's details - http://stackoverflow.com/posts/9486195/revisions – BradGreens Apr 25 '13 at 04:20
  • Edited to be more clear. Thanks for posting your working code, @will. – RobW Aug 25 '13 at 18:23

5 Answers5

18

Try this:

$('#fix').on('touchmove',function(e){
    if(!$('.scroll').has($(e.target)).length)
        e.preventDefault();
});

EDITED

e.target contains the final target node of the touch event. You can stop all events that are not "bubbling accross" your .scroll divs.

I think there are better solutions, but this one must be ok.

rootr
  • 382
  • 1
  • 11
adriendenat
  • 3,445
  • 1
  • 25
  • 25
10
document.addEventListener('touchmove', function(e){e.preventDefault()}, false);
document.getElementById('inner-scroll').addEventListener('touchmove', function(e){e.stopPropagation()}, false);

The idea is that your main scroll is always (to your discretion) disabled, but in your inner-scroll you prevent the event from bubbling up (or propagating), so it will never reach the first event listener, which would ultimately cancel the touchmove event.

I hope this is what you were looking for. I've had a situation similar to yours, where the main scroll was disabled on tablet, but i wanted an inner scroll to work. This seemed to do the job.

Prusprus
  • 7,987
  • 9
  • 42
  • 57
8

This should work:

$(document).on('touchmove', function(e) {
    if (!$(e.target).parents('.scroll')[0]) {
        e.preventDefault();
    }
});
nick
  • 3,544
  • 1
  • 26
  • 22
  • The subject specifies the parent, not the element itself, so use of closest() would not be correct. – nick Dec 24 '14 at 23:03
  • A cleaner way to write that is: `$(document).on("touchmove", function(e) { e.preventDefault() }); $(document).on("touchmove", ".scroll", function(e) { e.stopPropagation() });` – gwendall Apr 30 '15 at 14:51
  • That will disable touchmove across the whole document. You also don't need to reference $(document) twice if you chain the functions. – nick May 01 '15 at 20:14
2

I found an interesting discussion here: https://github.com/joelambert/ScrollFix/issues/2 , and there's a good solution from bjrn

Basically, it takes advantage of flex boxes. Worked well for me on ipad. Here is a test link he set up that shows it at work: http://rixman.net/demo/scroll/

What's especially nice about this solution is that it's all CSS and no javascript required.

ryan
  • 2,311
  • 3
  • 22
  • 28
1

I found another solution using css-classes and document.ontouchmove. I'm developing an IPad Webapp with Sliders and wanted to prevent all the other elements to bounce when there's a touchmove event. This is my solution as an abstract:

HTML:

<body>
  <div id="outterFrame" class="fullscreen"> <!-- class fullscreen is self-explaining I guess -->
      <div id="touchmove_enabled class="enable_touchmove sliderbutton">Button</div>
  </div>
</body>

Javascript:

/* preventDefault on touchmove events per default */
document.ontouchmove = function(event)
{
   var sourceElement = event.target || event.srcElement;
   if(!hasClass(sourceElement,"enable_touchmove"))
{
   e.preventDefault();
}
};

/* helper method "hasClass" */
function hasClass(element,class)
{
if(element.className != null)
{
    return element.className.match(new RegExp('(\\s|^)'+class+'(\\s|$)'));
}
return false;
}

Now, only the touchmove event of the div "touchmove_enabled" is fired.

Probably my solution may be useful for somebody. Patric

Patric S.
  • 41
  • 1