Mobile / Touch Enabled Screens - horizontal scroll on swipe - javascript

This question has been asked after a detailed discussion on this SO question
Problem:
I need a horizontal scroll which can be scrolled using mouse drag on desktops and swipe events on touch enabled screens
Possible Solution:
I tried using the jQuery dragscrollable which works fine on desktops but not on touch enabled devices
So then I went on to explore Touch Swipe Jquery Plugin and came up with a possible solution at JSFiddle Code and the result for the JSFiddle can be found here
You can also find a working demo at here
My java script code is as follows
//to detect if device has touch enabled
var is_touch_device = 'ontouchstart' in document.documentElement;
$(function()
{
$('.myClass').dragscrollable();
//if touch is enabled then we have to map the swipe event
if(is_touch_device)
$('.panel_list').swipe( { swipeStatus:scroll_panel_list, allowPageScroll:'horizontal' } );
function scroll_panel_list(event, phase, direction, distance)
{
var pos = $('.myClass').scrollLeft();
if(direction == 'left')
{
$('.myClass').animate({scrollLeft: pos + 200} );
}
if(direction == 'right')
{
$('.myClass').animate({scrollLeft: pos - 200} );
}
}
});​
I have tested it works fine on Android browser but not very reponsive on iPhone.
Can someone help me come up with a better solution ? I am using twitter bootstrap
EDIT:1
Well now I guess I might have hit upon a nice plugin in that seems to work fine on desktops and touch enabled devices, the plugin is called jquery.dragscroll, I have an updated demo here
EDIT:2
There seems to be another plugin that has support for touch-enabled devices, it is called Overscroll. I haven't evaluated it as yet

Additionally, there is the "Swipeview" script
http://cubiq.org/swipeview

I found an older solution and modified it for horizontal scrolling. I've tested it on Android Chrome and iOS Safari and the listener touch events have been around a long time, so it has good support: http://caniuse.com/#feat=touch.
Usage:
touchHorizScroll('divIDtoScroll');
Functions:
function touchHorizScroll(id){
if(isTouchDevice()){ //if touch events exist...
var el=document.getElementById(id);
var scrollStartPos=0;
document.getElementById(id).addEventListener("touchstart", function(event) {
scrollStartPos=this.scrollLeft+event.touches[0].pageX;
},false);
document.getElementById(id).addEventListener("touchmove", function(event) {
this.scrollLeft=scrollStartPos-event.touches[0].pageX;
},false);
}
}
function isTouchDevice(){
try{
document.createEvent("TouchEvent");
return true;
}catch(e){
return false;
}
}
Original Vertical one-finger touch scroll:
http://chris-barr.com/2010/05/scrolling_a_overflowauto_element_on_a_touch_screen_device/
Vertical now has a simplified CSS solution, doesn't work for horizontal DIV scroll on mobile though:
-webkit-overflow-scrolling: touch
The question also asks for mouse-grab on desktop, which can be accomplished with Nice Scroll, and does work in tandem with my solution above if you need it:
https://github.com/inuyaksa/jquery.nicescroll
var nice = $("#mydiv").getNiceScroll({horizrailenabled: true, hwacceleration: true});

This is possible with
http://labs.rampinteractive.co.uk/touchSwipe/demos/Page_scrolling.html
$('body').swipe({
swipe: function(event, direction, distance, duration, fingerCount) {
switch (direction) {
case 'left':
// Code here
break;
case 'right':
//Code here
break;
}
},
allowPageScroll: "vertical"
});

Related

hammer.js - need default zoom behaviour + swipe

