Off canvas menu appear on scroll - scrolltop

I've tried a lot of different things I've seen online (tutorials and videos) but I can't seem to get this to work.
What I have is a hero banner at the top with 100% width and height. The menu has been hidden up using -webkit-transform: translate(0,-100%); and once the user scrolls down to the bottom of the hero banner, the menu will then appear, sliding down using -webkit-transform: translate(0,0); within an addClass. I have used ease to animate it. However, my javascript isn't working.
Here is a jsfiddle
$(document).ready(function(){
var nav = $(".slide-nav-container");
var banner = $(".hero");
$(window).scroll(function() {
var windowpos = $(window).scrollTop();
if (windowpos >= banner.outerHeight()) {
nav.addClass("slide-menu");
} else {
nav.removeClass("slide-menu");
}
});
});

You are only checking for the height of the banner, you need to add it to the position too.
windowpos >= banner.outerHeight()+banner.offset().top
banner.offset().top will return you the position of the banner on your site, adding its height will be the bottom of the banner.

Related

Scroll To Top button that only appears when you reached the end of the page

I think this is rather easy achieved, but I couldn't find out how to- and couldn't find much documentary about it.
I hate those 'scroll to top' buttons that appear after you already scrolled just 300px. Like I'm that lazy to scroll to top on myself. Therefor I would like to have a scroll to top button that only appears when you reached the bottom of the page (minus 100vh (100% viewport height).
Let's take in account the button is called .scrollTopButton and it's CSS is opacity: 0 and it's position: fixed on default.
How would I make the button appear when you reached the bottom of the page, minus 100vh and scroll along?
I was thinking of comparing the body height minus 100vh with (window).scrollTop().
var vH = $(window).height(),
bodyMinus100vh = ($('body').height() - vH);
if (bodyMinus100VH < $(window).scrollTop) {
$('.scrollTopButton').toggle();
};
Fixed it myself. Quite easy, honestly.
$(window).scroll(function () {
var vH = $(window).height(),
bodyHeight = ($(document).height() - (vH * 2)),
// When you open a page, you already see the website as big
// as your own screen (viewport). Therefor you need to reduce
// the page by two times the viewport
scrolledPX = $(window).scrollTop();
if (scrolledPX > bodyHeight) {
$('.scrollTopButton').css('opacity', '1');
} else {
$('.scrollTopButton').css('opacity', '0')
};
});

Prevent Mobile Safari from presenting toolbar when bottom of viewport is tapped

