Android Element Selection Issues - javascript

I'm attempting to make my website tablet friendly and I'm facing a strange issue.
I am testing on an Android 4.0 tablet with Chrome 30.
I have a fixed modal popover screen. While this screen is on, we don't want to let the user scroll the background so touch events are prevented. However, in this window we have a scrollable area with overflow:scroll, therefore the touch event is not prevented if the touch start event is detected there. So far so good - Android responsibly scrolls the area as expected.
Problem is, if the user long-presses an element inside the scroll area for about half a second, and only then scrolls - the element where the touch started appears to be selected for a moment. That selection disappears after a bit. But, in case the user scrolls during that bit - the whole page scrolls instead of the scrollable area. It's as if the focus is changed. What's going on?
I tried to set CSS selection rules on the elements inside but it didn't help.
-webkit-touch-callout:none;
-webkit-user-select:none;
-khtml-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
Every other answer suggests to prevent the touch event which I can't because it's meant to be scrolled. Any ideas what's causing this?

OK, I got it.
Add cursor:default!important to the above CSS rules.
I DID come across such a solution but it failed on first try. In my case, the specificity of elements inside the scrollable area was too strong, rendering cursor:default useless and I didn't realize it. I apply this only to mobile devices by detecting the useragent, therefore I can afford dismissing the previous cursor attribute with !important as it won't affect any desktop clients. Sweet!
Still not sure why this worked. If anyone could supply information on how the cursor attribute affects Chrome on Android I will be grateful.

Related

how to launch a webpage on mobile web application without scroll

