iOS has a convention that double-tapping on the top bar (i.e., where the current time is displayed) scrolls the app to the top state. For example, double-tapping the top bar in Safari brings you to the top of the current web page, and double-tapping the top in Facebook/Twitter brings you to the top of the feed. It's a very useful navigation shortcut.
Let's call it a TopTap for purposes of this question.
I'm wondering how I can detect TopTaps in a JavaScript app in mobile Safari -- that is, NOT in an iOS app, but in a web page that happens to be viewed in mobile Safari.
In my particular case, I can't rely on the built-in mobile Safari TopTap behavior because my document consists of a single <canvas>
element that implements its own scrollable interface. I want to be able to detect a TopTap so that I can scroll that <canvas>
to its top state.
I've experimented with adding an onscroll
event handler, but there's no distinguishing information in that event that would let me isolate the TopTaps. Also, I can't use touch events (touchstart, etc.), because a TopTap happens in the browser/OS chrome, outside the scope of the web page.
Any ideas?
As it turns out double tapping the native status bar will trigger a scroll event on document.body
which you can in fact listen for. Trick is, as you mentioned, how can you determine if it's from a double tap or not?
In order to detect it, the body has to be scrolled down to begin with. And setting the scroll position causes a scroll event.
While it's a bunch of hacks, I've been able to get this working:
Code: https://github.com/HenrikJoreteg/holy-grail-ios-layout
Demo: http://ios-layout.surge.sh
Basically, you don't really use the <body>
as a container. You set it as position: absolute
and full height/width. Then you have some kind of container element that you set to position: fixed
that you use as your actual container for your content.
Doing this lets you programmatically scroll the body without affecting anything visual on the page at all.
Now you're set up to listen for scroll events on the body and ideally the user can't actually cause a scroll on the body except via double tapping the status bar.
Unfortunately you have to do all sorts of silly things to make this work.
100%
in the body so the body can actually has something to scroll.
overflow-y: scroll; /* has to be scroll, not auto */
-webkit-overflow-scrolling: touch;
Anyway, it's something :)
More info in the github repo, but anyway, this really does seem to work quite well for iOS 8 (haven't tested other versions of iOS).
If your target is recent iOS, you could put an element over your canvas
that is position: fixed
to the top of the viewport and use that to detect double-taps.
EDIT: I was thinking something like the below, but as Adrian points out he needs it to happen when the native browser chrome is double-tapped as well.
<!doctype html>
<html>
<head>
<style type="text/css">
canvas {
height: 1000px;
width: 320px;
}
#top-tap {
height: 16px;
left: 0;
position: fixed;
right: 0;
top: 0;
}
</style>
</head>
<body>
<canvas></canvas>
<a id="top-tap"></a>
<script type="text/javascript">
function secondTap() {
window.scrollTo(0,0)
}
document.querySelector('#top-tap').addEventListener('touchend', function (e) {
var self = this
this.addEventListener('touchend', secondTap)
setTimeout(function () {
self.removeEventListner('touchend', secondTap)
}, 100)
})
</script>
</body>
</html>
Is it possible to create a hidden native html element, the same height as the content in your canvas. Then map the scroll position on the native element to the same position within the canvas. Could also hide the canvas scroll widget.
So as far as the users concerned they only see the native scroll bar... but all the scroll events map to the canvas - including the status bar tap.
May not work if you have other HTML content on the page but might if there's only the canvas visible.
Edit: Here's a very crude prototype http://jsbin.com/cisizopi/3
I'm simulating a canvas and it's contents with divs
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With