We have a simple mobile app running in Mobile Safari (MS) on iOS. When the user scrolls down the page n pixels, a "top" button slides up from the bottom. The top button is fixed position. Problem is, when you start scrolling in MS, the navigation and toolbar UI is hidden. When you tap the "top" button, it reveals the bottom toolbar and a second tap is required to tap the "top" button. Is there any way to disable the default "tap on the bottom part of the viewport to reveal the toolbar" behavior so our top button works as expected (i.e. jumps to the top of the page with one click, not two?
No there is not. You can control the content of your webpage but not the behavior of the safari app.
The simple solution here is to add about 50px padding-bottom on your bottom most div. Safari seems to think that you are trying to access the bottom navigation bar, unless you click well above the bottom area. With extra padding at bottom, the user will click much higher on the page (not always, but in general).
Mika and typeoneerror are correct, but there is a workaround.
The best workaround solution I found (that doesn't require minimal-ui) is to force the bottom navigation of iOS Safari to always stay open/visible. That way, clicks to the bottom of the window never open the bottom navigation since it's always open.
To do that, you just need to apply some CSS and browser targeting with JS. Detailed steps on how:
How might one force-show the mobile Safari bottom nav bar to show programmatically?
Buttons aligned to bottom of page conflict with mobile Safari's menu bar
For iOS 7.1, you can set this in your header to minimize the UI:
<meta name="viewport" content="width=device-width, minimal-ui">
It was introduced in iOS 7.1 beta 2. This site was instrumental in helping me understand how minimal-ui works: http://www.mobilexweb.com/blog/ios-7-1-safari-minimal-ui-bugs
Here's how I'm dealing with this. With a position:fixed;bottom:0 toolbar of my own, I'm adding 44px offset to it (with a semi-transparent buffer zone) shortly after the safari toolbar is hidden (as this is the scenario where a tap near the bottom will reveal the toolbar again).
var min_inner_height = false;
var max_inner_height = false;
var passiveIfSupported = false;
try {
window.addEventListener("test", null, Object.defineProperty({}, "passive", {
get: function () {
passiveIfSupported = {
passive: true
};
}
}));
} catch (err) {}
document.addEventListener('scroll', function (e) {
var win_inner_h = window.innerHeight;
if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {
if (min_inner_height === false || win_inner_h < min_inner_height) {
min_inner_height = win_inner_h;
}
if ((max_inner_height === false || win_inner_h > max_inner_height) && win_inner_h > min_inner_height) {
max_inner_height = win_inner_h;
}
if (max_inner_height !== false && max_inner_height == win_inner_h) {
addElementClass(document.body, 'safari-toolbars-hidden');
} else {
removeElementClass(document.body, 'safari-toolbars-hidden');
}
}
}, passiveIfSupported);
This basically adds the .safari-toolbars-hidden class to the <body> sometime around when they disappear due to the user scrolling down the page.
At this point, I move my own toolbar up the page:
.my-bottom-toolbar {
bottom: 0px;
position: fixed;
}
#supports (-webkit-overflow-scrolling: touch) {
/* CSS specific to iOS devices */
.my-bottom-toolbar {
box-shadow: 0 44px 0 rgba(255, 255, 255, 0.8);
transition: bottom 0.15s ease-in-out;
}
.safari-toolbars-hidden .my-bottom-toolbar {
bottom: 44px;
}
}
Hope this helps someone!
Instead of offsetting by a further 44px, you could also add an extra 44px of bottom padding if that works better for your case.
The best solution for me comes from this article.
My solution is with react but simply translated from the articles solution.
import { useWindowHeight } from '#react-hook/window-size/throttled';
//... inside your component
const height = useWindowHeight();
React.useEffect(() => {
const vh = height * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}, [height]);
body {
/* other styles */
height: 100vh;
height: calc(var(--vh, 1vh) * 100);
}
Now when the innerHeight changes the hook is fired and the height variable is adjusted. The window's innerHeight changes when the safari url bar and bottom navigation are hidden so my app fits just right for both situations.

iOS 7 Chrome - Fixed position drawer doesn't stay at bottom when virtual keypad pops up

I have a bottom drawer which is fix positioned at the bottom. When tapped, the drawer will slide up and show more content. On iOS 7, when user taps on input tag or textarea tag, the virtual keyboard pops up. However, the drawer jumps up on the page instead of sticking to the bottom when keypad pops up. Please see the diagram below for illustration.
I firstly encountered the issue also on Safari, but I added the following code to change the fixed position to absolute when keypad is open:
// Apple.Device detects if it is an apple device
if (Modernizr.touch && Apple.Device) {
/* cache dom references */
var $body = jQuery('body');
/* bind events */
$(document)
.on('focus', 'input, textarea', function(e) {
$body.addClass('fixfixed');
})
.on('blur', 'input, textarea', function(e) {
$body.removeClass('fixfixed');
});
}
CSS code:
.fixfixed #drawer {
bottom: 0;
margin-left: auto;
margin-right: auto;
position: absolute;
left: 0;
right: 0;
}
This fix works on Safari on iOS 7 but it doesn't work on Chrome. Also, there is a weired behavior:
If there is an input tag on the page and I tap on it on iPad, then the virtual keyboard opens and the drawer jumps up. If the drawer happens to then cover the I clicked on, the click event actually fires on the drawer. Why is that?
How can I resolve this issue? (I've been searching for a while but how do I debug Chrome on iOS?)
Many thanks for your help!
Update
I've used the following code to detect if it is Chrome on iOS 7, if so, I hide the Drawer when the virtual keyboard is up, and re-display the drawer when virtual keyboard is down.
function iOSversion() {
if (/iP(hone|od|ad)/.test(navigator.platform)) {
// supports iOS 2.0 and later: <http://bit.ly/TJjs1V>
var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
}
}
var iosVersion = iOSversion();
if (navigator.userAgent.match('CriOS') && iosVersion[0] == '7') {
$(document).hammer().on('tap', 'input, textarea', function(e) {
$('body').addClass('chromefixfixed');
})
.on('blur', 'input, textarea', function(){
body.removeClass('chromefixfixed');
})
}
CSS code:
.chromefixfixed #drawer {
display: none;
}
Still the question remains: how do I get Chrome on iOS 7 to work like Chrome on Android (without hiding the drawer when keyboard is up)?
Thanks for the help!
Position fixed bottom and position absolute bottom are an absolute nightmare on iOS and android devices, in my experience it's just not possible to get it to render consistently even across the most modern devices, let alone the older ones that dominate the market currently. So much so, that as a dev I would ask a designer to rethink the layout because of it. I believe it's called "sidestepping the turd".

How to tell SPARK scroll bar in TextArea to recalculate?

