Feature detection for position: fixed - javascript

I am trying to find a script that detects if a device places position: fixed elements relative to the ViewPort and not to the entire document.
Currently, standard desktop browsers and Mobile Safari (for iOS 5) do so, whereas Android devices place the fixed elements relative to the entire document.
I have found a couple of tests to detect this, but none of the seem to work:
http://kangax.github.com/cft/ Gives me a false positive when I pass it from an Android device.
https://gist.github.com/1221602 Gives me a false negative when I pass it in an iPhone with iOS 5.
Does anybody know where to find / how to write a test that actually detects that? I don't want to rely on browser sniffing.

According to the contributors at Modernizr, you cannot do this without detecting the browser in use. The contributors are quite well-established in the field.
Testing for position: fixed on iOS and Android devices is listed under the Undetectables wiki page in the Modernizr project.
The MobileHTML5 website lists the support for position:fixed. http://mobilehtml5.org/

Actually, the guys from the Filament Group did a smart thing with their Fixedfixed putting the user agent string of known false positives in their test.
Check it # http://github.com/filamentgroup/fixed-fixed
Someone could complete it with some false negatives too, and make it a modernizr aditional featur test.

I've created another check if position:fixed is really supported in browser. It creates fixed div and try to scroll and check if the position of div changed.
function isPositionFixedSupported(){
var el = jQuery("<div id='fixed_test' style='position:fixed;top:1px;width:1px;height:1px;'></div>");
el.appendTo("body");
var prevScrollTop = jQuery(document).scrollTop();
var expectedResult = 1+prevScrollTop;
var scrollChanged = false;
//simulate scrolling
if (prevScrollTop === 0) {
window.scrollTo(0, 1);
expectedResult = 2;
scrollChanged = true;
}
//check position of div
suppoorted = (el.offset().top === expectedResult);
if (scrollChanged) {
window.scrollTo(0, prevScrollTop);
}
el.remove();
return suppoorted;
}
This function was tested in Firefox 22, Chrome 28, IE 7-10, Android Browser 2.3.

Related

Potential CSS scroll snap bug in Chrome

Please check this CodePen. The problem is described there. There is also a demo. I've tested it on Mac and Windows.
There is the same behavior in Chrome (on Mac and Windows).
Meanwhile, other browsers (even Safari and Opera which are on WebKit too) do not jump to the nearest snap point instantly.
It is a bug?
If so, what is the correct place where I can report it? Or what is the correct place (WebKit related) where I can ask about it?
// Snippet of code required by Stack Overflow to post a question
slider.addEventListener("scroll", _.debounce((e) => {
const currentHeight = parseFloat(e.target.style["height"]);
e.target.style["height"] = currentHeight + (flag ? 5 : -5) + "px";
flag = !flag;
}, 50));
"Chrome introduced auto-snap after layout changes in M81." (from Scroll snapping after layout changes)
Issue 1181843: Scroll-snap jumps to the nearest snap position on repaint in Chrome
This behavior is intentional in Chrome, unfortunately.

Detecting opera mini to "hack" position: fixed

I am trying to detect opera mini so that I can put a workaround for my position:fixed drawer navigation. I'm using the following bit of code for the browser detection (taken from dev.Opera) :
var isOperaMini = Object.prototype.toString.call(window.operamini) === '[object OperaMini]';
(function() {
if(navigator.userAgent.indexOf('Opera Mini') > -1){
$('HTML').addClass('opera');
console.log('opera')
}else{
document.getElementsByTagName('body').className+=' no-opera';
console.log('nuh-uh')
}
})();
but it is not working. the user agent become true for mozilla also. How can I detect the browser so that I can put in the hack?
** The hack is just to use position:absolute, I'll lose the stickiness, but without the hack my site is unusable.
*** I know why mozilla is true - according to this article, user agent is a mess!

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.

How do I keep a bar on the bottom of a page in IE 6,7,8 or how do I force IE to redraw the interface?

I need to glue a bar to the bottom of the client view in the web browser. Traditionally I would use position:fixed; except that I need to support my IE 6 clients. I've got a very extensive hack to glue the bar to the bottom of the page and over the content, however when the user scrolls down or right, the bar stays fixed on the page.
To correct this issue I use a javascript event that gets fired using setInterval and when running the function in IE (8)'s debug tools the event fires and changes the position top and position left attributes but the page doesn't redraw the element. The code works but the element is not moving, see below.
Just so you know, the fix has to work in IE quirks mode... it can't work if the other IE versions are trying to use a standard. Believe me, I've tried.
P.S. This is really aggravating because I'm double checking IE9 support as well... get this the element does not move with the scroll bars in IE 6, 7, and 8 but moves in IE 9 and it still displays "IE Quirks Mode." And Microsoft said that this release wouldn't effect anything,...
HTML Structure
<body>
<div id="j_zoom_area" style="zoom:100%;">
The Application area the the zoom is changed (by the bar) for accessibility...
</div>
<div id="j_protectorite">
<div class="j_bar">
<div class="j_plate">Zoom Controls, Help, Search, other misc controls</div>
<div class="j_plate">Copyright info, privacy policy, etc...</div>
</div>
</div>
<script type="text/javascript" language="javascript">
j_doBar();
</script>
</body>
The CSS for the bar is https://kscserver.com/ERP-API/Style/includes.css.
The particular javascript for the bar correction.
//This controls the scrolling of the bar
function j_FixBarSlowly(){
var nTop = 0;
var nLeft = 0;
nTop = (document.body.scrollTop + document.body.clientHeight) - 67;
nLeft = document.body.scrollLeft;
//document.title = document.body.scrollTop + '+' + document.body.clientHeight + '-67' + '=' + nTop + 'px';
document.getElementById("j_protectorite").style.Top = nTop + 'px';
document.getElementById("j_protectorite").style.Left = nLeft + 'px';
document.getElementById("j_protectorite").style.Bottom = '';
document.getElementById("j_protectorite").style.Position = 'absolute';
//Ie6,7,8 hack to force redraw
}
function j_doBar() {
//j_FixBarSlowly();
//if (setInterval != undefined) {
// setTimeout("j_doBar();",5);
//} else {
setInterval("j_FixBarSlowly();",5);
//}
}
I'd suggest using an IE fix hack, such as Dean Edwards' IE7.js.
This script runs when your page loads in IE and fixes some common problems in older versions of IE. The documentation lists the things it deals with, and includes position:fixed;.
Hope that helps.
(Of course, the best solution - for your sanity - is just to give up trying to make IE6 look identical to newer browsers, and just live with a non-sticky footer in IE6. As long as it doesn't affect usability, I don't see a problem with IE6 users having a slightly less perfect page layout. But I know some people don't have the luxury of doing that; if your users are demanding it, they're the ones you have to listen to, not me!)
After further testing the best solution was to use an I Frame for the page content and have a div at the bottom of the page content. Of course a few javascript tweaks for proper sizing and you have a perfect solution.
Just for a visual reference.

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