Disable Chrome's pull-to-refresh on iPhone - pull-to-refresh

I am implementing a drawing app on my site and trying to prevent overscroll while the user draws on the canvas. Despite trying several reported solutions, I cannot disable Chrome's pull-to-refresh.
According to https://developers.google.com/web/updates/2017/11/overscroll-behavior, the following one line of css should do the trick..yet pull-to-refresh and an annoying user experience persists. Any ideas?
<!DOCTYPE html>
<html>
<style type="text/css">
body {
/* Disables pull-to-refresh but allows overscroll glow effects. */
overscroll-behavior-y: contain;
}
</style>
<body>
<h1>Simple Site</h1>
</body>
<script type="text/javascript">
</script>
</html>

I had the same problem. I found that CSS property only works on chrome-android.
Finally, I successfully prevent pull-to-refresh on chrome-ios through the following:
<script>
function preventPullToRefresh(element) {
var prevent = false;
document.querySelector(element).addEventListener('touchstart', function(e){
if (e.touches.length !== 1) { return; }
var scrollY = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
prevent = (scrollY === 0);
});
document.querySelector(element).addEventListener('touchmove', function(e){
if (prevent) {
prevent = false;
e.preventDefault();
}
});
}
preventPullToRefresh('#id') // pass #id or html tag into the method
</script>

For newer version of Chrome v75.0.3770.103 on IOS
preventDefault()
does no longer disable pull-to-refresh.
Instead, you can add in
{passive:false}
as additional option into the event listener.
E.g.
window.addEventListener("touchstart", eventListener, {passive:false});

In newer version of chrome in IOS preventDefault(); is no longer disables pull to refresh.
For latest, you can just add inobounce js cdn to your header of the page you want to disable pull to refresh. This will do the magic.
<script src="https://cdnjs.cloudflare.com/ajax/libs/inobounce/0.2.0/inobounce.js"></script>

The only thing that worked for me was iNoBounce.
Example React snippet:
import 'inobounce'
...
<div style={{
height: windowHeight,
WebkitOverflowScrolling: 'touch',
overflowY: 'auto' }}
>Content goes here</div>

Related

How can I properly position a cursor on IOS11 Safari in popup forms?

