Control scrolling on webkit browsers - html

I have a one page website and I need effect like this website. When user scrolls down, it should scroll right to the next page. You can test that effect by opening the second link in chrome and try scrolling up and down. I know there is nothing much but I have tried researching about everything possible and I just cant figure out a way top even start this functionality. I don't need to be spoon fed so just point me into the right direction by providing me a start and I will try and do the rest. Thanks.

Here's my advice: first of all, you need to understand the core functionality. Try debugging the website and searching for the effect's responsible files.
For your start, you should study about HTML5, CSS3 and Accordions. The HTML5 will help you improve the scrolling functions. CSS3 is resposible for the effect. And "accordion" is the name of the effect that scrolls straight to the page from the link you clicked on the menu.
The problem is that working with HTML5, you will find a few cross-browser issues ahead. An easy fix is to use the html5shive.js, that "forces" IE to understand HTML5 tags.
An alternative for this, is using jQuery, that will give you the same effect without trouble. You should look for "jQuery navigation with smooth scroll".
You can also use only accordion, which is way more easy.
There's a left sidebar that allows you to click on the links and leads you to the page with the scrolling effect. That's an accordion, combined with jQuery. If you don't pretend to include a menu with links, you can only use jQuery for the scroll.
Here's a good start for you: jsfiddle.net/7ZVb7/1383/
:)

This is how it is done:
function scrollingBaby(e) {
var t = Math.floor(e.pageY / $(window).height());
e.wheelDeltaY < 0 ? t++ : e.wheelDeltaY > 0 && t--;
var n = $(".winHeight").size();
n--;
t < 0 ? t = 0 : t > n && (t = n);
//alert(t);
console.log($('.moveThis'+t).offset().top);
$("html,body").animate({
//scrollTop: $(window).height() * t
scrollTop: $('.moveThis'+t).offset().top
}, 400, "easeInOutExpo", function () {
setTimeout(function () {
onAnimation = !1
}, 1200)
})
}
var slideActual = 0,
flag = 0,
reveal = 1,
revealPro = 0,
offset = 0,
heightInfo = 0,
widthInfo = 0,
onAnimation = !1;
(function (e) {
var t = navigator.platform.toUpperCase().indexOf("WIN") !== -1;
window.onmousewheel = document.onmousewheel = function (e) {
e = e || window.event;
e.preventDefault && e.preventDefault();
if (onAnimation == 0) {
onAnimation = !0;
scrollingBaby(e)
}
e.returnValue = !1
};
t && IE8JETEMMERDE();
sliderSize();
setTimeout(function () {
checkImageSize()
}, 800);
countMedia(1)
})(jQuery);
Where in, ".winHeight" is the class that is on all the 'pages' or divs that would need to be animated in this way and '.moveThis0', '.moveThis1' etc are the classes that are applied so that we can gather the offset of divs and direct them to top of the page.

Related

What exactly is "scroll position"? [duplicate]

