iScroll with native scrolling on one axis
Asked Answered
G

7

13

I am using the most wonderful javascript tool iScroll4 http://cubiq.org/iscroll-4 on a mobile website for iOS and Android. Here is what my layout looks like:

layout problem

The horizontally scroll-able area is making use of iScroll4 with the following settings:

   var myScroll = new iScroll('frame',  { hScrollbar: false, vScrollbar: false, vScroll: false })

The horizontal scrolling part works great. This issue is what happens when a user attempts to scroll up or down the page placing their finger on the horizontal scrolling area. So I need native vertical scrolling, and iScroll horizontal scrolling on the same area.

What I have tried so far: Removing e.preventDefault() in the iScroll code (allows for native scrolling, but in BOTH axes). Removing e.preventDefault() and then disabling horizontal scrolling page wide with this:

var touchMove;

document.ontouchstart = function(e){
    touchMove = e.touches[0];
}

document.ontouchmove = function(e){
    var theTouch = e.touches[0] || e.changedTouches[0];
    var Xer      = rs(touchMove.pageX - theTouch.pageX).toPos();
    var Yer      = rs(touchMove.pageY - theTouch.pageY).toPos();        
    touchMove    = theTouch;
    if(Yer > Xer){ e.preventDefault(); }
}

which seems to do nothing. How can I allow for native vertical scrolling in the horizontal scrolling area, without loosing the horizontal scrolling of iScroll? I am really stumped here. Thanks in advance.

(just for the record rs(foo).toPos() is a function that makes foo a positive number regardless of its value).

Gurias answered 17/10, 2011 at 22:17 Comment(0)
G
10

Suggested edit to @Lukx's excellent solution. New versions of iScroll4 place the e.preventDefault() in onBeforeScrollMove which can be overridden. By placing the if block into this option, default is not prevented for vertical scrolling, and vertical can scroll natively.

myScroll = new iScroll('scrollpanel', {
    // other options go here...
    vScroll: false,
    onBeforeScrollStart: function ( e ) {
        if ( this.absDistX > (this.absDistY + 5 ) ) {
            // user is scrolling the x axis, so prevent the browsers' native scrolling
            e.preventDefault();
        }
    },
});
Gurias answered 30/10, 2012 at 19:55 Comment(5)
Sorry I did really somehow forget about your comment. This is a great answer, thank you for enhancing it! +1Unify
The preventDefault call is actually in onBeforeScrollStart, and the absDist properties seem to be updated only after this event has fired (touchstart/mousedown), so the condition only works on the second consecutive swipe in the same direction. I am happy with this though - in fact the extra effort required to switch axis makes my horizontal scrollers a little easier to use. I've tried many (all?) other approaches to achieve this functionality with iScroll and this method won in the end. +1!Iranian
Mind that this doesn't work if you already added e.preventDefault to touchstart and touchmove events like they recommend in iScroll examples to prevent clicking through dragging links, so you need to remove that handler first.Rapeseed
True, I guess you could have the vertical scrolling also be an iScroll. Otherwise its a given.Gurias
Note, this is no longer relevant with iscroll 5 - see my answerLamontlamontagne
U
12

If you would like to achieve the effect described by Fresheyeball without hacking the core, and without changing from iScroll to swipeview, then iScroll 4 does offer you its event listeners to work with.

myScroll = new iScroll('scrollpanel', {
    // other options go here...
    vScroll: false,
    onBeforeScrollMove: function ( e ) {
        if ( this.absDistX > (this.absDistY + 5 ) ) {
            // user is scrolling the x axis, so prevent the browsers' native scrolling
            e.preventDefault();
        } else {
            // delegate the scrolling to window object
        window.scrollBy( 0, -this.distY );
    }
    },
});

