Why hammer.js swiping does not work for iPhone - javascript

I couldn't find a more direct way to ask people who are familiar with hammer.js so i'm posting here.
So i've been working on a web app with 8thwall using hammer.js for swiping/scrolling. i've been testing it on my samsung 10 and only now testing on the iphones. The swiping/scrolling has been working fine with my samsung 10 but it doesnt work at all with iphones that i could get my hands on. I've tried iphone 8plus, iphone xr, iphone 6s. Please advise on what i need to do. Thank you.
my codes :
//SCROLLING FUNCTION
AFRAME.registerComponent('scroll-lines', {
init: function(){
var container = document.getElementById("scrolling-container");
var content = document.getElementById("button-collections");
var hammer = new Hammer(container);
var initialX = 0;
var deltaX = 0;
var offset = initialX + deltaX;
hammer.on("panleft panright", function(ev) {
deltaX = ev.deltaX;
offset = initialX + deltaX;
container.scroll(-offset, 0);
});
Hammer.on(container, "mouseup", function(e) {
initialX = offset;
});
}
})
<!--SCROLLING BUTTONS-->
<!--IN ORDER FOR THESE TO BE DISPLAYED NEED TO STYLE THE Z-INDEX: 10. REFER style.css PAGE-->
<div id="scrolling-container">
<div id="button-collections">
<div id="box-all" class="cantap"></div>
<div id="box-seremban" class="cantap"></div>
<div id="box-klang" class="cantap"></div>
<div id="box-ampang" class="cantap"></div>
<div id="box-petaling" class="cantap"></div>
<div id="box-kj" class="cantap"></div>
<div id="box-ekspres" class="cantap"></div>
<div id="box-transit" class="cantap"></div>
<div id="box-monorail" class="cantap"></div>
<div id="box-kajang" class="cantap"></div>
<div id="box-skypark" class="cantap"></div>
</div>
</div>
The CSS:
#scrolling-container{
z-index: 10;
position: absolute;
display: flex;
top: 55%;
width: 100%;
cursor: pointer;
background-color: red;
}
#button-collections{
display: flex;
flex-direction: horizontal;
overflow: scroll;
height: 150px;
padding-top: 170px;
width: 100%;
}
UPDATE: I tried the suggestions below but they did not resolve the issue. I found that if i used var hammer = new Hammer(container); it works for android not iOS but if i use var hammer = new Hammer(content); it works for both but at the mouseup function i am not able to scroll to the end for both iOS and android. using panleft, panright, panend
UPDATE 2: So since hammerjs is sorta working on the iphone, my question is sort of answered. closed question. opened a new follow up question for current situation

Well are you sure you want to use pan? Pan is for dragging basically. Swipe is called swipe in hammer.js. It could be that on android the correct gesture is triggered, but not on iphone. If you move your finger fast, its a swipe, and won't be recognized as a pan. Also instead of mouseup you should use panend probably (because maybe android fires mouseup, and iphone doesn't).
Possible events with pan are:
panstart
panmove
panend
pancancel
panleft
panright
panup
pandown

Try to use swipeleft and swiperight events instead of pan events.

Without a cursor defined on the elements iOS does not fire the mousedown or mouseup client-side events which are needed for swipe. See How to make my 'click' function work with iOS.
Assign a class ios-device to your <body> when you detect iOS and use the following style.
body.ios-device {
cursor: pointer !important; /* iOS click events don't fire without this! */
}

Related

How to select element thats between ranges?