I'm trying to detect the position of the browser's scrollbar with JavaScript to decide where in the page the current view is.
My guess is that I have to detect where the thumb on the track is, and then the height of the thumb as a percentage of the total height of the track. Am I over-complicating it, or does JavaScript offer an easier solution than that? What would some code look like?
You can use element.scrollTop and element.scrollLeft to get the vertical and horizontal offset, respectively, that has been scrolled. element can be document.body if you care about the whole page. You can compare it to element.offsetHeight and element.offsetWidth (again, element may be the body) if you need percentages.
I did this for a <div> on Chrome.
element.scrollTop - is the pixels hidden in top due to the scroll. With no scroll its value is 0.
element.scrollHeight - is the pixels of the whole div.
element.clientHeight - is the pixels that you see in your browser.
var a = element.scrollTop;
will be the position.
var b = element.scrollHeight - element.clientHeight;
will be the maximum value for scrollTop.
var c = a / b;
will be the percent of scroll [from 0 to 1].
document.getScroll = function() {
if (window.pageYOffset != undefined) {
return [pageXOffset, pageYOffset];
} else {
var sx, sy, d = document,
r = d.documentElement,
b = d.body;
sx = r.scrollLeft || b.scrollLeft || 0;
sy = r.scrollTop || b.scrollTop || 0;
return [sx, sy];
}
}
returns an array with two integers- [scrollLeft, scrollTop]
It's like this :)
window.addEventListener("scroll", (event) => {
let scroll = this.scrollY;
console.log(scroll)
});
Answer for 2018:
The best way to do things like that is to use the Intersection Observer API.
The Intersection Observer API provides a way to asynchronously observe
changes in the intersection of a target element with an ancestor
element or with a top-level document's viewport.
Historically, detecting visibility of an element, or the relative
visibility of two elements in relation to each other, has been a
difficult task for which solutions have been unreliable and prone to
causing the browser and the sites the user is accessing to become
sluggish. Unfortunately, as the web has matured, the need for this
kind of information has grown. Intersection information is needed for
many reasons, such as:
Lazy-loading of images or other content as a page is scrolled.
Implementing "infinite scrolling" web sites, where more and more content is loaded and rendered as you scroll, so that the user doesn't
have to flip through pages.
Reporting of visibility of advertisements in order to calculate ad revenues.
Deciding whether or not to perform tasks or animation processes based on whether or not the user will see the result.
Implementing intersection detection in the past involved event
handlers and loops calling methods like
Element.getBoundingClientRect() to build up the needed information for
every element affected. Since all this code runs on the main thread,
even one of these can cause performance problems. When a site is
loaded with these tests, things can get downright ugly.
See the following code example:
var options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
var observer = new IntersectionObserver(callback, options);
var target = document.querySelector('#listItem');
observer.observe(target);
Most modern browsers support the IntersectionObserver, but you should use the polyfill for backward-compatibility.
If you care for the whole page, you can use this:
document.body.getBoundingClientRect().top
Snippets
The read-only scrollY property of the Window interface returns the
number of pixels that the document is currently scrolled vertically.
window.addEventListener('scroll', function(){console.log(this.scrollY)})
html{height:5000px}
Shorter version using anonymous arrow function (ES6) and avoiding the use of this
window.addEventListener('scroll', () => console.log(scrollY))
html{height:5000px}
Here is the other way to get the scroll position:
const getScrollPosition = (el = window) => ({
x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,
y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop
});
If you are using jQuery there is a perfect function for you: .scrollTop()
doc here -> http://api.jquery.com/scrollTop/
note: you can use this function to retrieve OR set the position.
see also: http://api.jquery.com/?s=scroll
I think the following function can help to have scroll coordinate values:
const getScrollCoordinate = (el = window) => ({
x: el.pageXOffset || el.scrollLeft,
y: el.pageYOffset || el.scrollTop,
});
I got this idea from this answer with a little change.

How to (hack and) maximize Google Doc's Drawing Window to full screen?

