Custom JavaScript scrolling on iOS 8 - is it possible? - javascript

Am building a page for iOS 8 where I need to completely block built-in scrolling and scroll the page through javascript alone (Eg. using scrollTo() method).
I don't want to hide the scrollbars, just to block the scrolling action (seems like it's done through event.preventDefault() ) and implement scrolling in JavaScript.
Can this be done?

JavaScript has a scroll event, unfortunately it is not cancelable (i.e. event.preventDefault() does not work).
If however you're only using touch enabled devices and scrolling is done through touch the touchmove event will be fired upon scrolling, which is cancelable, using event.preventDefault(), and will prevent scrolling. Note that this also prevents other actions requiring touchmove such as zooming.
Another solution, which might look a bit glitchy on some devices, is to store the scroll position of the document at the place you want to lock it and go to those coordinates whenever a user is scrolling. Something like the following:
var locked = false,
posX = 0,
posY = 0;
var lock = function(){
//assign the current coordinates to the position variables
var doc = document.documentElement;
posX = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
posY = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
locked = true;
};
var unlock = function(){
locked = false;
};
document.addEventListener('scroll', function(e){
if(locked)
scrollTo(posX, posY);
});

A couple years ago I made a cordova/phonegap app using iScroll. At the time it helped me through some seriously bad scroll view issues.
You can disable scroll, force move to etc
Perhaps it could work for you.

Related

Define areas for touch events using ngTouch

I am working on an AngularJs web app and have just started using ngTouch to recognize swipe left and right, I want to use this to open and close my side bar menu. Currently it works and the menu opens and close however, I have the swipe events tied to the entire wrapper like so <div ng-style="body_style" id="wrapper" ng-swipe-right="showMenu()" ng-swipe-left="hideMenu()"> which makes the entire app the swipe area. I would like to be able to set it to just an area in the top left quadrant of the app like so:
This will allow me to set up other swipe areas for additional functionality rather than being limited to just one area.
I know that I can make a div and place it there with defined width and height but the div is going to block content in other areas so that when someone goes to "click" something they would actually be clicking the invisible div for the swipe area. I know there has to be a way around this but I cannot think of one.
tldr: How do I set up an area to capture swipe events that will not effect the interactivity of other elements that may be below it?
In the source for angular-touch, it uses a function called bind. Bind takes an element that should be watched for swipes and watches its events, such as start. So if you wanted to, you could change the source to stop executing the rest of the code if the start is outside of some x or y coordinate that you set.
You could change this code,
pointerTypes = pointerTypes || ['mouse', 'touch'];
element.on(getEvents(pointerTypes, 'start'), function(event) {
startCoords = getCoordinates(event);
active = true;
totalX = 0;
totalY = 0;
lastPos = startCoords;
eventHandlers['start'] && eventHandlers['start'](startCoords, event);
});
To something like this:
pointerTypes = pointerTypes || ['mouse', 'touch'];
element.on(getEvents(pointerTypes, 'start'), function(event) {
startCoords = getCoordinates(event);
if (startCoords.y <= 400 && startCoords.y >= 100 && startCoords.x <=500) {
active = true;
totalX = 0;
totalY = 0;
lastPos = startCoords;
eventHandlers['start'] && eventHandlers['start'](startCoords, event);
}
});
Kind of an ugly hack, but it'll work.

detect (event) attempting to scroll up/down when already at top/bottom of the page with javascript or angularJS,