After we upgraded my iPhone to IOS11, I started seeing a cursor in a random position in my login window. This also happens on Chrome / IOS11. The position of the cursor is marked red on screenshots below.
Try adding position: fixed to the body of the page.
Piggybacking off of ybentz's answer. If you use the bootstrap modal, you can add this to your main.js file:
var savedScrollPosition;
$(document).on('show.bs.modal', '.modal', function() {
savedScrollPosition = $(window).scrollTop();
});
$(document).on('hidden.bs.modal', '.modal', function() {
window.scrollTo(0, savedScrollPosition);
});
And then this to your css because you'll already have the modal-open class being added anytime the modal pops:
body.modal-open {
position: fixed;
width: 100%;
}
Thanks for the help ybentz!! I would've responded to your comment, but I don't have the reputation to do so yet.
Ignacios Answer solved the Problem for me.
If i show an overlayer/modal i add the class fixed to the body.
Also add to css this rule:
body.fixed{
position: fixed;
}
I had the same problem and the position: fixed solution on the body does solve it so that's great. One thing to note though is that adding the class to the body causes the browser to "jump" to the top of the page so when you remove it when the popup/modal is closed it might be confusing for the user.
If your popup/modal is full screen on iOS what you can do to fix it is save the scroll position before adding the position: fixed class with something like this (using jQuery but can be done easily with vanilla js):
var savedScrollPosition = $(window).scrollTop()
$('body').addClass('has-fullscreen-modal')
and then restore it on popup close like this:
$('body').removeClass('has-fullscreen-modal')
window.scrollTo(0, savedScrollPosition)
and your css will be
body.has-fullscreen-modal {
position: fixed;
}
Hope that helps!
Personally, position: fixed scroll to top automatically. Quite annoying !
To avoid penalizing other devices and versions I apply this fix only to the appropriate versions of iOS.
**VERSION 1 - All modals fix**
For the javascript/jQuery
$(document).ready(function() {
// Detect ios 11_x_x affected
// NEED TO BE UPDATED if new versions are affected
var ua = navigator.userAgent,
iOS = /iPad|iPhone|iPod/.test(ua),
iOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);
// ios 11 bug caret position
if ( iOS && iOS11 ) {
// Add CSS class to body
$("body").addClass("iosBugFixCaret");
}
});
For the CSS
/* Apply CSS to iOS affected versions only */
body.iosBugFixCaret.modal-open { position: fixed; width: 100%; }
**VERSION 2 - Selected modals only**
I modified the function to fire only for selected modals with a class .inputModal
Only the modals with inputs should be impacted to avoid the scroll to top.
For the javascript/jQuery
$(document).ready(function() {
// Detect ios 11_x_x affected
// NEED TO BE UPDATED if new versions are affected
(function iOS_CaretBug() {
var ua = navigator.userAgent,
scrollTopPosition,
iOS = /iPad|iPhone|iPod/.test(ua),
iOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);
// ios 11 bug caret position
if ( iOS && iOS11 ) {
$(document.body).on('show.bs.modal', function(e) {
if ( $(e.target).hasClass('inputModal') ) {
// Get scroll position before moving top
scrollTopPosition = $(document).scrollTop();
// Add CSS to body "position: fixed"
$("body").addClass("iosBugFixCaret");
}
});
$(document.body).on('hide.bs.modal', function(e) {
if ( $(e.target).hasClass('inputModal') ) {
// Remove CSS to body "position: fixed"
$("body").removeClass("iosBugFixCaret");
//Go back to initial position in document
$(document).scrollTop(scrollTopPosition);
}
});
}
})();
});
For the CSS
/* Apply CSS to iOS affected versions only */
body.iosBugFixCaret.modal-open { position: fixed; width: 100%; }
For the HTML
Add the class inputModal to the modal
<div class="modal fade inputModal" tabindex="-1" role="dialog">
...
</div>
Nota bene
The javascript function is now self-invoking
REF : iOS 11 Safari bootstrap modal text area outside of cursor
I have fixed this issue with this CSS
#media(max-width:767px) {
body {
position:fixed !important;
overflow:auto !important;
height:100% !important;
}
}

Using drag and drop in phonegap installed

I'm using simple HTML code to work on a drag and drop function as part of my IOS app. This piece of code works perfectly in the browser however when I copy it into my xcode file the image won't drag.
<!DOCTYPE HTML>
<html>
<head>
<style type="text/css">
#div1 {width:350px;height:70px;padding:10px;border:1px solid #aaaaaa;}
</style>
<script>
function allowDrop(ev)
{
ev.preventDefault();
}
function drag(ev)
{
ev.dataTransfer.setData("Text",ev.target.id);
}
function drop(ev)
{
ev.preventDefault();
var data=ev.dataTransfer.getData("Text");
ev.target.appendChild(document.getElementById(data));
}
</script>
</head>
<body>
<p>Drag the image into the rectangle:</p>
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<br>
<img id="drag1" src="images/face.png" draggable="true" ondragstart="drag(event)" width="336" height="69">
</body>
</html>
Reading over Apple's webview capabilities document states that you have to set a CSS property for this to work.
From the docs:
Making an Element Draggable
WebKit provides automatic support to let users drag common items, such as images, links
and selected text. You can extend this support to include specific elements on an HTML
page. For example, you could mark a particular div or span tag as draggable.
To mark an arbitrary element as draggable, add the -webkit-user-drag attribute
(previously -khtml-user-drag) to the style definition of the element. Because it is a
cascading style sheet (CSS) attribute, you can include it as part of a style definition
or as an inline style attribute on the element tag. The values for this attribute are
listed in Table 1.
So standard draggables will work out of the box, but other elements like div or span require the -webkit-user-drag attribute to be appended.
Example:
#drag1 { -webkit-user-drag: element; }
Source: https://developer.apple.com/library/mac/documentation/AppleApplications/Conceptual/SafariJSProgTopics/Tasks/DragAndDrop.html
This code is what worked in the end.
<script>
var nodeList = document.getElementsByClassName('contents');
for(var i=0;i<nodeList.length;i++) {
var obj = nodeList[i];
obj.addEventListener('touchmove', function(event) {
var touch = event.targetTouches[0];
// Place element where the finger is
event.target.style.left = touch.pageX + 'px';
event.target.style.top = touch.pageY + 'px';
event.preventDefault();
});
}
</script>