EDIT FOR CLARIFICATION:
What I want:
A full screen javascript canvas which can handle touch events without those events being further interpreted by the browser, but also reserve the ability to open a new window on user action.
Examples:
I should be able to swipe my finger around without the webpage trying to scroll
I should be able to swipe my finger around without the contents of the webpage being nudged in any way (normally, when one scrolls to the end of a scroll region, the browser allows some additional spring-loaded buffer scrolling to signal to the user that it is the end of the scroll region).
I should be able to pinch and pan without the webpage zooming
etc...
The point:
I need to interpret these events accurately and in realtime MYSELF to respond to these actions WITHIN THE CANVAS. (I am doing realtime drawing via requestAnimationFrame, allowing me to react to user events without using the DOM)
The state of things currently:
This all works perfectly (except for the ability to open a new window) because I position the canvas to be the full size of the viewport (handling any window resize events), and the canvas listens to ontouchstart, ontouchmove, ontouchend, etc... events, calling evt.preventDefault() after I have handled the user input myself. This works to ensure the canvas is ALWAYS full screen, doesn't budge, and user input is accurately given to me to handle in-game.
The Problem:
One bit of user input I need to handle is the launching of a webpage when they click the region of my canvas with a "launch my webpage" button. However, window.open(mywebpage) doesn't work, because mobile safari only allows such an action in the callstack of a click event. Because I rely on ontouchstart to get responsive controls, and evt.preventDefault() in an ontouchstart event CANCELS the click event from happening, I cannot launch the webpage (it gets blocked by the browser).
My attempted solutions, and why they are insufficient:
Just use a click event rather than ontouchstart: this means I can't prevent scrolling/etc... additionally, it is not as responsive, and doesn't allow me to handle touch-and-drag events well.
Overlay a div (or an a) tag atop the canvas over the launch webpage zone, and add a click event to that: if the user clicks-and-drags starting within this tag, then it allows the page to scroll and zoom. Trying to fix this results in the same problem as before.
ORIGINAL POST:
I have a mobile application that is a full-screen canvas, which locks itself positionally (can't scroll or zoom) so that I can correctly interpret user input uninterrupted (swipes, pans, etc...).
It locks itself in by intercepting touchstart events and calling evt.preventDefault (as well as the meta viewport no-zoom stuff which as far as I can tell doesn't actually do anything?).
This works great, and is absolutely necessary to make a game (or game-like application) function.
The problem is that I also have a "go to this webpage" button. I can intercept the touchstart, and use window.open(somewebpage), but mobile popup blockers will block it. The "rules" seem to be "the webpage will be allowed to be opened iff it is done in the call stack of a user interaction, AND that interaction is a 'click' event".
I have the first part down, but if I change the event to a click event, the web page now interprets swipes as scrolls (and pinches as zooms, etc...). If I have both a click and a touchstart event, then calling evt.preventDefault() on the touchstart (which stops the scroll/zoom) also stops the click event.
If I overlay a div atop the click zone of the "launch webpage" button, then the player can scroll/zoom when their input begins in that button, which results in an unpredictable and wonky experience.
How can I launch another webpage without allowing the current webpage to scroll?
Edit: at request, here is a code snippet at least partially illustrating what I'm trying to do https://jsfiddle.net/phildo/0q8e47fk/10/.
Note that in the "real" case, the canvas takes up the full width/height of the screen, and is explicitly set accordingly on screen resize.
Preventing bounces of any kind on mobile web page is a vast problem through out the mobile devices not depending about the manufacturer. I had similar issue on Windows Phone 8 app years ago and there (quite surprisingly) was a solution dedicated to Windows environment which of course cannot applied here.
For iOS you need an iOS solution, right?
The very solution is named iNoBounce. The idea is to add the little js library to your html page, code with some good conventions and the js lib will do the dirty job of preventing the default when necessary.
The trick it actually does is not to prevent just anything, but the ones only, that are "extra" and will cause the bounce events.
With the words of iNoBounce GitHub Readme:
iNoBounce detects if the browser supports -webkit-overflow-scrolling by checking for the property on a fresh CSSStyleDeclaration. If it does, iNoBounce will listen to touchmove and selectively preventDefault() on move events that don't occur on a child of an element with -webkit-overflow-scrolling: touch set. In addition, iNoBounce will preventDefault() when the user is attemping to scroll past the bounds of a scrollable element, preventing rubberbanding on the element itself (an unavoidable caveat).
The example code asks you to use the following parts (there is a separate example code for canvas, this is only the most common solution):
// All you need is an element with `height` or `max-height`, `overflow: auto` and `-webkit-overflow-scrolling: touch`.
<script src="inobounce.js"></script>
<style>
ul {
height: 115px;
border: 1px solid gray;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
</style>
Source:
[1] https://github.com/lazd/iNoBounce
Edit:
I found out you did not limit yourself to iOS. For other browsers, try
[2] https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior
which introduces overscroll-behavior setting, that you can set to none to disable bounces.
It will work only on Android, not ie or iOS.
For mobile Windows Phone I had the solution like this:
div.wp8ScrollFix {
-ms-touch-action: none;
}
which effectively does the same as iNoBounce, now with single CSS line for the div containing the canvas.
Edit2:
For a search of semi universal solution, I could find that
-touch-action: none;
applied to div element that includes the canvas, you can disable default touch events and for the canvas, define your own.
The solution works on any other than Safari browsers. As in [3] there may be some variants like
-ms-touch-action: none;
but I suppose they are now all same without prefixes. The [3] solution is very old and world has changed a lot from those days.
The sad thing is, the browser support is same at least 2019 [4] and maybe now also.
Sources:
[3] jQuery / HTML5 / gwt app for WP8 (Lumia 920) device: vertical css scroll fix
[4] https://css-tricks.com/almanac/properties/t/touch-action/
Problem
Show a div on top of full screen canvas element that intercepts normal click events on element canvas.
Solution
Aside from click events, you need to intrrcept the following touch events:
touchstart
touchend
touchmove
touchcancel
Additional Info
You only preventDefault on the canvas events so you should still be able to create a clickable/touchable element in the canvas that shows a div outside the canvas positioned with a z-index higher than the canvas element by setting on display: block on the div. The div should also have 100vh and 100vw set foe width and height respectively and be position: fixed. The div should also have a button to hide again display: none.
References
https://stackoverflow.com/a/51127296/806876

IOS virtual keyboard obscuring page bottom text box

We have a webpage with fixed header and footer and scrollable content.
It has 20 text boxes. The ones at the bottom e.g. Zip, Telephone are obscured by the iOS virtual keyboard that pops up on text box focus.
If I detect that the user has a device with a virtual keyboard I could add half a screen padding to the bottom text box so that the user can scroll down and the virtual keyboard doesn’t obscure it.
Is this the normal way to handle it and if so is there a JavaScript/css way to detect if the device is going to pop up a virtual keyboard? Or since it’s an aspx page should we try and detect it serverside?
Actually iOS seems now not to have that issue — rather it pushed the bottom up rather than popping over. I’m not sure why it occurred before.
Adding padding would certainly be effective though it may slightly deteriorate the quality of your webpage. However, if it is a recurring issue that needs immediate action, I would suggest padding.
This question on StackOverflow should be very helpful to you:
Move a view up only when the keyboard covers an input field
Ya Usually you should add padding, then it would go up and be more user friendly. I think that's a good idea. I think it's normal.

How to prevent mobile keyboard from covering html input

I have a simple web app with a few text inputs and the inputs toward the bottom of the page get covered up by the iPhone keyboard. This is a terrible user experience making it difficult for the user to see what text they are entering as well as selecting other elements on the page after they are done entering text.
Apple documents the behavior here: https://developer.apple.com/library/archive/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
I've seen several posts about this problem and there are multiple solutions for iOS app development but no real solutions for web apps.
I don't have the resources to test on multiple devices and I don't have an android device so I don't know if this problem even occurs there.
Is there a cross platform solution for detecting when a keyboard is covering the UI, how much of the UI is being covered, and an approach to ensure the input is visible?
This answer is a hack; but it's the best solution I have come up with.
Adding a padding to the bottom of the page that is large enough for the keyboard enables content to be displayed as desired when the keyboard is visible.
Using some javascript listeners and a CSS class, this padding can be added only when the keyboard is displayed.
body.keyboard {
height: calc(100% + 500px); /* add padding for keyboard */
}
The following javascript will add and remove the CSS class when an input has focus, which is the closest I can get to figuring out when the keyboard is displayed.
// focus events don't bubble, must use capture phase
document.body.addEventListener("focus", event => {
const target = event.target;
switch (target.tagName) {
case "INPUT":
case "TEXTAREA":
case "SELECT":
document.body.classList.add("keyboard");
}
}, true);
document.body.addEventListener("blur", () => {
document.body.classList.remove("keyboard");
}, true);
It's also worth noting that iOS performs an annoying zoom if the input font size is less that 16px so this is also important.
input {
font-size: 16px !important;
}
Please let me know if you have a better solution.
What about this: stackoverflow: Scroll textfield up when keyboard popsup?
Even if you're not using jquery you could still bind the focus event and scroll the page using the window.scrollTo(0, document.body.scrollHeight); function and just scroll to the bottom. Because interestingly, the space is created, but not scrolled to.
If you want to go fancy, you can check for the window size in order to determine if and how much you want to scroll.
What you can do is hide the keyboard on any click event. So, figure out when you don't want to display a keyboard and when do you really want to show it.
So once figured, you can hide the keyboard like:
InputMethodManager manager = getSystemService(INPUT_METHOD_SERVICE);
manager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS);
Here's an answer that might help you: Scroll textfield up when keyboard popsup
In summary, you can make use of the visual viewport api and update the next render accordingly.
What I did is to scroll the elements into view when focused by touch.
There is no exact api or anything to know if the keyboard is open at all, or how much the keyboard covers (in chrome actually the keyboard reduces the viewport, not overlays on top), but looking at average mobiles, it covers roughly half of it.
As for the implementation, I use a kind of simple heuristic. You can go all-in if the device is a touch device see mobile device detection section here, but this doesn't necessarily cover laptops with touch screens. (and doing it based on resolution is a bad idea also)
I took this "touch device" approach one step forward, and listen on the touchstart event on form elements and set a variable something like hasTouchKeyboard, and then scroll element to top of page (or at least first half that is likely not covered) when focused if this variable is true.
The idea behind it is that the touchstart usually fires before the focused event, plus the real differentiator is that if the user touched the input field, then it is 100% that it is using a touch device AND actually used touch on it (that will likely trigger the keyboard), and not just focused it with mouse on a hybrid device.
It is definitely not bulletproof, but fairly simple and works well without any user agent magic or other crazy stuff.
For www.lokilist.com I needed a solution that worked without javascript for the text input used to enter a Session ID.
On the server, I used the "isMobileDevice()" PHP function found here:
https://stackoverflow.com/a/23874239/20094562
Then attached a css class with onfocus css only if the requesting browser was "mobile" (that function considers tablets as mobile, which is all the better for this purpose since we actually care about touch screens).
To summarize:
Detect touch screens based on user agent with isMobileDevice()
In your css, include a placeholder class like ".mobileTextInput".
Also include in your css the onfocus property of that class like ".mobileTextInput:focus". This is what will reposition your text input when selected.
If the user agent is a mobile device, add the mobileTextInput class to the text elements you want to move above the virtual keyboard.

android webview image in overflow div not draggable if it has links on it

I have an android app that uses webview to render large images in a div. The div has fixed width height to match the screen leaving some area on top for controls. The image has an image-map on it and areas created with links.
Now the issue is that when I drag the image from non-mapped areas, it works fine, but when I drag from mapped area, it does not work and I think stucks in the touch event (because I also see the orange focus border on the image map area that opens links).
This happens only when the page is run from within app+webview, but if I load the page in browser on same mobile, it works fine.
Also this does not happen in my Android 5.1 Vega IRON, but it happens in older phones like Android 4.4 Droid, and same version Huawei.
Is this some known issue?
This might have something to do with touch events in application, as it works in browser but not in application.
I found out that in some conditions, the image-map does not let the mouse button events go past, therefore it does not perform even the default dragging and click behavior.
I had to remove image-map and build my own absolute positioned and display:block <a> tags for each area, and then it worked.
Hope this helps someone.

Deadzone in iPhone 6 Plus, Safari, iOS8. Bottom area loses interactivity when tabs are visible

I'm making a webapp and want to use all the screen and avoid scrolling if possible. There will be buttons that need to be clicked in many areas of the page.
The area at the bottom of mobile safari is not clickable when the tab bar is open and I rotate to portrait and back to landscape.
I have set the body height to the same as window.innerHeight instead of 100% so that I don't have to scroll to get to buttons at the bottom.
bodyEl.style.height = window.innerHeight + 'px';
It may be the case that this area is not usable for interactive content.
Here is a demo with code:
http://plnkr.co/edit/327sUQ?p=preview
You should be able to open a preview of it on iPhone 6 Plus by clicking on the fullscreen button 'Launch the preview in a separate window'.
Update:
It definitely looks like it was a bug in iOS8, it's working as expected in iOS8.4, 9.0 & 10.2 from my simulator.
I may have found an answer to your issue, but would love to hear if you can confirm. Setting the content of your page to have the following styles:
overflow-y: scroll (allows you to scroll below the viewport, but only if necessary per the length of the content; the default value is visible)
-webkit-overflow-scrolling: touch (to smooth any scroll behavior)
in addition to your height: 100% (which forces the content to fill the viewport)
appears to force the iOS menu (tabs and address bar on top as well as nav bar on the bottom) in Safari to always appear. That way, button clicks to the top and bottom of the page are no longer "dead zones" and will actually work instead of opening up the Safari menu.
I'm searching for a solution for a similar problem - this is not an answer, but an attempt of explanation.
First of all - currently, I cannot confirm this behavior - seems like it's fixed in iOS 8.4 (12H143). Didn't know exaclty in which version I saw it the last time.
Nethertheless, I try to explain what I've found out (until we decided to not bother anymore).
The not clickable area is not always a dead zone. If you scroll up before you click, chances are high you get it working. Thus I think the (empty) standard navigation bar is there (height and behavior of the elements are similar/the same), even if it's empty (all element moved to the address bar) in landscape mode.
Btw.:
There's another (similar) problem with the iPhone 6 plus (not fixed yet).
If you have a position: fixed element on the top of a webpage, in landscape mode and only if there are two or more tabs open (and the tab bar is visible), you can click through this element (even through buttons) - as if the whole thing isn't there.
I know this question is a bit old, but since the issue still exists I thought I should share my experience...
As of now there is NO fix for the issue, but there is a workaround. The solution jennz0r provided may work for some, but I didn't like the idea of the menu bar always showing. I saw a website that had the issue solved... well it seemed to work at least. I didn't find anything in their css or js.
So what was the fix? Since the "dead zone" is 44px in height they simply made their floating bar 88px height :D Users would instinctively tap on the top/center of the button and it will almost always work at first try!
Another workaround would be to simply make the button/bar float 44px from the bottom.
"If it's stupid, but it works... it ain't stupid..." ;)

Categories

Resources