so i want to detect scrolling to the top(or bottom) when i'm already at the top(or bottom) of the page. I've seen a couple questions with similar problems here, but the only answer was to detect mousewheel event.
but considering the fact that i want to detect it when it's triggered by any similar action (like pressing the up/down key, or mousewheel and touchpad scrolling, or pageUp/Down, or home/end ...) should I create eventlisteners for each and every one of them? does anyone know a better way of doing it??
This is what I have for you so far. I can detect when you hit the top or bottom regardless of how you scrolled. The issue is detecting when you are trying to keep scrolling after you reached it because the scroll event does not fire unless you actually scroll. The only thing I can think of is to programmatically scroll the screen up slightly so that you can scroll again, but I don't recommend doing that. Other than this I don't see any other way other than creating custom events for each possible way the user can scroll.
$(function(){
var lastScrollBarPos = 0;
$(window).scroll(function(event){
var browserViewportHeight = $(window).height();
var hmtlDocHeight = $(document).height();
var scrollBarPos = $(window).scrollTop();
if (scrollBarPos > lastScrollBarPos){
// downscroll code
console.log('scroll down');
if(hmtlDocHeight == browserViewportHeight + scrollBarPos){
console.log('reached bottom');
}
} else {
// upscroll code
console.log('scroll up');
if(scrollBarPos == 0){
//we are at the top and someone is scrolling
console.log('reached top');
}
}
lastScrollBarPos = scrollBarPos;
});
});

Restricting Kendo Grid to scroll in one direction at a time on touch screen

When a user is on a touch screen device, I'd like to restrict diagonal scrolling - so the idea is to force scrolling one direction at a time - horizontal or vertical.
I've set up a JS Fiddle that detects if touch scrolling is enabled and I'm able to output the x and y coordinates. But I don't see an offset or anything and figure I need that to calculate the intended direction.
I know that apple uses a directionalLockEnabled that will restrict, so I'm wondering if something like this is available in Kendo. If not, maybe there's a way I can figure out which direction the user is intending to scroll in and 'freeze' the other coordinate.
A JS fiddle I created (relevant part in the dataBound method):
http://jsfiddle.net/dmathisen/tskebcqp/
(the relevant code only works on touch... but should work if you enable mobile emulation in dev tools)
Another issue is the amount of times the scroll event is triggered. When working, maybe I can set up a debounce to handle how often it's triggered.
If you want to use javascript for this fix, you can calcul the ranges of the X and Y moves.
For that with touch devices, set the start posi X and Y when touchstart and calcul the distances when touchmove
var touchY = touchX = 0;
$(document).delegate('.yourWrap', 'touchstart', function(e){
touchX = e.originalEvent.touches[0].pageX;
touchY = e.originalEvent.touches[0].pageY;
});
$(document).delegate('.yourWrap', 'touchmove', function(e){
if (Math.abs(touchX - e.originalEvent.touches[0].pageX)
> Math.abs(touchY - e.originalEvent.touches[0].pageY)) {
// Block overflow-y
} else {
// Block overflow-x
}
touchX = e.originalEvent.touches[0].pageX;
touchY = e.originalEvent.touches[0].pageY;
});
For wheel devices, compare delta
(e.wheelDeltaY/3 || -e.deltaY)
(e.wheelDeltaX/3 || -e.deltaX)

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.

iPad Web App: Detect Virtual Keyboard Using JavaScript in Safari?