I want to make it so swiping over an image will take a user to the previous or next image, depending on the direction of the swipe. That part works great, BUT it removes the ability to zoom in on the image. According to this answer the solution is to enable touchAction: 'auto', but that breaks hammer.js entirely, preventing it from working for swipe at all.
Code:
<script src="../js/hammer.min.js"></script>
<script>
$('.imagecontainer').each(function(){
var options = {
touchAction: 'auto',
};
var mc = new Hammer(this, options);
mc.on("swipeleft", function() {
navTo('jsnavnext');
return false;
});
mc.on("swiperight", function() {
navTo('jsnavprev');
return false;
});
});
</script>
I can get pinch-zoom to work in the least user-friendly way possible with touchAction: 'pan-y' (basically requires the user to make a wacky circle motion), but that's not...great. Ideally I'd like hammer.js to ONLY add swipe navigation and not touch any other default behaviour.
I had a problem that was similar. I wanted to be able to detect swipes on a div and also be able to pinch zoom on the div. The swipe listener alone seemed to disable zoom. Adding touchAction:'auto' allowed me to pinch zoom, but it read would both fire my swipe action and scroll (I didn't want it to scroll, just wanted to fire the swipe action). The key part of the solution I have here is using recognizeWith, and then call preventDefault on panstart and panmove. This leaves me with just the desired pinch zoom and swipes
Here's my solution:
var target = document.getElementById("target-div")
var mc = new Hammer(target, {touchAction: 'auto' });
mc.add( new Hammer.Pan({
direction: Hammer.DIRECTION_ALL,
threshold: 0 }) );
mc.add(new Hammer.Swipe({
direction: Hammer.DIRECTION_ALL,
threshold: 1,
velocity:0.1
})).recognizeWith(mc.get('pan'));
mc.on('panstart panmove', function(ev) { ev.preventDefault(); });
mc.get('pinch').set({ enable: true });
mc.on("pinch", function(ev) {
//do anything you need to here, or not
});
mc.on("swipeleft", function(ev) {
//do something
}
mc.on("swiperight", function(ev) {
//do something
}
I am still new to hammer.js, but I have noticed a lot of issues on github where people are trying to combine multiple gestures on the same element. I hope this helps!

Horizontal One-Page Site: Mobile-Webkit Scrolling & Swiping Issues

Here is a basic demo of what I'm working with: http://jsfiddle.net/3N8wY/9/
Issue #1
If you view that link from a stock Android browser, or (more importantly) an iOS device, the website will not scroll. It does this odd fidgety/pulse thing and goes no where. It will sometimes scroll if you choose a link way on down the line, but it never ends up in the right spot.
I believe this has to do with the JS. When I tried it on my phone, I noticed it wasn't hashing the new value of the selected link.
JS
$(document).ready(function () {
$('.main-nav').on('click', function (e) {
e.preventDefault();
var toTarget = $(this).attr('href');
history.pushState(null, null, toTarget);
$(window).triggerHandler('hashchange');
});
});
$(window).on('hashchange', function () {
if(!window.location.hash) return;
var $target = $(window.location.hash);
console.log($target);
$('html, body').stop().animate({
scrollLeft: $target.offset().left,
scrollTop: $target.offset().top
}, 900, 'swing');
});
CREDIT FOR JS - Horizontal One-Page site won't go "backwards" to previous DIV
Issue #2
If you swipe a little left or right, it moves the page. I do not want that. Setting the overflow to hidden has not helped with swiping.
Ideally, if the user swiped enough right or left, it would "snap" the page in the desired direction, and then push the correct hash value. If they didn't swipe enough, it would snap back to the current page.
Having said that, I will be quite happy with it if it just doesn't move at all. I had envisioned that the user would use the menu to navigate, and only be able to scroll up and down.
Somewhat off-topic
Does anyone have a suggestion for a desktop browser that closely emulates the browser in an iOS device? I believe that webkit driving the stock Android browser is very similar, so I think I'd kill two birds here if I could get a hold of that for testing. On another project, I noticed that my desktop version of Safari seemed to deliver very different results than what I'd find on an iOS device (absolutely positioned elements behaved differently with respect to "top/margin-top" in each respective browser).
Thank you very much in advance for reading and contributing! I am extremely appreciative and grateful.
Issue #1
Turns out that I didn't have Modernizr installed correctly (hadn't included the no-js class in the html tag), which once rectified, solved the hashing issue I was running into on some stock Android browsers.
After that was fixed, I still ran into the odd scrolling behavior. Often, the page would scroll to the desired location, but then jump back. Upon further research, I came across this:
Jquery Animate ScrollLeft doesnt work on ipad.
That seemed to fix the undesired scrolling behavior for some of the poor performers, but not on iOS devices. This could have something to do with it, ScrollLeft and ScrollTop on Ipad using animate chain (jQuery), but I've figured out something else that works (will post below).
So far as I can tell, iOS devices (7+) automatically scroll to top BEFORE any scrollLeft animation. I don't have access to any physical devices, but I do have access to an iMac, where I was able to get ahold of the iOS Simulator and observed the unwanted scrolling behavior. I tried unlinking the two scrolling actions (left & top, as most posts will suggest you try), but that didn't make a difference.
It might have had something to do with what I was scrolling (ie the body or the html), I read that in a few posts, but messing with that rendered no useful results.
As I was testing, I realized that by only scrolling left, my script finally functioned "properly".
The interesting bit is that I noticed that the browser would scroll to top AUTOMATICALLY BEFORE horizontally scrolling to my target. So, if they update their code to make scrollLeft function properly, I'll have to go back and add a scrollTop function. For the the time being...
Not exactly a "fix" (the browser behaving inappropriately working to my "benefit", worrisome), but I'll take it.
Issue #2
Just to clarify, it was especially tricky to tackle this one because the page NEEDS to be able to scroll left and right (what with the horizontal layout and all), but only when I want it to.
As far as attempting to disable swiping, I really came up short. The closest I got there was with a plugin called touchSwipe; however, that broke too much (CSS-layout in some mobile browsers), and I couldn't seem to re-enable the tapping of non-link('a') assets.
What I ended up doing is creating a function that would monitor the horizontal scroll position of the window, and reposition the window if it changed. Seems a little buggy in some browsers, but it seems like I'm close to making 'everybody' happy.
EDIT: Changed the function to the much more compliant window.scrollTo(), just had to fetch positions before I fired it. Haven't tested a browser that didn't like it (so far, fingers crossed).
Side note
Lastly, when I got to RWD testing...
I was spoiled with the 'Resize Window' plugin for Chrome, and didn't realize the lackluster availability of working plugins for other browsers. So, I created a testbed full of 20 or so iframes of differing proportions, set to match the most popular device dimensions.
When I got down to mobile device dimensions, I realized that the browser's scrollbar was skewing my proportions. I'd looked into custom scrollbars before, so I delved back into it to attempt to equalize the variable all across the field.
After trying many different plugins, 'nicescroll' was the only one I could get working properly (http://nicescroll.areaaperta.com/). If you're going to use it, be sure to run a mobile test (http://www.detectmobilebrowsers.com/), and only run it on non-mobile devices (admittedly, this script seems to fail at picking up some mobile browsers, but it's better than nothing). All the mobile browsers I tested already had a similar scrollbar in place (by default), so it's completely unnecessary (plus it breaks some mobile browsers).
Working JS
$(document).ready(function() {
var loadedTarget = $(window.location.hash);
function unbindWindow() { $(window).unbind('scroll'); }
function repositionWin() {
unbindWindow();
var targetPosLeft = loadedTarget.offset().left;
$(window).on('scroll', function(e) {
var alteredPosLeft = $(window).scrollLeft();
var alteredPosTop = $(window).scrollTop();
if (alteredPosLeft != targetPosLeft) {
window.scrollTo(targetPosLeft, alteredPosTop),
unbindWindow(), // MAY BE UNNECESSARY, IOS SCARED ME INTO IT, SAME AS BELOW
repositionWin();
}
});
}
function browserResult() {
if (jQuery.browser.mobile === true) {
$('body').css({"overflow-x":"hidden","overflow-y":"scroll"});
repositionWin();
}
else {
setTimeout ((function(){
$("html").niceScroll({
cursorcolor: '#231f20',
cursoropacitymax: '0.5',
scrollspeed: '100',
mousescrollstep: '50'
});
}), 300);
setTimeout (repositionWin, 300);
}
}
browserResult();
$('.main-nav-link').click(function(e) {
e.preventDefault();
var toTarget = $(this).attr('href');
history.pushState(null, null, toTarget);
// CODE SPECIFIC TO PROJECT (NAMELY FLEXSLIDER PAUSE/PLAY STUFF) OMITTED
$(window).triggerHandler('hashchange');
});
});
$(window).on('hashchange', function () {
if(!window.location.hash) return;
var target = $(window.location.hash);
var targetHash = window.location.hash;
var iOS = ( navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false );
var currentPosition = $(window).scrollLeft();
var targetPosLeft = target.offset().left;
var targetPosTop = target.offset().top;
function unbindWindow() { $(window).unbind('scroll'); }
function repositionWin() {
unbindWindow();
$(window).on('scroll', function() {
var alteredPosLeft = $(window).scrollLeft();
var alteredPosTop = $(window).scrollTop();
if (alteredPosLeft != targetPosLeft) {
window.scrollTo(targetPosLeft, alteredPosTop),
unbindWindow(),
repositionWin();
}
});
}
function fadePages() {
if (targetPosLeft == currentPosition) {
}
else {
function fadePageOut() {
$('.page-container').stop(true,false).animate({
opacity: "0.25",
transition: "opacity 0.1s 0.0s ease"
});
}
function fadePageIn() {
$('.page-container').stop(true,false).animate({
opacity: "1.0",
transition: "opacity 0.3s 0.0s ease"
});
}
fadePageOut();
setTimeout (fadePageIn, 900);
}
}
function pageChange() {
if (jQuery.browser.mobile === true) {
if (iOS === true) {
unbindWindow();
$('html,body').stop(true,false).animate({
scrollLeft: targetPosLeft}, 1400);
setTimeout (repositionWin, 1500);
}
else {
unbindWindow();
$('html,body').stop(true,false).animate({
scrollLeft: targetPosLeft}, 1200, function() {
$(this).stop(true,false).animate({
scrollTop: targetPosTop
}, 200, repositionWin);
});
}
}
else {
fadePages();
unbindWindow();
$('html,body').stop(true,false).delay(100).animate({
scrollLeft: targetPosLeft,
scrollTop: targetPosTop
}, 1300, repositionWin);
}
}
// WAITING FOR OTHER ANIMATIONS TO COMPLETE SO THAT MOBILE DEVICES AREN'T TOO OVERLOADED
if ($('#mini-site-menu-button-container').is(':visible') === true && $('#main-menu-wrapper').hasClass('show-main-menu') === true) {
setTimeout (pageChange, 300)
}
if ($('.footer-container').is(':visible') === true) {
setTimeout (pageChange, 500)
}
if ($('.form-instructions-wrapper').hasClass('expand-form-instruct') === true) {
setTimeout (pageChange, 500)
}
if ($('.quick-quote-container').hasClass('toggle-open') === true) {
setTimeout (pageChange, 500)
}
if ($('#mini-site-menu-button-container').is(':visible') === false && $('.footer-container').is(':visible') === false && $('.form-instructions-wrapper').hasClass('expand-form-instruct') === false && $('.quick-quote-container').hasClass('toggle-open') === false) {
pageChange();
}
if ($('#main-menu-wrapper').hasClass('show-main-menu') === false && $('.footer-container').is(':visible') === false && $('.form-instructions-wrapper').hasClass('expand-form-instruct') === false && $('.quick-quote-container').hasClass('toggle-open') === false) {
pageChange();
}
});
Cheers.
I'll update as time goes on, or if I find a better solution to either of the issues. I had zero programming experience actually writing any of my own code (and this isn't all "mine") before this (changing selectors was pretty much the extent of my "skills"), so please excuse any glaring mistakes.

Scroll a page with touch events

I have a website page and I've added to the body of the page touch events.
More exactly for swipe right and swipe left. Since the event listener is added to the body of the page and I have added event.preventDefault(); i can't scroll the page any more.
How can i scroll the page in the browser ?
P.S. The code is pure javascript / library agnostic.
Edit #1. This site viewed in mobile seems to do it http://swipejs.com/ . It slides the tabs right to left and back as well as scroll the website. I just can't seen in the code how :(
Use iscroll plugin. it's help to you.
see example : http://cubiq.org/dropbox/iscroll4/examples/simple/
Unfortunately there is no easy answer. The best way is to build smart gesture recognizers. Or use something like this (for Safari Mobile):
http://mud.mitplw.com/JSGestureRecognizer/#single-gesture
You will notice that when you are touching a gesture recognizer, there is no scrolling. However, you could make the callback of a recognizer scroll the page.
Wondering why it only says it supports Safari mobile? That's because Safari mobile has its own set of touch events. However, you can use it as a start and try to add support for other platforms.
I have the same problem that swiping without "preventDefault()". Because I want to achieve a pulltorefresh's effect, I can only prevent the pulldown event but not pullup. The code like this:
function touchBindMove(evt){
//evt.preventDefault();
try{
var deviceHeight = window.innerHeight;
var touch = evt.touches[0]; //获取第一个触点
var x = Number(touch.pageX); //页面触点X坐标
var y = Number(touch.pageY); //页面触点Y坐标
//记录触点初始位置
if((y - offsetStart) > 0 && document.body.scrollTop == 0){
evt.preventDefault();
var page = document.getElementsByClassName('tweet-page')[0];
var rate = 0;
end = x;
offsetEnd = y;
rate = (offsetEnd - offsetStart) / (2 * deviceHeight);
//tool.print(rate);
easing.pullMotion(page, rate);
}
}catch(e){
alert(e.message);
}
}
"y - offsetStart" judges whether the event is pulldown and "document.body.scrollTop == 0" judges the scrollbar is in the middle or not.
Maybe it can help you a little bit.

ios6 touch event handling javascript

I am trying to improve the touchwipe js. where i want to hanlde only horizontal scroll and if its any vertical scrolling comes up let browser handle my fix works fine in ios5 but in ios6 its getting frezee any idea ?
http://archive.plugins.jquery.com/project/Touchwipe-iPhone-iPad-wipe-gesture
Please find code following
function onTouchMove(e) {
//var e = (e.changedTouches != undefined)? touches[0] : e;
if(isMoving) {
var x = e.touches[0].pageX;
var dx = startX - x;
if(Math.abs(dx) >= config.min_move_x)
{
if(config.preventDefaultEvents) {
e.preventDefault();
}
cancelTouch();
if(dx > 0) {
config.wipeLeft();
}
else {
config.wipeRight();
}
return false;
}
else {
console.log('y moving.. ');
// attachTouch();
return true;
}
}
}
Just adding some more details .
I am trying to animate my list horizontal by BXslider and its works fine want to support the touch events so added the touch wipe jquery . But adding it give 1 issue that's horizontal scroll animation works ok but touch page vertical scroll not works.
So i tried to fixed issue by return true; if its only Y move, at touchwipe js. This fix working fine on android4 and ios5 but fails in ios6 if i scroll my page vertically little aggressively(moving finger in oval/circle for 4-5 sec) its freez bxslider animation.
further debugging give inside that bxslider.gotoNextSlide()-> calling the Jquery $parent.animate() method for sliding which stop working in ios6 after aggressive scroll.
Now i m stuck and not able to understand why this animate() stops working ??
EG >>>
http://jsfiddle.net/qtCHx/
This probably happens due to this bug:
http://bugs.jquery.com/ticket/12620
A temporary solution here:
https://gist.github.com/3798925

Can I disable a horizontal mouse scroll with JS?

I am building a one page site that uses javascript to navigate vertically and horizontally across the page to see different content.
I want the user to be able to scroll up and down but not horizontally (currently the user can scroll horizontally in FireFox and see content they shouldn't be able to see unless they use the navigation.
Unfortunately I can't use overflow-x: hidden; because it interferes with the smooth-scroll JS I am using.
I did find some script (below) to disable any mouse wheel movement but I only want to disable the horizontal movement. Can anyone help?
document.addEventListener('DOMMouseScroll', function(e){
e.stopPropagation();
e.preventDefault();
e.cancelBubble = false;
return false;
}, false);
I ran into this same problem as well, the "overflow-x:hidden" CSS trick is nice, but it doesn't work for the horizontal scrolling capability of the Mac Mouse (FF only). The code you have works fine, but obviously kills both vertical and horizontal scrolling. I think the extra bit you need there is to check the "e.axis" property. Here's what I have:
document.addEventListener('DOMMouseScroll', function(e) {
if (e.axis == e.HORIZONTAL_AXIS) {
e.stopPropagation();
e.preventDefault();
e.cancelBubble = false;
}
return false;
}, false);
Hope that helps!
Well, your code work only in firefox, so here is a more universal solution, but it's also kill the vertical scroll and so far I couldn't figure out how to stop that.
if(window.addEventListener){
window.addEventListener('DOMMouseScroll',wheel,false);}
function wheel(event){
event.preventDefault();
event.returnValue=false;}
window.onmousewheel=document.onmousewheel=wheel;
After some experimentation, this bit of code works
$(window).bind('mousewheel', function(e){
if(e.originalEvent.wheelDeltaX != 0) {
e.preventDefault();
}
});
$(document).keydown(function(e){
if (e.keyCode == 37) {
e.preventDefault();
}
if (e.keyCode == 39) {
e.preventDefault();
}
});
This prevents the OSX magic mouse, track pad and default arrow keys from causing horz scrolling in safari, chrome and ff as of their latest release.
I can make no claim to this being the best solution, but it works. I fear it may cause performance issues as its comparing the x-axis delta of wheel scroll to 0.
You can do it simply with css styles.
<body style=" overflow-x:hidden; ">
<style>
body { overflow-x:hidden; }
</style>

Categories

Resources