I have side bullets that are menu scrolling to section of onepage, i did a scroll animation but i would like to change class="active" between bullets when scrolling. I know it can be easly achieved by jQuery(body,window).scroll() with $('#scrollspymenu li').each() but its really bad way. I can break out of each() function but this way i still need to get from first li element to (for example) 5th li. I thought about getting all data-target elements to some array with save offsetTop and offsetTop+height and in scroll event i will check which element is on middle of the screen. ( i mean window scroll top position + 1/2 window height)
I have no idea how to make like some kind of timeline with sections with calculated range AND what function can select (based on input) element that is attached to 2 range values.
I'm also using scrollMagic, maybe it does it better? Still would like to use pure js or jquery for future use.
I'm sorry i couldnt form a good specific question but i tried my best.
my comment
(..) I want fastest possible way to get from scrolll event to proper element. Best possible performance of the page. It's stupid when u scroll, 1 scroll tick is 100x on scroll function, and every function goes every element. For 4 elements its checks elements 400 times...
JS
var page = $("html, body");
$('#scrollspymenu a, .go-to, .go-to2').click(function(event) {
event.preventDefault();
var target = $(this).attr('data-target');
if(target){
page.on("scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove", function(){
page.stop();
});
page.animate({ scrollTop: $(target).offset().top-50 }, 700, function(){
page.off("scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove");
});
}
})
Html Side menu
<nav id="scrollspymenu">
<ul>
<li class="active"><span>Knauf Group</span></li>
<li><span>History</span></li>
<li><span>Values</span></li>
()...)
<li><span>Automobile industry</span></li>
<li><span>Solutions</span></li>
</ul>
First off, I am not 100% sure this resolves your issue, as the intent is not totally clear to me.
What THIS does is if you click on a menu, it scrolls to that associated target.
Also if you tab, shift-tab it scrolls to that associated target.
If you mouse over a menu, then scroll, it scrolls the target. I think you get the idea and if not a 100% solution perhaps you can build from this.
The console.logstuff should be removed eventually but will illustrate what is firing at what point for you.
var page = $("html,body");
$('#scrollspymenu').on('click scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove', 'a', function(event) {
// event.preventDefault();
event.stopImmediatePropagation();
var elem = $(this);
var target = elem.data('target');
$('.active').removeClass('active');
if (!!target) {
$(target).addClass('active');
elem.addClass('active');
console.log('in here');
page.off("scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove", function() {
console.log('in stop');
page.stop(); // stop any animation
});
page.animate({
scrollTop: ($(target).offset().top - 50)
}, {duration:700, start:function() {
console.log('in callback', $(target).offset().top - 50);
page.on("scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove");
}});
}
})
#pagethings {
margin-left: 200px;
}
#contentbody {
float: right;
width: 100%;
background-color: #F0F0F0;
}
#scrollspymenu {
float: left;
width: 200px;
margin-left: -200px;
background-color: #CCCCCC;
/* fix to top */
position: fixed;
/* force scroll bars */
overflow: scroll;
}
.section {
margin: 1em;
padding-bottom: 4em;
}
#clearingdiv {
clear: both;
}
.active{ background-color: #EECCAA;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="pagethings">
<nav id="scrollspymenu">
<ul>
<li class="active"><span>Knauf Group</span></li>
<li><span>History</span></li>
<li><span>Values</span></li>
<li><span>Automobile industry</span></li>
<li><span>Solutions</span></li>
</ul>
</nav>
<div id="contentbody">
<div class="section sec-company-home sec-home-knaufgroup">My Home of my grand thing</div>
<div class="section sec-company-history">History of my grand thing</div>
<div class="section sec-company-values">Values of my grand thing</div>
<div class="section sec-company-automobile">My great car of my grand thing. I might put something bigger in here, pictures etc so I fake this with some text to make it bigger. The brown cow jumped over the crazy blue moon but why is that moon blue and how can that cow breathe when jumping over
a moon and why is it "over" the moon not "around" the moon for a literal quotation?</div>
<div class="section sec-company-solutions">Solution of my grand thing, drive my great car fast</div>
</div>
</div>

mouseleave does not fire on Safari

I've this function perfectly working on chrome and firefox (both on macOs). I've checked it in safari 10.03(macOs) and it fire on mouseenter but not on mouseleave.
Function:
$(document).ready(function(){
$("#mazzo").on("mouseenter", ".pick", function() {
var immagine = $(this).data('immagine');
$("#anteprima").attr("src", immagine);
});
$("#mazzo").on("mouseleave", ".pick", function() {
$("#anteprima").removeAttr("src");
console.log("Mouse out");
});
});
HTML:
<div id="peranteprima">
<img id="anteprima" src="immagini/void.png" alt="">
</img>
</div>
<div id="mazzo">
<div class="pick" id="0" data-immagine="immagini/12345.png">
<img src="immagini/pick.png" alt="immagine box" class="box">First pick
</div>
</div>
CSS:
#peranteprima {
position: relative;
margin-left: -213px;
}
#anteprima {
position: fixed;
bottom: 75%;
top: 0%;
max-width: 215px;
height: 322px;
z-index: 10;
}
I've tried to set .attr("src", "") but the behaviour on safari it's the same. I've also tried mouseout but with no luck.
The mouseleave event reference say this:
Safari 7 fires the event in many situations where it's not allowed to,
making the whole event useless. See bug 470258 for the description of
the bug (it existed in old Chrome versions as well). Safari 8 has
correct behavior
I've checked many times for a solution here and on other sites but I've found only unanswered question or nothing that fit my problem right now.
There's a way to make it work on safari?

Fullscreen video doesn't allow scrolling on firefox