I have problem with vertical scroll in SPARK text area. I've added simple code in creation complete to insert 200 lines in text area:
private function creationCompleteHandler(event:FlexEvent):void
{
for (var iind:uint = 1; iind < 200; iind++)
{
testTextArea.text += iind.toString() + "\n";
}
}
And when I start application I noticed that the last 10% (approximately) of scroll bar is "a free walk". When scroll is on 90% page is scrolled all the way down, and when I move thumb of the scroll bar in last 10% of scroll bar nothing moves.
This is all until I change text in text area in browser, then scroll bar acts normal.
Thanks
edit: I tried to dispatch event "change", but it's still not working.
Try below, though it doesn't make much sense, at creationComplete block:
testTextArea.scroller.verticalScrollBar.value = testTextArea.scroller.verticalScrollBar.maximum;
testTextArea.validateNow();
testTextArea.scroller.verticalScrollBar.value = 0;
Then when you listen:
testTextArea.scroller.verticalScrollBar.addEventListener(Event.CHANGE, on Change);
You can do whatever you want with:
if(testTextArea.scroller.verticalScrollBar.value >= (testTextArea.scroller.verticalScrollBar.maximum-5))
This certainly looks hacky!

fixed position div freezes on page (iPad)

I have an asp.net web site I am building to be supported on ipad. When I focus on an input element and the keyboard pops up, the position fixed header div(which normally scrolls along with the page) will pop up the page a distance equivalent to the amount the keyboard takes up and freeze there for the duration of the input process. Once the keyboard is dropped back down, the div snaps back into place and behaves normally again. I am testing on iOS5 so position: fixed should be supported.
Is this a known issue? Has someone come across this and dealt with it before? I can't seem to find anything on this.
Fixed positioning is broken on iOS5/iOS6/iOS7.
Edit 3: See link to a working fix near end of this answer for iOS8.
Position:fixed is broken when either:
a) the page is zoomed
or
b) the keyboard shows on the iPad/iPhone (due to an input getting focus).
You can view the bugs yourself in jsbin.com/icibaz/3 by opening the link and zooming, or giving the input focus. You can edit the edit the html yourself.
Notes about bugs (a) and (b):
A fixed div with top: 0px; left: 0px; will show in the wrong position (above or below the top of the screen) when an input gets focus and the keyboard shows.
The problem seems to have something to do with the auto-centering of the input on the screen (changing window.pageYOffset).
It appears to be a calculation fault, and not a redraw fault: if you force the top: to change (e.g. switching between 0px and 1px) on the onScroll event, you can see the fixed div move by a pixel, but it remains in the wrong place.
One solution I used previously is to hide the fixed div when an input gets focus - see the other Answer I wrote.
The fixed div seems to becomes stuck at the same absolute position on the page it was at at the time when the keyboard opened.
So perhaps change the div to absolute positioning when an input has focus? Edit 3: see comment at bottom using this solution. Or perhaps save the pageXOffset/pageYOffset values before the keyboard is opened, and in an onScroll event calculate the difference between those values and the current pageXOffset/pageYOffset values (current once the keyboard is opened), and offset the fixed div by that difference.
There appears to be a different problem with fixed positioning if the page is zoomed - try it here (Also good information here about Android support for fixed in comments).
Edit 1: To reproduce use jsbin (not jsfiddle) and use the fullscreen view of jsbin (not the edit page). Avoid jsfiddle (and edit view of jsbin) because they put the code inside an iframe which causes interference with fixed positioning and pageYOffset.
Edit 2: iOS 6 and iOS 7 Mobile Safari position:fixed; still has the same issues - presumably they are by design!.
Edit 3: A working solution for (b) is when the input get focus, change the header to absolute positioning and then set the header top on the page scroll event for example. This solution:
Uses fixed positioning when input not focused (using window.onscroll has terrible jitter).
Don't allow pinch-zoom (avoid bug (a) above).
Uses absolute positioning and window.pageYOffset once an input gets focus (so header is correctly positioned).
If scrolled while input has focus, set style.top to equal pageYOffset (header will jitter somewhat due to onscroll event delay even on iOS8).
If using UIWebView within an App on iOS8, or using <=iOS7, if scrolling when input has focus, header will be super jittery because onscroll is not fired till scroll finishes.
Go back to fixed position header once input loses focus (Example uses input.onblur, but probably tider to use
document.body.onfocus).
Beware usability fail that if header too large, the input can be occluded/covered.
I couldn't get to work for a footer due to bugs in iOS page/viewport height when the keyboard is showing.
Edit example using http://jsbin.com/xujofoze/4/edit and view using http://output.jsbin.com/xujofoze/4/quiet
For my needs, I found it easier to use an absolute positioned header, hide it before scroll and show it when finish scroll (I need the same code to support iOS4 and Android).
For my purposes, I hide the header on a touchstart event, and show it again on touchend or scroll event (plus some timers to improve responsiveness/reduce flickering). It flashes, but is the best compromise I could find. One can detect the start of scrolling using the touchmove event (jQuery does this), but I found touchmove didn't work as well for me because:
regularly the iPad fails to do a repaint before scrolling (i.e. the absolute header remains stuck - even though the top was changed before scrolling started).
when an input element gets focus, the iPad auto-centres the element, but the scrollstart event doesn't get fired (because no touchmove if just clicking an input).
Implementing a fixed header on iOS5 could be improved by using a hybrid approach of fixed and absolute positioning:
used fixed positioning for iOS5 until an input gets focus.
when an input gets focus (keyboard showing), change to the iOS4 absolute positioning code.
when the keyboard is closed, change back to fixed positioning.
Code to detect when keyboard is closed (e.g. using keyboard hide key) is to register the DOMFocusOut event on the document element and do something like the following code. The timeout is needed because the DOMFocusOut event can fire between when one element gets the focus and another loses it.
function document_DOMFocusOut() {
clearTimeout(touchBlurTimer);
touchBlurTimer = setTimeout(function() {
if (document.activeElement == document.body) {
handleKeyboardHide();
}
}.bind(this), 400);
}
My fixed header code is something like:
{
setup: function() {
observe(window, 'scroll', this, 'onWinScroll');
observe(document, 'touchstart', this, 'onTouchStart');
observe(document, 'touchend', this, 'onTouchEnd');
if (isMobile) {
observe(document, 'DOMFocusOut', this, 'docBlurTouch');
} else if (isIE) {
// see http://ajaxian.com/archives/fixing-loss-of-focus-on-ie for code to go into this.docBlurIe()
observe(document, 'focusout', this, 'docBlurIe');
} else {
observe(isFirefox ? document : window, 'blur', this, 'docBlur');
}
},
onWinScroll: function() {
clearTimeout(this.scrollTimer);
this.scrolling = false;
this.rehomeAll();
},
rehomeAll: function() {
if ((isIOS5 && this.scrolling) || isIOS4 || isAndroid) {
this.useAbsolutePositioning();
} else {
this.useFixedPositioning();
}
},
// Important side effect that this event registered on document on iOs. Without it event.touches.length is incorrect for any elements in the document using the touchstart event!!!
onTouchStart: function(event) {
clearTimeout(this.scrollTimer);
if (!this.scrolling && event.touches.length == 1) {
this.scrolling = true;
this.touchStartTime = inputOrOtherKeyboardShowingElement(event.target) ? 0 : (new Date).getTime();
// Needs to be in touchStart so happens before iPad automatic scrolling to input, also not reliable using touchMove (although jQuery touch uses touchMove to unreliably detect scrolling).
this.rehomeAll();
}
},
onTouchEnd: function(event) {
clearTimeout(this.scrollTimer);
if (this.scrolling && !event.touches.length) {
var touchedDuration = (new Date).getTime() - this.touchStartTime;
// Need delay so iPad can scroll to the input before we reshow the header.
var showQuick = this.touchStartTime && touchedDuration < 400;
this.scrollTimer = setTimeout(function() {
if (this.scrolling) {
this.scrolling = false;
this.rehomeAll();
}
}.bind(this), showQuick ? 0 : 400);
}
},
// ... more code
}
jQuery mobile supports scrollstart and scrollstop events:
var supportTouch = $.support.touch,
scrollEvent = "touchmove scroll",
touchStartEvent = supportTouch ? "touchstart" : "mousedown",
touchStopEvent = supportTouch ? "touchend" : "mouseup",
touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
function triggerCustomEvent( obj, eventType, event ) {
var originalType = event.type;
event.type = eventType;
$.event.handle.call( obj, event );
event.type = originalType;
}
// also handles scrollstop
$.event.special.scrollstart = {
enabled: true,
setup: function() {
var thisObject = this,
$this = $( thisObject ),
scrolling,
timer;
function trigger( event, state ) {
scrolling = state;
triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
}
// iPhone triggers scroll after a small delay; use touchmove instead
$this.bind( scrollEvent, function( event ) {
if ( !$.event.special.scrollstart.enabled ) {
return;
}
if ( !scrolling ) {
trigger( event, true );
}
clearTimeout( timer );
timer = setTimeout(function() {
trigger( event, false );
}, 50 );
});
}
};
This is somewhat still a problem in iOS13 (when a long text gets deleted in the 'textarea' field, fixed header jumps to the start of that 'textarea' field, obstructing the view), therefore, I thought I share my quick fix:
Since my footer is rather large, I went about without any JS and just adding a greater z-index to the footer than what the fixed header has. Out of sight, out of mind.

Resources