Friends of the Internets,
Google Docs's Intert Drawing tool works, except for the fact that it wastes half of all 16:9 screens, since it opens a forced-square window that is UNRESIZABLE, cripling all drawings that are intended for LANDSCAPE and/or PORTRAIT format! Think of all the standard formats like A4, A3, 16:9 monitors.
I've been asking this quetsion to super users, to no avail. NOBODY seems to know the answer! I'm resorting to skilled programmers to hack our way into this and am planning on opening a bounty worth 500 as soon as this this becomes available for this question! This is an essential yet overlooked potential portion of Google Docs that has been overlooked.
Any and all solutions that make this work in Google's own browser Chrome will be:
Awarded 500 bounty points
Accepted as answer
I think the simplest way is make a Chrome Extension Plugin for solve this problem. I made an example of how you should work, of course, a rudimentary but for the purpose is ok. Check it on github (Download the zip, unpack, go to chrome extensions and => "Load unpacked", enjoy :D). For more complex solutions you need to use google document api.
Example of code
document.addEventListener('DOMContentLoaded', function () {
let fullScreenBtn = document.getElementById('fullScreenBtn');
fullScreenBtn.onclick = function (element) {
function modifyDOM() {
function setToFullScreen(iteration, drawer) {
drawer.style.left = '0';
drawer.style.top = '0';
drawer.style.borderRadius = '0';
drawer.style.width = '100%';
drawer.style.height = '100vh';
document.getElementsByClassName('modal-dialog-content')[iteration].style.height = '100vh';
var iframe = drawer.getElementsByTagName("IFRAME")[0]
iframe.width = '100%';
iframe.height = '100%';
var canvas = iframe.contentWindow.document.getElementById('canvas-container');
canvas.style.borderLeft = 'solid 2px red';
canvas.style.borderRight = 'solid 2px red';
}
var drawers = document.getElementsByClassName('modal-dialog');
let drawerCount = drawers.length;
if (drawerCount) {
for (let i = 0; i < drawerCount; i++) {
setToFullScreen(i, drawers[i]);
}
} else {
alert('First off all open the drawer!')
}
return document.body.innerHTML;
}
chrome.tabs.query({ active: true }, function (tabs) {
var tab = tabs[0];
chrome.tabs.executeScript(tab.id, {
code: '(' + modifyDOM + ')();'
}, (results) => {
// console.log(results[0]);
});
});
};
If you want, you can resize the drawing canvas also, but if you do, it make some bugs (like #Anthony Cregan said) ...
You can do with changing the code in this section
var canvas = iframe.contentWindow.document.getElementById('canvas-container');
canvas.style.left = '0';
//canvas.style.position = ''; // works but in resizing elemnts bug
canvas.style.minWidth = '100%';
In action
I acheived this by opening in chrome, pressing F11 (fullscreen), F12 (console). I then navigated the dom in the Elements tab to:
#canvas-container
then set the element styles manually
left: 41px
width: 1787px
EDIT: unfortunately subsequent edits seem to reset the styles you enter manually, there may be a way to enforce these after subsequent draw actions but for now this solution is only good for displaying the end result, not drawing full-screen.
EDIT EDIT: you can enforce these by adding them to the element in the styles sidebar and maintain them with !important but this causes the draw functions to lose their co-ordinates (pen tool draws away from the pointer along the x-axis for instance).
Boom! As you said it's a hack. But this works:
F12->Console->Paste->Enter
let modal = document.getElementsByClassName("sketchy-dialog")[0];
modal.style.width="100%";
modal.style.height="100%";
modal.style.left="0px";
modal.style.top="0px";
let content = document.getElementsByClassName("modal-dialog-content")[0];
content.style.height="100%";
let iframe;
let iframes = document.getElementsByTagName("iframe");
for(let x=0;x<iframes.length;x++)
{
let elem = iframes[x];
if(elem.src.startsWith("https://docs.google.com/drawings"))
{
iframe = elem;
}
}
iframe.style.width="100%";
iframe.style.height="100%";
I use this simple “hack” from console. Just open the drawing modal > press F12 > Click Console > and paste this following js.
modal = document.getElementsByClassName('modal-dialog');
frame = modal[0].getElementsByTagName('iframe');
modal[0].style.width = window.innerWidth+"px";
modal[0].style.height = window.innerHeight+"px";
modal[0].style.top = 0;
modal[0].style.left = 0;
frame[0].width = window.innerWidth;
frame[0].height = window.innerHeight;

VueJS BackToTop Button

So I am experimenting with VueJS but stumbled on something small which I can't seem to find the answer to.
I would like to have a simple BackToTop button and I usually use jQuery to solve this with code like this:
jQuery(document).ready(function($){
//ToTop
var totop = $("#totop");
$(window).scroll(function(){
($(this).scrollTop() > 200) ? totop.fadeIn() : totop.fadeOut();
});
//Smooth Scroll
$(function(){
setTimeout(function(){
if (location.hash){
window.scrollTo(0,450);
target = location.hash.split('#');
smoothScrollTo($('#'+target[1]));
}
},1);
$('a[href*="#"]').not('[href="#"]').not('[href="#0"]').click(function(event){
if(location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname){
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
if(target.length){
event.preventDefault();
$('html, body').animate({scrollTop: target.offset().top},1000,function(){
var $target = $(target);
$target.focus();
if($target.is(":focus")){
return false;
}else{
$target.attr('tabindex','-1');
$target.focus();
};
});
}
}
});
});
$(window).bind("mousewheel",function(){$("html,body").stop(true,false);});
//End
});
But as I want to completely stick true to VueJS this is for me out of the question.
While I already am using the vue-scrollto library, I find it a bit annoying to use a package for something a simple like this: https://cdn.jsdelivr.net/npm/vue-scrollto
My current to top button is coded in html like following:
<button id="totop" v-scroll-to="'body'" v-if="toTopVisible"></button>
Where as you can see am using the vue-scrolto package and an if statement to make it visible or not on scroll. But even this is something I can't turn my head around how I would this work and activate on scroll after let's say 300px.
What I thought would be something easy, turned out quite hard and while I'm still learning I would love to hear some thoughts regarding this.
What would be a good back to top button VueJS code that not only scrolls back up to 0,0 (or a specific id/hash) but also appears only after 300px and disappears if it's at the top of the page (< than 300px scrolled).
I've already went through countless of Google Pages, CodePens and JSFiddles, but a lot of them either use heavy packages or even combined JQuery with VueJS which in my opinion is even stranger.
So if anyone could enlighten me or give me some direction where I could look for a perfect Back To Top Button that has the simple and similar functionality than my jQuery code has, then that would be great!
Thanks in advance for further information.

dragging and dropping onto a jquery slider - posting position to server

I'm trying to implement dragging an item onto a jquery slider. For example, if the item is dropped onto 86% of the slider I would like to POST this position to the server so the item can be place 86% along the result set on the server.
How do you detect dropping onto a jQuery slider and the percentage POSTed to the server?
Since I'm not so good at explaining things, I made a jsFiddle for you. Although this might not be exactly what your looking for, it should be a good starting point!
Here's the code :
$(function () {
//the draggable object
$("#dragobject").draggable();
//Prepare the slider
var range = 100,
sliderDiv = $("#slider");
// Activate the UI slider
sliderDiv.slider({
min: 0,
max: range,
create : function(){
$(this).find(".ui-slider-handle").hide();
}
});
// Number of tick marks on slider
var position = sliderDiv.position(),
sliderWidth = sliderDiv.width(),
minX = position.left,
maxX = minX + sliderWidth,
tickSize = sliderWidth / range;
//Set slider as droppable
sliderDiv.droppable({
//on drop
drop: function (e, ui) {
var finalMidPosition = $(ui.draggable).position().left + Math.round($("#dragobject").width() / 2);
//If within the slider's width, follow it along
if (finalMidPosition >= minX && finalMidPosition <= maxX) {
var val = Math.round((finalMidPosition - minX) / tickSize);
sliderDiv.slider("value", val);
alert(val + "%");
//do ajax update here to set the position
/*$.ajax({
type: "POST",
url: url,
data: val,
success: function () {
//congrats
},
dataType: dataType
});*/
}
}
});
});
And here's the jsFiddle link : jsFiddle example
Hope it helps,
Marc.
SOURCES :
Jquery slider that slides while mouse move,
jQuery UI slider
Since you are using jQuery, lets assume you are using jQuery UI for your drag and drop. First read this: http://api.jqueryui.com/droppable/#event-drop
Then realize that you get the offset position of the dropped element relative to the droppable container as part of the event. This would be where you could compute that into percentage if you needed.
For example, dropped at position left -> 90px of a container that you know to be 100px wide means 90% is your magic number.
Or if you are using native drag and drop, check out this simple edit: http://jsbin.com/ezuke/3283/edit . If you pop a console log on the event in the drop event, you will see that it also exposes the offset of where you dropped it and you could again consume that in your calculation of %.

issue with iOS fixed position css [duplicate]

I have a mobile website which has a div pinned to the bottom of the screen via position:fixed. All works fine in iOS 5 (I'm testing on an iPod Touch) until I'm on a page with a form. When I tap into an input field and the virtual keyboard appears, suddenly the fixed position of my div is lost. The div now scrolls with the page as long as the keyboard is visible. Once I click Done to close the keyboard, the div reverts to its position at the bottom of the screen and obeys the position:fixed rule.
Has anyone else experienced this sort of behavior? Is this expected? Thanks.
I had this problem in my application. Here's how I'm working around it:
input.on('focus', function(){
header.css({position:'absolute'});
});
input.on('blur', function(){
header.css({position:'fixed'});
});
I'm just scrolling to the top and positioning it there, so the iOS user doesn't notice anything odd going on. Wrap this in some user agent detection so other users don't get this behavior.
I had a slightly different ipad issue where the virtual keyboard pushed my viewport up offscreen. Then after the user closed the virtual keyboard my viewport was still offscreen. In my case I did something like the following:
var el = document.getElementById('someInputElement');
function blurInput() {
window.scrollTo(0, 0);
}
el.addEventListener('blur', blurInput, false);
This is the code we use to fix problem with ipad. It basically detect discrepancies between offset and scroll position - which means 'fixed' isn't working correctly.
$(window).bind('scroll', function () {
var $nav = $(".navbar")
var scrollTop = $(window).scrollTop();
var offsetTop = $nav.offset().top;
if (Math.abs(scrollTop - offsetTop) > 1) {
$nav.css('position', 'absolute');
setTimeout(function(){
$nav.css('position', 'fixed');
}, 1);
}
});
The position fixed elements simply don't update their position when the keyboard is up. I found that by tricking Safari into thinking that the page has resized, though, the elements will re-position themselves. It's not perfect, but at least you don't have to worry about switching to 'position: absolute' and tracking changes yourself.
The following code just listens for when the user is likely to be using the keyboard (due to an input being focused), and until it hears a blur it just listens for any scroll events and then does the resize trick. Seems to be working pretty well for me thus far.
var needsScrollUpdate = false;
$(document).scroll(function(){
if(needsScrollUpdate) {
setTimeout(function() {
$("body").css("height", "+=1").css("height", "-=1");
}, 0);
}
});
$("input, textarea").live("focus", function(e) {
needsScrollUpdate = true;
});
$("input, textarea").live("blur", function(e) {
needsScrollUpdate = false;
});
Just in case somebody happens upon this thread as I did while researching this issue. I found this thread helpful in stimulating my thinking on this issue.
This was my solution for this on a recent project. You just need to change the value of "targetElem" to a jQuery selector that represents your header.
if(navigator.userAgent.match(/iPad/i) != null){
var iOSKeyboardFix = {
targetElem: $('#fooSelector'),
init: (function(){
$("input, textarea").on("focus", function() {
iOSKeyboardFix.bind();
});
})(),
bind: function(){
$(document).on('scroll', iOSKeyboardFix.react);
iOSKeyboardFix.react();
},
react: function(){
var offsetX = iOSKeyboardFix.targetElem.offset().top;
var scrollX = $(window).scrollTop();
var changeX = offsetX - scrollX;
iOSKeyboardFix.targetElem.css({'position': 'fixed', 'top' : '-'+changeX+'px'});
$('input, textarea').on('blur', iOSKeyboardFix.undo);
$(document).on('touchstart', iOSKeyboardFix.undo);
},
undo: function(){
iOSKeyboardFix.targetElem.removeAttr('style');
document.activeElement.blur();
$(document).off('scroll',iOSKeyboardFix.react);
$(document).off('touchstart', iOSKeyboardFix.undo);
$('input, textarea').off('blur', iOSKeyboardFix.undo);
}
};
};
There is a little bit of a delay in the fix taking hold because iOS stops DOM manipulation while it is scrolling, but it does the trick...
None of the other answers I've found for this bug have worked for me. I was able to fix it simply by scrolling the page back up by 34px, the amount mobile safari scrolls it down. with jquery:
$('.search-form').on('focusin', function(){
$(window).scrollTop($(window).scrollTop() + 34);
});
This obviously will take effect in all browsers, but it prevents it breaking in iOS.
This issue is really annoying.
I combined some of the above mentioned techniques and came up with this:
$(document).on('focus', 'input, textarea', function() {
$('.YOUR-FIXED-DIV').css('position', 'static');
});
$(document).on('blur', 'input, textarea', function() {
setTimeout(function() {
$('.YOUR-FIXED-DIV').css('position', 'fixed');
$('body').css('height', '+=1').css('height', '-=1');
}, 100);
});
I have two fixed navbars (header and footer, using twitter bootstrap).
Both acted weird when the keyboard is up and weird again after keyboard is down.
With this timed/delayed fix it works. I still find a glitch once in a while, but it seems to be good enough for showing it to the client.
Let me know if this works for you. If not we might can find something else. Thanks.
I was experiencing same issue with iOS7. Bottom fixed elements would mess up my view not focus properly.
All started working when I added this meta tag to my html.
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no,height=device-height" >
The part which made the difference was:
height=device-height
Hope that helps someone.
I've taken Jory Cunningham answer and improved it:
In many cases, it's not just one element who goes crazy, but several fixed positioned elements, so in this case, targetElem should be a jQuery object which has all the fixed elements you wish to "fix". Ho, this seems to make the iOS keyboard go away if you scroll...
Needless to mention you should use this AFTER document DOM ready event or just before the closing </body> tag.
(function(){
var targetElem = $('.fixedElement'), // or more than one
$doc = $(document),
offsetY, scrollY, changeY;
if( !targetElem.length || !navigator.userAgent.match(/iPhone|iPad|iPod/i) )
return;
$doc.on('focus.iOSKeyboardFix', 'input, textarea, [contenteditable]', bind);
function bind(){
$(window).on('scroll.iOSKeyboardFix', react);
react();
}
function react(){
offsetY = targetElem.offset().top;
scrollY = $(window).scrollTop();
changeY = offsetY - scrollY;
targetElem.css({'top':'-'+ changeY +'px'});
// Instead of the above, I personally just do:
// targetElem.css('opacity', 0);
$doc.on('blur.iOSKeyboardFix', 'input, textarea, [contenteditable]', unbind)
.on('touchend.iOSKeyboardFix', unbind);
}
function unbind(){
targetElem.removeAttr('style');
document.activeElement.blur();
$(window).off('scroll.iOSKeyboardFix');
$doc.off('touchend.iOSKeyboardFix blur.iOSKeyboardFix');
}
})();
I have a solution similar to #NealJMD except mine only executes for iOS and correctly determines the scroll offset by measuring the scollTop before and after the native keyboard scrolling as well as using setTimeout to allow the native scrolling to occur:
var $window = $(window);
var initialScroll = $window.scrollTop();
if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
setTimeout(function () {
$window.scrollTop($window.scrollTop() + (initialScroll - $window.scrollTop()));
}, 0);
}
I have fixed my Ipad main layout content fixed position this way:
var mainHeight;
var main = $('.main');
// hack to detects the virtual keyboard close action and fix the layout bug of fixed elements not being re-flowed
function mainHeightChanged() {
$('body').scrollTop(0);
}
window.setInterval(function () {
if (mainHeight !== main.height())mainHeightChanged();
mainHeight = main.height();
}, 100);
I had a similar problem to #ds111 s. My website was pushed up by the keyboard but didn't move down when the keyboard closed.
First I tried #ds111 solution but I had two input fields. Of course, first the keyboard goes away, then the blur happens (or something like that). So the second input was under the keyboard, when the focus switched directly from one input to the other.
Furthermore, the "jump up" wasn't good enough for me as the whole page only has the size of the ipad. So I made the scroll smooth.
Finally, I had to attach the event listener to all inputs, even those, that were currently hidden, hence the live.
All together I can explain the following javascript snippet as:
Attach the following blur event listener to the current and all future input and textarea (=live): Wait a grace period (= window.setTimeout(..., 10)) and smoothly scroll to top (= animate({scrollTop: 0}, ...)) but only if "no keyboard is shown" (= if($('input:focus, textarea:focus').length == 0)).
$('input, textarea').live('blur', function(event) {
window.setTimeout(function() {
if($('input:focus, textarea:focus').length == 0) {
$("html, body").animate({ scrollTop: 0 }, 400);
}
}, 10)
})
Be aware, that the grace period (= 10) may be too short or the keyboard may still be shown although no input or textarea is focused. Of course, if you want the scrolling faster or slower, you may adjust the duration (= 400)
really worked hard to find this workaround, which in short looks for focus and blur events on inputs, and scrolling to selectively change the positioning of the fixed bar when the events happen. This is bulletproof, and covers all cases (navigating with <>, scroll, done button). Note id="nav" is my fixed footer div. You can easily port this to standard js, or jquery. This is dojo for those who use power tools ;-)
define([
"dojo/ready",
"dojo/query",
], function(ready, query){
ready(function(){
/* This addresses the dreaded "fixed footer floating when focusing inputs and keybard is shown" on iphone
*
*/
if(navigator.userAgent.match(/iPhone/i)){
var allInputs = query('input,textarea,select');
var d = document, navEl = "nav";
allInputs.on('focus', function(el){
d.getElementById(navEl).style.position = "static";
});
var fixFooter = function(){
if(d.activeElement.tagName == "BODY"){
d.getElementById(navEl).style.position = "fixed";
}
};
allInputs.on('blur', fixFooter);
var b = d.body;
b.addEventListener("touchend", fixFooter );
}
});
}); //end define
This is a difficult problem to get 'right'. You can try and hide the footer on input element focus, and show on blur, but that isn't always reliable on iOS. Every so often (one time in ten, say, on my iPhone 4S) the focus event seems to fail to fire (or maybe there is a race condition), and the footer does not get hidden.
After much trial and error, I came up with this interesting solution:
<head>
...various JS and CSS imports...
<script type="text/javascript">
document.write( '<style>#footer{visibility:hidden}#media(min-height:' + ($( window ).height() - 10) + 'px){#footer{visibility:visible}}</style>' );
</script>
</head>
Essentially: use JavaScript to determine the window height of the device, then dynamically create a CSS media query to hide the footer when the height of the window shrinks by 10 pixels. Because opening the keyboard resizes the browser display, this never fails on iOS. Because it's using the CSS engine rather than JavaScript, it's much faster and smoother too!
Note: I found using 'visibility:hidden' less glitchy than 'display:none' or 'position:static', but your mileage may vary.
Works for me
if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
$(document).on('focus', 'input, textarea', function() {
$('header').css({'position':'static'});
});
$(document).on('blur', 'input, textarea', function() {
$('header').css({'position':'fixed'});
});
}
In our case this would fix itself as soon as user scrolls. So this is the fix we've been using to simulate a scroll on blur on any input or textarea:
$(document).on('blur', 'input, textarea', function () {
setTimeout(function () {
window.scrollTo(document.body.scrollLeft, document.body.scrollTop);
}, 0);
});
My answer is that it can't be done.
I see 25 answers but none work in my case. That's why Yahoo and other pages hide the fixed header when the keyboard is on. And Bing turns the whole page non-scrollable (overflow-y: hidden).
The cases discussed above are different, some have issues when scrolling, some on focus or blur. Some have fixed footer, or header. I can't test now each combination, but you might end up realizing that it can't be done in your case.
Found this solution on Github.
https://github.com/Simbul/baker/issues/504#issuecomment-12821392
Make sure you have scrollable content.
// put in your .js file
$(window).load(function(){
window.scrollTo(0, 1);
});
// min-height set for scrollable content
<div id="wrap" style="min-height: 480px">
// website goes here
</div>
The address bar folds up as an added bonus.
In case anyone wanted to try this. I got the following working for me on a fixed footer with an inputfield in it.
<script>
$('document').ready(
function() {
if (navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/webOS/i) || navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)
|| navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/BlackBerry/i) || navigator.userAgent.match(/Windows Phone/i)) {
var windowHeight = $(window).height();
var documentHeight = $(document).height();
$('#notes').live('focus', function() {
if (documentHeight > windowHeight) {
$('#controlsContainer').css({
position : 'absolute'
});
$("html, body").animate({
scrollTop : $(document).height()
}, 1);
}
});
$('#notes').live('blur', function() {
$('#controlsContainer').css({
position : 'fixed'
});
$("html, body").animate({
scrollTop : 0
}, 1);
});
}
});
</script>
I have the same issue. But I realized that the fixed position is just delayed and not broken (at least for me). Wait 5-10 seconds and see if the div adjusts back to the bottom of the screen. I believe it's not an error but a delayed response when the keyboard is open.
I tried all the approaches from this thread, but if they didn't help, they did even worse.
In the end, I decided force device to loose focus:
$(<selector to your input field>).focus(function(){
var $this = $(this);
if (<user agent target check>) {
function removeFocus () {
$(<selector to some different interactive element>).focus();
$(window).off('resize', removeFocus);
}
$(window).on('resize', removeFocus);
}
});
and it worked like a charm and fixed my sticky login-form.
Please NOTE:
The JS code above is only to present my idea, to execute this snippet please replace values in angular braces (<>) with appropriate values for your situation.
This code is designed to work with jQuery v1.10.2
This is still a large bug for for any HTML pages with taller Bootstrap Modals in iOS 8.3. None of the proposed solutions above worked and after zooming in on any field below the fold of a tall modal, Mobile Safari and/or WkWebView would move the fixed elements to where the HTML body's scroll was situated, leaving them misaligned with where they actually where laid out.
To workaround the bug, add an event listener to any of your modal inputs like:
$(select.modal).blur(function(){
$('body').scrollTop(0);
});
I'm guessing this works because forcing the HTML body's scroll height re-aligns the actual view with where the iOS 8 WebView expects the fixed modal div's contents to be.
If anybody was looking for a completely different route (like you are not even looking to pin this "footer" div as you scroll but you just want the div to stay at the bottom of the page), you can just set the footer position as relative.
That means that even if the virtual keyboard comes up on your mobile browser, your footer will just stay anchored to the bottom of the page, not trying to react to virtual keyboard show or close.
Obviously it looks better on Safari if position is fixed and the footer follows the page as you scroll up or down but due to this weird bug on Chrome, we ended up switching over to just making the footer relative.
None of the scrolling solutions seemed to work for me. Instead, what worked is to set the position of the body to fixed while the user is editing text and then restore it to static when the user is done. This keeps safari from scrolling your content on you. You can do this either on focus/blur of the element(s) (shown below for a single element but could be for all input, textareas), or if a user is doing something to begin editing like opening a modal, you can do it on that action (e.g. modal open/close).
$("#myInput").on("focus", function () {
$("body").css("position", "fixed");
});
$("#myInput").on("blur", function () {
$("body").css("position", "static");
});
iOS9 - same problem.
TLDR - source of the problem. For solution, scroll to bottom
I had a form in a position:fixed iframe with id='subscribe-popup-frame'
As per the original question, on input focus the iframe would go to the top of the document as opposed to the top of the screen.
The same problem did not occur in safari dev mode with user agent set to an idevice. So it seems the problem is caused by iOS virtual keyboard when it pops up.
I got some visibility into what was happening by console logging the iframe's position (e.g. $('#subscribe-popup-frame', window.parent.document).position() ) and from there I could see iOS seemed to be setting the position of the element to {top: -x, left: 0} when the virtual keyboard popped up (i.e. focussed on the input element).
So my solution was to take that pesky -x, reverse the sign and then use jQuery to add that top position back to the iframe. If there is a better solution I would love to hear it but after trying a dozen different approaches it was the only one that worked for me.
Drawback: I needed to set a timeout of 500ms (maybe less would work but I wanted to be safe) to make sure I captured the final x value after iOS had done its mischief with the position of the element. As a result, the experience is very jerky . . . but at least it works
Solution
var mobileInputReposition = function(){
//if statement is optional, I wanted to restrict this script to mobile devices where the problem arose
if(screen.width < 769){
setTimeout(function(){
var parentFrame = $('#subscribe-popup-frame',window.parent.document);
var parentFramePosFull = parentFrame.position();
var parentFramePosFlip = parentFramePosFull['top'] * -1;
parentFrame.css({'position' : 'fixed', 'top' : parentFramePosFlip + 'px'});
},500);
}
}
Then just call mobileInputReposition in something like $('your-input-field).focus(function(){}) and $('your-input-field).blur(function(){})