Easeljs addEventListener do not work when bitmap added to stage

I do not understand, why eventlistener (copied from demo) does not work, If I add an bitmap to stage. It seems that bitmap causes problem, because if I add another circle etc. the click works fine.
See example:
<!DOCTYPE html>
<html>
<head>
<title>EaselJS demo: Simple animation</title>
<link href="../_shared/demo.css" rel="stylesheet" type="text/css">
<script src="http://code.createjs.com/easeljs-0.7.0.min.js"></script>
<script>
var stage, circle;
var counter = 0;
var ticounter = 0
var images = []
var mytext = 'kk';
var lepakko;
var mx = 0;
function init() {
stage = new createjs.Stage("demoCanvas");
var circle = new createjs.Shape();
circle.graphics.beginFill("red").drawCircle(0, 0, 50);
circle.x = 500;
circle.y = 500;
stage.addChild(circle);
stage.update();
lepakko = new createjs.Bitmap("halloween-bat.png");
//Click works, if line below is commented out, why?
//stage.addChild(lepakko);
circle.addEventListener("click",circle_event);
stage.update();
}
function circle_event(event) {
alert("clicked");
};
</script>
</head>
<body onLoad="init();">
<canvas id="demoCanvas" width="700" height="700">
alternate content
</canvas>
</body>
</html>
The click should not work by default. EaselJS library needs explicit enabling of the mouseover event. You need to add:
stage.enableMouseOver(20);
after creating the stage. To change the cursor to a pointer when it's over the object there is a property in EaselJS called cursor:
// doesn't work, even if a function is decleared outside
// circle.addEventListener("mouseover", function() { document.body.style.cursor = "pointer"; });
// this works
circle.cursor = "pointer";
Method enableMouseOver is documented on EaselJS website. Do note that listening to mouseover and other events in EaselJS is a lot more demanding for a web browser.

iframe prevents iScroll scrolling on mobile Safari