I'm writing a web app for the iPad (not a regular App Store app - it's written using HTML, CSS and JavaScript). Since the keyboard fills up a huge part of the screen, it would make sense to change the app's layout to fit the remaining space when the keyboard is shown. However, I have found no way to detect when or whether the keyboard is shown.
My first idea was to assume that the keyboard is visible when a text field has focus. However, when an external keyboard is attached to an iPad, the virtual keyboard does not show up when a text field receives focus.
In my experiments, the keyboard also did not affect the height or scrollheight of any of the DOM elements, and I have found no proprietary events or properties which indicate whether the keyboard is visible.
I found a solution which works, although it is a bit ugly. It also won't work in every situation, but it works for me. Since I'm adapting the size of the user interface to the iPad's window size, the user is normally unable to scroll. In other words, if I set the window's scrollTop, it will remain at 0.
If, on the other hand, the keyboard is shown, scrolling suddenly works. So I can set scrollTop, immediately test its value, and then reset it. Here's how that might look in code, using jQuery:
$(document).ready(function(){
$('input').bind('focus',function() {
$(window).scrollTop(10);
var keyboard_shown = $(window).scrollTop() > 0;
$(window).scrollTop(0);
$('#test').append(keyboard_shown?'keyboard ':'nokeyboard ');
});
});
Normally, you would expect this to not be visible to the user. Unfortunately, at least when running in the Simulator, the iPad visibly (though quickly) scrolls up and down again. Still, it works, at least in some specific situations.
I've tested this on an iPad, and it seems to work fine.
You can use the focusout event to detect keyboard dismissal. It's like blur, but bubbles. It will fire when the keyboard closes (but also in other cases, of course). In Safari and Chrome the event can only be registered with addEventListener, not with legacy methods. Here is an example I used to restore a Phonegap app after keyboard dismissal.
document.addEventListener('focusout', function(e) {window.scrollTo(0, 0)});
Without this snippet, the app container stayed in the up-scrolled position until page refresh.
If there is an on-screen keyboard, focusing a text field that is near the bottom of the viewport will cause Safari to scroll the text field into view. There might be some way to exploit this phenomenon to detect the presence of the keyboard (having a tiny text field at the bottom of the page which gains focus momentarily, or something like that).
maybe a slightly better solution is to bind (with jQuery in my case) the "blur" event on the various input fields.
This because when the keyboard disappear all form fields are blurred.
So for my situation this snipped solved the problem.
$('input, textarea').bind('blur', function(e) {
// Keyboard disappeared
window.scrollTo(0, 1);
});
hope it helps.
Michele
Edit: Documented by Apple although I couldn't actually get it to work: WKWebView Behavior with Keyboard Displays: "In iOS 10, WKWebView objects match Safari’s native behavior by updating their window.innerHeight property when the keyboard is shown, and do not call resize events" (perhaps can use focus or focus plus delay to detect keyboard instead of using resize).
Edit: code presumes onscreen keyboard, not external keyboard. Leaving it because info may be useful to others that only care about onscreen keyboards. Use http://jsbin.com/AbimiQup/4 to view page params.
We test to see if the document.activeElement is an element which shows the keyboard (input type=text, textarea, etc).
The following code fudges things for our purposes (although not generally correct).
function getViewport() {
if (window.visualViewport && /Android/.test(navigator.userAgent)) {
// https://developers.google.com/web/updates/2017/09/visual-viewport-api Note on desktop Chrome the viewport subtracts scrollbar widths so is not same as window.innerWidth/innerHeight
return {
left: visualViewport.pageLeft,
top: visualViewport.pageTop,
width: visualViewport.width,
height: visualViewport.height
};
}
var viewport = {
left: window.pageXOffset, // http://www.quirksmode.org/mobile/tableViewport.html
top: window.pageYOffset,
width: window.innerWidth || documentElement.clientWidth,
height: window.innerHeight || documentElement.clientHeight
};
if (/iPod|iPhone|iPad/.test(navigator.platform) && isInput(document.activeElement)) { // iOS *lies* about viewport size when keyboard is visible. See http://stackoverflow.com/questions/2593139/ipad-web-app-detect-virtual-keyboard-using-javascript-in-safari Input focus/blur can indicate, also scrollTop:
return {
left: viewport.left,
top: viewport.top,
width: viewport.width,
height: viewport.height * (viewport.height > viewport.width ? 0.66 : 0.45) // Fudge factor to allow for keyboard on iPad
};
}
return viewport;
}
function isInput(el) {
var tagName = el && el.tagName && el.tagName.toLowerCase();
return (tagName == 'input' && el.type != 'button' && el.type != 'radio' && el.type != 'checkbox') || (tagName == 'textarea');
};
The above code is only approximate: It is wrong for split keyboard, undocked keyboard, physical keyboard. As per comment at top, you may be able to do a better job than the given code on Safari (since iOS8?) or WKWebView (since iOS10) using window.innerHeight property.
I have found failures under other circumstances: e.g. give focus to input then go to home screen then come back to page; iPad shouldnt make viewport smaller; old IE browsers won't work, Opera didnt work because Opera kept focus on element after keyboard closed.
However the tagged answer (changing scrolltop to measure height) has nasty UI side effects if viewport zoomable (or force-zoom enabled in preferences). I don't use the other suggested solution (changing scrolltop) because on iOS, when viewport is zoomable and scrolling to focused input, there are buggy interactions between scrolling & zoom & focus (that can leave a just focused input outside of viewport - not visible).
During the focus event you can scroll past the document height and magically the window.innerHeight is reduced by the height of the virtual keyboard. Note that the size of the virtual keyboard is different for landscape vs. portrait orientations so you'll need to redetect it when it changes. I would advise against remembering these values as the user could connect/disconnect a bluetooth keyboard at any time.
var element = document.getElementById("element"); // the input field
var focused = false;
var virtualKeyboardHeight = function () {
var sx = document.body.scrollLeft, sy = document.body.scrollTop;
var naturalHeight = window.innerHeight;
window.scrollTo(sx, document.body.scrollHeight);
var keyboardHeight = naturalHeight - window.innerHeight;
window.scrollTo(sx, sy);
return keyboardHeight;
};
element.onfocus = function () {
focused = true;
setTimeout(function() {
element.value = "keyboardHeight = " + virtualKeyboardHeight()
}, 1); // to allow for orientation scrolling
};
window.onresize = function () {
if (focused) {
element.value = "keyboardHeight = " + virtualKeyboardHeight();
}
};
element.onblur = function () {
focused = false;
};
Note that when the user is using a bluetooth keyboard, the keyboardHeight is 44 which is the height of the [previous][next] toolbar.
There is a tiny bit of flicker when you do this detection, but it doesn't seem possible to avoid it.
The visual viewport API is made for reacting to virtual keyboard changes and viewport visibility.
The Visual Viewport API provides an explicit mechanism for querying and modifying the properties of the window's visual viewport. The visual viewport is the visual portion of a screen excluding on-screen keyboards, areas outside of a pinch-zoom area, or any other on-screen artifact that doesn't scale with the dimensions of a page.
function viewportHandler() {
var viewport = event.target;
console.log('viewport.height', viewport.height)
}
window.visualViewport.addEventListener('scroll', viewportHandler);
window.visualViewport.addEventListener('resize', viewportHandler);
Only tested on Android 4.1.1:
blur event is not a reliable event to test keyboard up and down because the user as the option to explicitly hide the keyboard which does not trigger a blur event on the field that caused the keyboard to show.
resize event however works like a charm if the keyboard comes up or down for any reason.
coffee:
$(window).bind "resize", (event) -> alert "resize"
fires on anytime the keyboard is shown or hidden for any reason.
Note however on in the case of an android browser (rather than app) there is a retractable url bar which does not fire resize when it is retracted yet does change the available window size.
Instead of detecting the keyboard, try to detect the size of the window
If the height of the window was reduced, and the width is still the same, it means that the keyboard is on.
Else the keyboard is off, you can also add to that, test if any input field is on focus or not.
Try this code for example.
var last_h = $(window).height(); // store the intial height.
var last_w = $(window).width(); // store the intial width.
var keyboard_is_on = false;
$(window).resize(function () {
if ($("input").is(":focus")) {
keyboard_is_on =
((last_w == $(window).width()) && (last_h > $(window).height()));
}
});
Try this one:
var lastfoucsin;
$('.txtclassname').click(function(e)
{
lastfoucsin=$(this);
//the virtual keyboard appears automatically
//Do your stuff;
});
//to check ipad virtual keyboard appearance.
//First check last focus class and close the virtual keyboard.In second click it closes the wrapper & lable
$(".wrapperclass").click(function(e)
{
if(lastfoucsin.hasClass('txtclassname'))
{
lastfoucsin=$(this);//to avoid error
return;
}
//Do your stuff
$(this).css('display','none');
});`enter code here`
The idea is to add fixed div to bottom.
When virtual keyboard is shown/hidden scroll event occurs.
Plus, we find out keyboard height
const keyboardAnchor = document.createElement('div')
keyboardAnchor.style.position = 'fixed'
keyboardAnchor.style.bottom = 0
keyboardAnchor.style.height = '1px'
document.body.append(keyboardAnchor)
window.addEventListener('scroll', ev => {
console.log('keyboard height', window.innerHeight - keyboardAnchor.getBoundingClientRect().bottom)
}, true)
This solution remembers the scroll position
var currentscroll = 0;
$('input').bind('focus',function() {
currentscroll = $(window).scrollTop();
});
$('input').bind('blur',function() {
if(currentscroll != $(window).scrollTop()){
$(window).scrollTop(currentscroll);
}
});
The problem is that, even in 2014, devices handle screen resize events, as well as scroll events, inconsistently while the soft keyboard is open.
I've found that, even if you're using a bluetooth keyboard, iOS in particular triggers some strange layout bugs; so instead of detecting a soft keyboard, I've just had to target devices that are very narrow and have touchscreens.
I use media queries (or window.matchMedia) for width detection and Modernizr for touch event detection.
As noted in the previous answers somewhere the window.innerHeight variable gets updated properly now on iOS10 when the keyboard appears and since I don't need the support for earlier versions I came up with the following hack that might be a bit easier then the discussed "solutions".
//keep track of the "expected" height
var windowExpectedSize = window.innerHeight;
//update expected height on orientation change
window.addEventListener('orientationchange', function(){
//in case the virtual keyboard is open we close it first by removing focus from the input elements to get the proper "expected" size
if (window.innerHeight != windowExpectedSize){
$("input").blur();
$("div[contentEditable]").blur(); //you might need to add more editables here or you can focus something else and blur it to be sure
setTimeout(function(){
windowExpectedSize = window.innerHeight;
},100);
}else{
windowExpectedSize = window.innerHeight;
}
});
//and update the "expected" height on screen resize - funny thing is that this is still not triggered on iOS when the keyboard appears
window.addEventListener('resize', function(){
$("input").blur(); //as before you can add more blurs here or focus-blur something
windowExpectedSize = window.innerHeight;
});
then you can use:
if (window.innerHeight != windowExpectedSize){ ... }
to check if the keyboard is visible. I've been using it for a while now in my web app and it works well, but (as all of the other solutions) you might find a situation where it fails because the "expected" size is not updated properly or something.
Perhaps it's easier to have a checkbox in your app's settings where the user can toggle 'external keyboard attached?'.
In small print, explain to the user that external keyboards are currently not detectable in today's browsers.
I did some searching, and I couldn't find anything concrete for a "on keyboard shown" or "on keyboard dismissed". See the official list of supported events. Also see Technical Note TN2262 for iPad. As you probably already know, there is a body event onorientationchange you can wire up to detect landscape/portrait.
Similarly, but a wild guess... have you tried detecting resize? Viewport changes may trigger that event indirectly from the keyboard being shown / hidden.
window.addEventListener('resize', function() { alert(window.innerHeight); });
Which would simply alert the new height on any resize event....
I haven't attempted this myself, so its just an idea... but have you tried using media queries with CSS to see when the height of the window changes and then change the design for that? I would imagine that Safari mobile isn't recognizing the keyboard as part of the window so that would hopefully work.
Example:
#media all and (height: 200px){
#content {height: 100px; overflow: hidden;}
}
Well, you can detect when your input boxes have the focus, and you know the height of the keyboard. There is also CSS available to get the orientation of the screen, so I think you can hack it.
You would want to handle the case of a physical keyboard somehow, though.

Categories

Resources