I'm using the fullscreen.js script and in one of my screens I will have a fullscreen Vimeo video. Apparently this will cause issues in FF and prevents me from scrolling up or down as soon as I reach the screen with the video. The issue was submitted to the GitHub page of the script but the author dismissed it as it's a FF issue (https://github.com/alvarotrigo/fullPage.js/issues/803).
I'm using all this with foundation CSS for the responsive video:
<div class="flex-video widescreen vimeo">
<iframe src="<?php the_sub_field('video') ?>"
width="400"
height="225"
frameborder="0"
webkitAllowFullScreen
mozallowfullscreen
allowFullScreen></iframe>
</div>
The bug is this one: https://bugzilla.mozilla.org/show_bug.cgi?id=779286 but I don't see that it was solved on FF 36 on Mac. The issue is not happening on chrome either.
Here is an example of the issue by someone else on the GitHub thread: http://jsbin.com/tunove/1/edit?html,output
The Issue:
The Mozilla bug you are looking at actually refers to the fullscreen mode API, an unrelated API that was fixed. I think the bug report you are looking for is this one:
Bug 1084121 - Mouse wheel event is captured by iframe and not propogated.
Steps to reproduce:
I have a div in which I manually capture mousewheel events, and use
that to scroll the div. Inside of this div, I have an embedded youtube
video, in an iframe.
Actual results:
While scrolling, if the mouse is over the iframe, scrolling no longer
works, because all mouse events, including mouse wheel events, are
captured by the iframe, and are not sent to the parent window.
Expected results:
The mouse wheel event should have been propagated to the parent
window. This is the behavior in chrome and safari.
Since the iframe is on a different domain, there does not appear to be
any feasible workaround for this.
This bug report is still open, and does not appear to be in the process of being implemented.
Also, according to the bug report, this behavior is not defined by any specification.
For what it's worth, I gave this bug report a vote to increase the importance. I agree, this is a user experience problem.
Workarounds:
Unfortunately, as far as directly fixing the wheel event issue goes, the suggestions in that GitHub issue are about all we have for cross-origin iframes. If the framed content were on the same domain or otherwise under your control, you could add another event listener inside the iframe, but Same-Origin Policy prevents this cross-domain.
The only options available to prevent the iframe from stealing the wheel events for cross-origin frames are:
Cover most or all of the iframe with transparent divs.
Use pointer-events: none; on the iframe. This will also prevent clicking on the video at all, so it has the same effect as covering the entire video with a transparent div.
Other Options:
This issue is apparently limited to the wheel events as it is possible to scroll a parent document while scrolling over an iframe.
<iframe src="data:text/html;charset=utf-8,%3Chtml%3E%3Cbody%3E%3Cp%3EScroll%20over%20this.%3C/p%3E%3C/body%3E%3C/html%3E" style="width: 100%; height: 100px;"></iframe>
<div style="background: red; width: 20px; height: 5000px;"></div>
fullPage.js is not structured this way, but if a parent element to the iframe were actually a scrollable element, it would be possible to listen for the scroll event and react to that.
It's a little shaky, but here's an example of something similar using the scroll event instead of the wheel event.
Example (JSFiddle):
var autoScrolling = false;
$('.wrap').on('scroll', function(e) {
if (autoScrolling) {
return;
}
//Get this element and find the number of children.
var $this = $(this);
var children = $this.children('.pane').length;
//Find the height of each pane, and the current position.
var paneHeight = this.scrollHeight / children;
var position = this.scrollTop / paneHeight;
var positionRound = Math.round(position);
//Find the target position.
var positionOff = position - positionRound;
var toShow = null;
if (positionOff < 0) {
toShow = positionRound - 1;
}
else if (positionOff > 0) {
toShow = positionRound + 1;
}
//If scrolling to a new pane, find the next one.
if (toShow !== null) {
autoScrolling = true;
$this.animate({
scrollTop: paneHeight * toShow
}, {
duration: 1000,
complete: function() {
setTimeout(function() {
autoScrolling = false;
}, 500);
}
});
}
});
html,
body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
.wrap {
height: 100%;
width: 100%;
overflow: auto;
}
.pane {
width: 100%;
height: 100%;
position: relative;
}
iframe {
background: white;
border: 0;
outline: 0;
display: block;
position: absolute;
width: 80%;
height: 80%;
left: 10%;
top: 10%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="wrap">
<div class="pane" style="background: red;">
<iframe src="data:text/html;charset=utf-8,%3Chtml%3E%3Cbody%3E%3Cp%3EScroll%20over%20this.%3C/p%3E%3C/body%3E%3C/html%3E"></iframe>
</div>
<div class="pane" style="background: green;">
<iframe src="data:text/html;charset=utf-8,%3Chtml%3E%3Cbody%3E%3Cp%3EScroll%20over%20this.%3C/p%3E%3C/body%3E%3C/html%3E"></iframe>
</div>
<div class="pane" style="background: blue;">
<iframe src="data:text/html;charset=utf-8,%3Chtml%3E%3Cbody%3E%3Cp%3EScroll%20over%20this.%3C/p%3E%3C/body%3E%3C/html%3E"></iframe>
</div>
</div>

How to prevent 'Overscroll history navigation' without preventing touchstart?

I'm implementing a swipe-base navigation, and I'm running into trouble with Chrome.
A newly implemented feature, 'Overscroll history navigation', is triggered when the page is dragged to the right, causing a jump back (to 'history -1'). To prevent this, I'd have to call .preventDefault() on touchstart, but this also disables everything from clicking links to scrolling.
How do I prevent browser UI events without interfering with the standard page?
Disabling the feature altogether by setting the appropriate flag in chrome fixes the issue, but isn't practical for a public-facing application.
chrome://flags/#overscroll-history-navigation
I eventually figured out a solution:
Chrome fires at least touchstart and touchmove before overscrolling. By tracking the direction of those events, an overscroll can be filtered from regular scrolling.
I've written up the code for this Hammer.js issue.
I used
html, body {
width: 100%;
height: 100%;
margin: 0;
overscroll-behavior: contain;
}
https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior
To leave scrolling vertical working and moving elemets horizontally you need to check if moving is more along x then y axis by comparing coordinates differences using Math.abs(), then decide to call .preventDefault() in touchmove event or not.
Sounds little strange but i think it is the only way to control this behemoth feature "Overscroll history navigation". ble.
$(function () {
function extract(e) {
if (e.changedTouches) {
e = e.changedTouches;
if (e['0'])
e = e['0'];
}
return e;
}
var div = $('div').html('Drag me left and right and see that Overscroll is disabled & drag me up and down to see that scroll still works<br /><br /><br />'.repeat(300));
var di = div.get(0); // get native dom element
var startx, lastx, lasty, l = false, active = false;
di.addEventListener("touchstart", function (e) {
active = true;
e = extract(e);
// init
lastx = e.pageX;
lasty = e.pageY;
l = parseInt(div.css('left'), 10);
startx = e.pageX;
}, false);
di.addEventListener("touchmove", function (ee) {
if (active) {
var e = extract(ee);
// check if preventDefault to cancel Overscroll history navigation only if it's needed
if (Math.abs(lastx - e.pageX) > Math.abs(lasty - e.pageY)) {
ee.preventDefault();
}
// update x y to next above calculation
lastx = e.pageX;
lasty = e.pageY;
// repositioning
div.css({left: (l + (e.pageX - startx))+'px'})
}
}, false);
di.addEventListener("touchend", function (e) {
active = false;
}, false);
});
Complete example to test on touch devices: DEMO
listen to onscroll event on the container and store the 'scrollLeft' value
this.offsetX = event.currentTarget.scrollLeft;
listen to onwheel event on the same container and use this handler
var offset = 0;
document.getElementById("1")
.addEventListener("scroll", function(event) {
offset = event.currentTarget.scrollLeft;
});
document.getElementById("1")
.addEventListener("wheel", function(event) {
// if we reach the left side of the scrollable content, and we scroll further -> block the event
if (offset === 0 && event.deltaX <= 0) {
event.preventDefault();
}
});
.container {
width: 100%;
background-color: blue;
overflow: auto;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.element {
display: inline-block;
min-width: 100px;
height: 50px;
margin: 10px;
background-color: red;
}
<div id="1" class="container">
<div class="element">
1
</div>
<div class="element">
2
</div>
<div class="element">
3
</div>
<div class="element">
4
</div>
<div class="element">
5
</div>
<div class="element">
6
</div>
<div class="element">
7
</div>
<div class="element">
8
</div>
</div>
It's 'touchmove' you will have to stopPropagation() AND preventDefault() inside the callback you can call whatever you need for checking the swipe and your navigation to happen.

trying to make navigation bar for tablet site

I'm designing a navigation bar for a tablet website. The navigation bar holds elements displayed horizontally, and I want to be able to display new elements with a swipe (kind of like a cover flow) without the window moving. This is the code I'm using now (jQuery Mobile):
//Tablet Features
$('#navHolder').bind('swipe',
function(e) {
$('#navHolder').animate({left:thisLeft - 100});
}
);
I dont think I can trigger a swipe without first disabling scroll, but I'm open to all suggestions. Please help.
Set the parent container of the element you are scrolling to overflow : hidden so no scroll-bars appear. Then swipe events should work fine since you won't be able to use native scrolling to scroll the content.
HTML --
<div id="navHolder-container">
<div id="navHolder">
<p>content in here</p>
</div>
</div>
CSS --
#navHolder {
position : absolute;
width : 1000px;
}
#navHolder-container {
position : relative;
overflow : hidden;
height : 100px;
width : 100%;
}
JS --
$(function () {
var convert = {
swipeleft : '-=100',
swiperight : '+=100'
};
$('#navHolder-container').bind('swipeleft swiperight', function(e) {
$('#navHolder').animate({ left: convert[e.type]});
});
});
Here is a demo: http://jsfiddle.net/B8PQn/1/

Categories

Resources