I am using iScroll on my mobile enable website (using iPhone here) to scroll inside a div.
In this this div, I have an iframe with a fixed height like this:
<body>
<div id="iscroller">
<iframe id="theIframe"></iframe>
Other stuff
</div>
</body>
Now, while scrolling within the div, everything works as expected but I cannot scroll when the scrolling gesture begins on the iframe.
The problem is described here pretty well: https://github.com/cubiq/iscroll/issues/41
So, I used the css workaround from that post by applying pointer-events:none to the iframe.
Now scrolling works perfectly but I cannot click any links which are defined within the iframe because all click/touch events on the iframe seems to be blocked due to pointer-events: none.
So, I thought:
"Ok, while the user scrolls, I need pointer-events:none. If he is
not scrolling (and instead clicking), I must set pointer-events:auto
in order to let the click/touch events pass."
So I did this:
CSS
#theIframe{pointer-events:none}
JavaScript
$("#theIframe").bind("touchstart", function(){
// Enable click before click is triggered
$(this).css("pointer-events", "auto");
});
$("#theIframe").bind("touchmove", function(){
// Disable click/touch events while scrolling
$(this).css("pointer-events", "none");
});
Even adding this doesn't work:
$("#theIframe").bind("touchend", function(){
// Re-enable click/touch events after releasing
$(this).css("pointer-events", "auto");
});
No matter what I do: Either scrolling doesn't work or clicking the link inside the iframe doesn't work.
Doesn't work. Any ideas?
I found the perfect solution. Works great on iOS and Android.
The basic idea is to put a div layer on top of that iframe. This way scrolling works smoothly.
If the user wants to tap/click on an element on that iframe I simply catch that click on the layer, save the x and y coordinates and trigger a click event on the iframe's content at these coordinates:
HTML:
<div id="wrapper">
<div id="layer"></div>
<iframe id="theIframe"></iframe>
</div>
Other stuff
CSS:
#layer{
position:absolute;
opacity:0;
width:100%;
height:100%;
top:0;
left:0;
right:0;
bottom:0;
z-index:2
}
JavaScript:
$('#layer').click(function(event){
var iframe = $('#theIframe').get(0);
var iframeDoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;
// Find click position (coordinates)
var x = event.offsetX;
var y = event.offsetY;
// Trigger click inside iframe
var link = iframeDoc.elementFromPoint(x, y);
var newEvent = iframeDoc.createEvent('HTMLEvents');
newEvent.initEvent('click', true, true);
link.dispatchEvent(newEvent);
});
I found a solution for this, it happens to be close to what other guys already mentioned on github but this may be useful for whoever wants to find a fast working resolution for this problem.
I'm assuming a few things, like there's only one iscroll container, here represented as ID. This is not properly tested and needs refactor. It's working in my project, but I changed it here slightly for the example but I guess you'll easily understand what you need to do:
var $iscroll = $('#iscroll');
document.addEventListener('touchstart', function(e) {
if ($iscroll.find('iframe').length > 0){
$.each($iscroll.find('iframe'), function(k,v){
var $parent = $(v).parent().first();
if ($parent.find('.preventTouch').length == 0){
$('<div class="preventTouch" style="position:absolute; z-index:2; width:100%; height:100%;"></div>')
.prependTo($parent);
};
$parent
.css('position', 'relative').css('z-index', 1);
});
$iscroll.find('.preventTouch').on('click', function(e){
e.preventDefault();
e.stopPropagation();
return false;
});
};
};
document.addEventListener('touchend', function(e) {
if ($iscroll.find('iframe').length > 0){
setTimeout(function(){
var $iscroll = $('#iscroll');
$iscroll.find('.preventTouch').remove();
$iscroll.find('iframe').css('z-index', '');
$iscroll.find('.preventTouch').off('click');
}, 400);
};
};
Thanks for looking!

ipad safari: disable scrolling, and bounce effect?

I'm working on a browser based app, currently I'm developing and styling for the ipad safari browser.
I'm looking for two things on the ipad: How can I disable vertical scrolling for pages that don't require it? & how can I disable the elastic bounce effect?
This answer is no longer applicable, unless you are developing for a very old iOS device... Please see other solutions
2011 answer: For a web/html app running inside iOS Safari you want something like
document.ontouchmove = function(event){
event.preventDefault();
}
For iOS 5 you may want to take the following into account: document.ontouchmove and scrolling on iOS 5
Update September 2014:
A more thorough approach can be found here: https://github.com/luster-io/prevent-overscroll. For that and a whole lot of useful webapp advice, see http://www.luster.io/blog/9-29-14-mobile-web-checklist.html
Update March 2016: That last link is no longer active - see https://web.archive.org/web/20151103001838/http://www.luster.io/blog/9-29-14-mobile-web-checklist.html for the archived version instead. Thanks #falsarella for pointing that out.
You can also change the position of the body/html to fixed:
body,
html {
position: fixed;
}
To prevent scrolling on modern mobile browsers you need to add the passive: false. I had been pulling my hair out getting this to work until I found this solution. I have only found this mentioned in one other place on the internet.
function preventDefault(e){
e.preventDefault();
}
function disableScroll(){
document.body.addEventListener('touchmove', preventDefault, { passive: false });
}
function enableScroll(){
document.body.removeEventListener('touchmove', preventDefault);
}
You can use this jQuery code snippet to do this:
$(document).bind(
'touchmove',
function(e) {
e.preventDefault();
}
);
This will block the vertical scrolling and also any bounce back effect occurring on your pages.
overflow: scroll;
-webkit-overflow-scrolling: touch;
On container you can set bounce effect inside element
Source: http://www.kylejlarson.com/blog/2011/fixed-elements-and-scrolling-divs-in-ios-5/
I know this is slightly off-piste but I've been using Swiffy to convert Flash into an interactive HTML5 game and came across the same scrolling issue but found no solutions that worked.
The problem I had was that the Swiffy stage was taking up the whole screen, so as soon as it had loaded, the document touchmove event was never triggered.
If I tried to add the same event to the Swiffy container, it was replaced as soon as the stage had loaded.
In the end I solved it (rather messily) by applying the touchmove event to every DIV within the stage. As these divs were also ever-changing, I needed to keep checking them.
This was my solution, which seems to work well. I hope it's helpful for anyone else trying to find the same solution as me.
var divInterval = setInterval(updateDivs,50);
function updateDivs(){
$("#swiffycontainer > div").bind(
'touchmove',
function(e) {
e.preventDefault();
}
);}
Code to To remove ipad safari: disable scrolling, and bounce effect
document.addEventListener("touchmove", function (e) {
e.preventDefault();
}, { passive: false });
If you have canvas tag inside document, sometime it will affect the usability of object inside Canvas(example: movement of object); so add below code to fix it.
document.getElementById("canvasId").addEventListener("touchmove", function (e) {
e.stopPropagation();
}, { passive: false });
none of the solutions works for me. This is how I do it.
html,body {
position: fixed;
overflow: hidden;
}
.the_element_that_you_want_to_have_scrolling{
-webkit-overflow-scrolling: touch;
}
Try this JS sollutuion:
var xStart, yStart = 0;
document.addEventListener('touchstart', function(e) {
xStart = e.touches[0].screenX;
yStart = e.touches[0].screenY;
});
document.addEventListener('touchmove', function(e) {
var xMovement = Math.abs(e.touches[0].screenX - xStart);
var yMovement = Math.abs(e.touches[0].screenY - yStart);
if((yMovement * 3) > xMovement) {
e.preventDefault();
}
});
Prevents default Safari scrolling and bounce gestures without detaching your touch event listeners.
Tested in iphone. Just use this css on target element container and it will change the scrolling behaviour, which stops when finger leaves the screen.
-webkit-overflow-scrolling: auto
https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling
improved answer #Ben Bos and commented by #Tim
This css will help prevent scrolling and performance issue with css re-render because position changed / little lagging without width and height
html,
body {
position: fixed;
width: 100%;
height: 100%
}
For those who are using MyScript the Web App and are struggling with the body scrolling/dragging (on iPad and Tablets) instead of actually writing:
<body touch-action="none" unresolved>
That fixed it for me.
You can use js for prevent scroll:
let body = document.body;
let hideScroll = function(e) {
e.preventDefault();
};
function toggleScroll (bool) {
if (bool === true) {
body.addEventListener("touchmove", hideScroll);
} else {
body.removeEventListener("touchmove", hideScroll);
}
}
And than just run/stop toggleScroll func when you opnen/close modal.
Like this toggleScroll(true) / toggleScroll(false)
(This is only for iOS, on Android not working)
Try this JS solution that toggles webkitOverflowScrolling style. The trick here is that this style is off, mobile Safari goes to ordinary scrolling and prevents over-bounce — alas, it is not able to cancel ongoing drag. This complex solution also tracks onscroll as bounce over the top makes scrollTop negative that may be tracked. This solution was tested on iOS 12.1.1 and has single drawback: while accelerating the scroll single over-bounce still happens as resetting the style may not cancel it immediately.
function preventScrollVerticalBounceEffect(container) {
setTouchScroll(true) //!: enable before the first scroll attempt
container.addEventListener("touchstart", onTouchStart)
container.addEventListener("touchmove", onTouch, { passive: false })
container.addEventListener("touchend", onTouchEnd)
container.addEventListener("scroll", onScroll)
function isTouchScroll() {
return !!container.style.webkitOverflowScrolling
}
let prevScrollTop = 0, prevTouchY, opid = 0
function setTouchScroll(on) {
container.style.webkitOverflowScrolling = on ? "touch" : null
//Hint: auto-enabling after a small pause makes the start
// smoothly accelerated as required. After the pause the
// scroll position is settled, and there is no delta to
// make over-bounce by dragging the finger. But still,
// accelerated content makes short single over-bounce
// as acceleration may not be off instantly.
const xopid = ++opid
!on && setTimeout(() => (xopid === opid) && setTouchScroll(true), 250)
if(!on && container.scrollTop < 16)
container.scrollTop = 0
prevScrollTop = container.scrollTop
}
function isBounceOverTop() {
const dY = container.scrollTop - prevScrollTop
return dY < 0 && container.scrollTop < 16
}
function isBounceOverBottom(touchY) {
const dY = touchY - prevTouchY
//Hint: trying to bounce over the bottom, the finger moves
// up the screen, thus Y becomes smaller. We prevent this.
return dY < 0 && container.scrollHeight - 16 <=
container.scrollTop + container.offsetHeight
}
function onTouchStart(e) {
prevTouchY = e.touches[0].pageY
}
function onTouch(e) {
const touchY = e.touches[0].pageY
if(isBounceOverBottom(touchY)) {
if(isTouchScroll())
setTouchScroll(false)
e.preventDefault()
}
prevTouchY = touchY
}
function onTouchEnd() {
prevTouchY = undefined
}
function onScroll() {
if(isTouchScroll() && isBounceOverTop()) {
setTouchScroll(false)
}
}
}
Consider the following architecture:
<body> <div id="root"></div> </body>
this css will work:
#root { position: fixed; height: 100%; overflow: auto; }
For those of you who don't want to get rid of the bouncing but just to know when it stops (for example to start some calculation of screen distances), you can do the following (container is the overflowing container element):
const isBouncing = this.container.scrollTop < 0 ||
this.container.scrollTop + this.container.offsetHeight >
this.container.scrollHeight
Disable safari bounce scrolling effect:
html,
body {
height: 100%;
width: 100%;
overflow: auto;
position: fixed;
}
I had an issue with grabbing the html element in the background, when a menu with scroll was open and either at the top or at the bottom at the scroll height. I tried lots of things. Setting html position to fixed was the closest I got to lock the screen, but in the PWA it resulted in a white area at the bottom, that I couldn't fix.
Finally I've found a solution, that worked for me 🎉:
html {
width: 100%;
height: 100%;
overflow: hidden;
}
body {
margin: 0;
height: calc(100vh - 1px)
overflow: hidden;
background-color: 'Whatever color you need to hide the 1px at the bottom'
}
Because it only seems to be an issue on iOS, I have targeted the devices from iPhone X to 12 Pro Max:
body {
margin: 0;
overflow: hidden;
background-color: '#TIP: You can use the color picker from the inspector';
#media only screen and (min-width: 375px) and (max-height: 926px) and (-webkit-device-pixel-ratio: 3) {
height: calc(100vh - 1px);
}
}
This is preventing any kind of scroll, touch or grab in the html or body elements, and scroll is still working in the menu or where else specified. Cheers.
body {
touch-action:none;
}
Using JQuery
// Disable
$("body").css({ "touch-action": "none" })
// Enable
$("body").css({ "touch-action": "auto" })
css overscroll-behavior is now supported in iOS 16. If you are targeting > iOS 16 devices, to prevent elastic bounce effect, add the following CSS to the html root
html {
overscroll-behavior: none;
}
Please note, the solution provided only disables elastic bounce effect when content is larger than viewport.
If you also want to completely disable scrolling in main page on iOS devices, use
html body {
overflow: hidden;
}
Similar to angry kiwi I got it to work using height rather than position:
html,body {
height: 100%;
overflow: hidden;
}
.the_element_that_you_want_to_have_scrolling{
-webkit-overflow-scrolling: touch;
}
Solution tested, works on iOS 12.x
This is problem I was encountering :
<body> <!-- the whole body can be scroll vertically -->
<article>
<my_gallery> <!-- some picture gallery, can be scroll horizontally -->
</my_gallery>
</article>
</body>
While I scrolling my gallery, the body always scrolling itself (human swipe aren't really horizontal), that makes my gallery useless.
Here's what I did while my gallery start scrolling
var html=jQuery('html');
html.css('overflow-y', 'hidden');
//above code works on mobile Chrome/Edge/Firefox
document.ontouchmove=function(e){e.preventDefault();} //Add this only for mobile Safari
And when my gallery end its scrolling...
var html=jQuery('html');
html.css('overflow-y', 'scroll');
document.ontouchmove=function(e){return true;}
Hope this helps~

Resources