By doing so, the onBeforeScrollMove-Handler checks whether the scroll direction seems to be horizontal, and then prevents the default handler, thus effectively locking the scroll action to the X-Axis (try commenting it out, you'll see the difference). Otherwise, if the scroll direction needs to be vertical, we make the browser scroll via the window.scrollBy() method. This is not exactly native, but does the job just fine.

Hope that helps

Lukx

[EDIT] My original solution, which didn't use window.scrollBy() ,did not work on slower Samsung phones, which is why I needed to adapt the answer.

Unify answered 18/5, 2012 at 7:52 Comment(2)
I would like to suggest and edit. If you are using the most recent version of iScroll4 you can use the new onBeforeScrollStart instead of onBeforeScrollMove. In doing so you can get rid of the else and scrollBy, native scrolling will work.Gurias
Ok I have not received a response on the suggested edit for some time. So I am moving the accepted answer to my own.Gurias
G
10

Suggested edit to @Lukx's excellent solution. New versions of iScroll4 place the e.preventDefault() in onBeforeScrollMove which can be overridden. By placing the if block into this option, default is not prevented for vertical scrolling, and vertical can scroll natively.

myScroll = new iScroll('scrollpanel', {
    // other options go here...
    vScroll: false,
    onBeforeScrollStart: function ( e ) {
        if ( this.absDistX > (this.absDistY + 5 ) ) {
            // user is scrolling the x axis, so prevent the browsers' native scrolling
            e.preventDefault();
        }
    },
});
Gurias answered 30/10, 2012 at 19:55 Comment(5)
Sorry I did really somehow forget about your comment. This is a great answer, thank you for enhancing it! +1Unify
The preventDefault call is actually in onBeforeScrollStart, and the absDist properties seem to be updated only after this event has fired (touchstart/mousedown), so the condition only works on the second consecutive swipe in the same direction. I am happy with this though - in fact the extra effort required to switch axis makes my horizontal scrollers a little easier to use. I've tried many (all?) other approaches to achieve this functionality with iScroll and this method won in the end. +1!Iranian
Mind that this doesn't work if you already added e.preventDefault to touchstart and touchmove events like they recommend in iScroll examples to prevent clicking through dragging links, so you need to remove that handler first.Rapeseed
True, I guess you could have the vertical scrolling also be an iScroll. Otherwise its a given.Gurias
Note, this is no longer relevant with iscroll 5 - see my answerLamontlamontagne
L
4

With iscroll 5, you can set eventPassthrough: true to achieve this. See http://iscrolljs.com/#configuring

Lamontlamontagne answered 18/9, 2014 at 1:56 Comment(0)
G
1

OLD ANSWER

UPDATE a special pluggin has been written just to address this problem: http://cubiq.org/swipeview

I found a way!

add a variable to the top of the document: if android is 15 and is iOS is 3

 var scrollTolerance = ( rs().isDevice('android') )?15:3;

disable the original e.preventDefault(); for scrolling. This is under onBeforeScrollStart:

the in _move just under

 timestamp = e.timeStamp || Date.now();

add this line

if( Math.sqrt(deltaX*deltaX) > scrollTolerance){e.preventDefault();}

What this does is the following: the scrollTolerance sets, you guessed it, a tolerance for finger direction. We don't want to demand a perfect vertical angle to get the up down native scroll. Also iOS does not detect properly and will never be higher than 4 for some reason so I used 3. Then we disable iScroll's standard e.preventDefault(); which prevents native vertical scrolling on our bi-scrollable area. Then we insert e.preventDefault(); only upon move and based on finger direction from tolerance.

This does not work perfect. But is acceptable and works on iOS and Android. If anyone sees better ways please post here. This is something I (and assume others) need to use regularly, and we should have a perfect rock solid solution.

Thanks.

Gurias answered 18/11, 2011 at 6:42 Comment(7)
didn't seem work for me. now it looks like the wrapper is totally gone from the image...how did you disable original e.preventDefault()Fearfully
I commented it out. Either way there is a better way now! Go back to the iScoll website, there is a new pluggin to address just this issue!Gurias
really!??! i've been on the site, i don't see the plugin. do you have a link to it? thanks so much!Fearfully
awesome, thanks...having some trouble implementing SwipeView with my iScroll4 wrapper. Any tips?Fearfully
open your own stackoverflow question, and link it here. I will do my best to help. If you want to give a +1 to this question and(or) answer that would be nice too.Gurias
thanks, i +1'd it yesterday...#9400760Fearfully
Not the answer :( its still at 0 upvotes. (shameless self motivated plea)Gurias
R
0

Please test this solution from Adam. https://gist.github.com/hotmeteor/2231984

I think the trick is to add the check in onBeforeScrollMove. First get the initial touch position in onBeforeScrollTouchStart and then in onBeforeScrollMove check the new position and then disable the required scroll based on the difference.

Reach answered 9/10, 2013 at 6:55 Comment(1)
Hmm. Nothing is being done with the event object in the onBeforeScrollMove. I think this can be written better like this: gist.github.com/Fresheyeball/6946017Gurias
B
0

iScroll 5 supports native scrolling of any axis!

http://iscrolljs.com/

Bifrost answered 20/3, 2014 at 15:30 Comment(0)
V
0

on iScroll5 just set eventPassthrougt to true. That fixes it.

Vasili answered 19/9, 2017 at 19:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.