Sticky Side Bar on touch devices - javascript

I have a "sticky" side bar using this plugin: https://github.com/AndrewHenderson/jSticky. Basically, it works well on desktop but on touch devices it delays a second or two until the page is fully scrolled to re-appear in its proper place. Is there any way to make it scroll WHILE the page is scrolling as on desktop? I have seen sticky elements on other sites work like this, so there should be a way check and implement the position smoother. The code is below. All you do is assign a .sticky class to an item. For a demo, you can see this page I'm working on: http://www.mjmlawoffice.com/responsive/index.php/fees
<script>
jQuery(function(){
jQuery(".sticky").sticky({
topSpacing: 0,
zIndex:2,
stopper: "#footer"
});
});
</script>
;(function($) {
$.fn.sticky = function(options) {
var defaults = {
topSpacing: 0, // No spacing by default
zIndex: '', // No default z-index
stopper: '.sticky-stopper' // Default stopper class, also accepts number value
},
settings = $.extend({}, defaults, options); // Accepts custom stopper id or class
// Checks if custom z-index was defined
function checkIndex() {
if (typeof settings.zIndex == 'number') {
return true;
} else {
return false;
}
}
var hasIndex = checkIndex(); // True or false
// Checks if a stopper exists in the DOM or number defined
function checkStopper() {
if ( 0 < $(settings.stopper).length || typeof settings.stopper === 'number' ) {
return true;
} else {
return false;
}
}
var hasStopper = checkStopper(); // True or false
return this.each(function() {
var $this = $(this),
thisHeight = $this.outerHeight(),
thisWidth = $this.outerWidth(),
topSpacing = settings.topSpacing,
zIndex = settings.zIndex,
pushPoint = $this.offset().top - topSpacing, // Point at which the sticky element starts pushing
placeholder = $('<div></div>').width(thisWidth).height(thisHeight).addClass('sticky-placeholder'), // Cache a clone sticky element
stopper = settings.stopper,
$window = $(window);
function stickyScroll() {
var windowTop = $window.scrollTop(); // Check window's scroll position
if ( hasStopper && typeof stopper === 'string' ) {
var stopperTop = $(stopper).offset().top,
stopPoint = (stopperTop - thisHeight) - topSpacing;
} else if (hasStopper && typeof stopper === 'number') {
var stopPoint = stopper;
}
if (pushPoint < windowTop) {
// Create a placeholder for sticky element to occupy vertical real estate
$this.after(placeholder).css({
position: 'fixed',
top: topSpacing
});
if (hasIndex) {
$this.css({ zIndex: zIndex });
}
if (hasStopper) {
if (stopPoint < windowTop) {
var diff = (stopPoint - windowTop) + topSpacing;
$this.css({ top: diff });
}
}
} else {
$this.css({
position: 'static',
top: null,
left: null
});
placeholder.remove();
}
};
$window.bind("scroll", stickyScroll);
});
};
})(jQuery);

this is because your css selector for #media screen and (min-width: 768px)
*js-mainnav.megamenu-sticky is only displayed on a desktop with min-width 768px
the attribute that it needs are not on smaller browser are not included.
try adding
#js-mainnav.megamenu-sticky {
z-index: 1000;
position: fixed !important;
}
outside the mediaquery

Related

Edge Swipe Navigation

Can you recommend a JS library that actually provides edge swipe functionality when working with bare-bones HTML & CSS?
I've searched all over and haven't found a source of truth for that problem.
I've seen lots and lots of libraries enabling swipe gestures but not edge swipe.
My last attempt was using Hammer.js which I've tried implementing as:
var swipe = new Hammer(document);
// detect swipe and call to a function
swipe.on('swiperight swipeleft', function (e) {
e.preventDefault();
var endPoint = e.pointers[0].pageX;
var distance = e.distance;
var origin = endPoint - distance;
//swipe right to open nav
if (origin <= 15 && e.type == 'swiperight') {
// open main menu
$('#navigation-menu').animate({
left: '0'
});
} else {
// close/hide menu(s)
$('#navigation-menu').animate({
left: '-100%'
});
}
});
Further, if not using any library, how can I implement a mobile edge swipe to show and hide content, (in my case it'd be a navigation menu) with vanilla JS?
At this point I'm open to either solution/direction.
Here is a solution, you can set thresholdStart, End, Milliseconds. You may want to tidy up the code, and port it for touch events (I used mouse events for testing in my browser more easily).
Use:
swipeEdgeFromLeft function and swipeEdgeFromRight function.
var div = document.body;
var mouse = {
isDown: false,
inLeft: false,
inRight: false,
downTimestamp: null
};
var width, thresholdStart, thresholdEnd, thresholdMilliseconds;
function resize(){
width = window.innerWidth;
thresholdStart = 0.1*width;//within 10% of screen width
thresholdEnd = 0.13*width;//beyond 13% of screen width
thresholdMilliseconds = 500;//must be done in 500 milliseconds
}
document.addEventListener("resize", resize, false);
resize();//initialize
div.addEventListener('mousedown'/*'touchstart'*/, function(e){
var x = e./*touches[0].*/pageX;
mouse.isDown = true;
mouse.downTimestamp = performance.now();
if(x < thresholdStart){
mouse.inLeft = true;
} else if(x > width-thresholdStart){
mouse.inRight = true;
}
});
div.addEventListener('mousemove'/*'touchmove'*/, function(e){
var x = e./*touches[0].*/pageX;
if(mouse.inLeft && x > thresholdEnd){
mouse.inLeft = false;
if(performance.now() - mouse.downTimestamp < thresholdMilliseconds){
swipeEdgeFromLeft();
}
} else if(mouse.inRight && x < width-thresholdEnd){
mouse.inRight = false;
if(performance.now() - mouse.downTimestamp < thresholdMilliseconds){
swipeEdgeFromRight();
}
}
});
div.addEventListener('mouseup'/*'touchend'*/, function(e){
//var x = e./*changedTouches[0].*/pageX;
mouse.isDown = false;
mouse.inLeft = false;
mouse.inRight = false;
mouse.downTimestamp = null;
});
function swipeEdgeFromLeft(){
console.log("edge swipe from left");
}
function swipeEdgeFromRight(){
console.log("edge swipe from right");
}
body {
max-width: 100vw;
height: 100vh;
}
.bar {
height: 100vh;
background-color: rgba(0,0,0,0.4);
position: fixed;
pointer-events: none;
}
#left-inner-threshold {
width: calc(0.1 * 100vw);
left: 0;
}
#right-inner-threshold {
width: calc(0.1 * 100vw);
right: 0;
}
#left-outer-threshold {
width: calc(0.13 * 100vw);
left: 0;
}
#right-outer-threshold {
width: calc(0.13 * 100vw);
right: 0;
}
<div id="left-inner-threshold" class="bar"></div>
<div id="left-outer-threshold" class="bar"></div>
<div id="right-inner-threshold" class="bar"></div>
<div id="right-outer-threshold" class="bar"></div>
Here's a solution to your existing code using Hammer.js v2.0.8
The explanation for how to achieve the edge swipe can be found here answered by #jovinbm.
$(document).ready(function () {
const swipe = new Hammer(document);
function getStartPosition(e) {
const delta_x = e.deltaX;
const delta_y = e.deltaY;
const final_x = e.srcEvent.pageX || e.srcEvent.screenX || 0;
const final_y = e.srcEvent.pageY || e.srcEvent.screenY || 0;
return {
x: final_x - delta_x,
y: final_y - delta_y
}
};
swipe.on('swiperight swipeleft', function (e) {
e.preventDefault();
const { x } = getStartPosition(e);
console.log(x);
//swipe right to open nav /* note the condition here */
if (e.type == 'swiperight' && x >= 0 && x <= 50) {
// open menu
$('#navigation').animate({
left: '0'
});
//swiping left should slide out nav and/or sub-nav
} else {
// close/hide menu
$('#navigation, #task-menu').animate({
left: '-100%'
});
}
});
});
Here's a pen showing it in action:
For swipes, only the final pointerup event is included as the srcEvent in the event object passed to your handler (see http://hammerjs.github.io/api/). The initial pointerdown event that carries the details of the initial position of where the swipe event started is not provided in the hammer event object. Fortunately, you can use the srcEvent in the event object to get the starting position of the event initial pointerdown event.
const getStartPosition = (e) => {
const delta_x = e.deltaX;
const delta_y = e.deltaY;
const final_x = e.srcEvent.pageX || e.srcEvent.screenX || 0;
const final_y = e.srcEvent.pageY || e.srcEvent.screenY || 0;
return {
x: final_x - delta_x,
y: final_y - delta_y
};
};
const handleSwipe = (e) => {
const {x} = getStartPosition(e);
if (x >= 0 && x <= 50) {
// handle swipe from left edge e.t.c
}
else {
// handle other case
}
};
The srcEvent is just a normal javascript event that inherits properties from UIEvent hence the pageX/pageY api above. This will probably not work in other browsers since some of them are not standardized

Scrolling sidebar inside a div with scrollbar - $(window).on('scroll', function()?

I want my social sidebar make scroll only within the gray div. I have already put the sidebar within the gray div does not exceed the footer or the content above. My difficulty is to sidebar scroll accompanying the scroll without going gray div.
http://test.eae.pt/beautyacademy/angebot/
JS:
beautyAcademy.sharer = {
element: void 0,
elementToScroll: void 0,
init:function() {
this.element = $('.js-sharer-ref');
console.log(this.element.length);
if(this.element.length != 1) {
return;
}
this.build();
},
build: function() {
this.binds();
},
binds: function() {
var _this = this;
// Element that's gonna scroll
this.$elementToScroll = $('.fixed-social');
// Element that's gonna scroll height
this.elementToScrollHeight = this.$elementToScroll.outerHeight();
// Element where scroll is gonna happen Height
this.elementHeight = this.element.outerHeight();
// Element where scroll is gonna happen distance to top
this.elementOffsetTop = this.element.offset().top;
// Scroll that was done on the page
this.windowScrollTop = $(window).scrollTop();
this.elementOffsetBottom = this.elementOffsetTop + this.elementHeight - this.elementToScrollHeight;
this.$elementToScroll.css('top', (_this.elementOffsetTop+80) + "px");
$(window).on('scroll', function() {
if(this.windowScrollTop + this.elementToScrollHeight < this.elementHeight )
this.$elementToScroll.css('margin-top', this.windowScrollTop );
});
}
};
You need to try like below :
$(function(){
if ($('#container').length) {
var el = $('#container');
var stickyTop = $('#container').offset().top; // returns number
var stickyHeight = $('#container').height();
$(window).scroll(function(){ // scroll event
var limit = $('#footer').offset().top - stickyHeight - 20;
var windowTop = $(window).scrollTop(); // returns number
if (stickyTop < windowTop){
el.css({ position: 'fixed', top: 0 });
}
else {
el.css('position','static');
}
if (limit < windowTop) {
var diff = limit - windowTop;
el.css({top: diff});
}
});
}
});
DEMO

Add class to element if at top of page and user scrolls up

I'm trying to create a splash page similar to http://kinkinurbanthai.com/ but I need to re-activate the splash if the user is at the top of the page, and does an additional scroll up to 'request' or activate the splash again.
I have the code working up to the point of reactivating the splash element; but I can't get it to wait for the additional 'scroll up' request.
Here is the code so far that works, minus the locking b/c that isn't doing what I need it to atm. Code: http://jsfiddle.net/teejudp3/2/
// Does stuff on load and scrolls
//-------------------------------//
$(window).on("load scroll",function(e){
var $window = $(window);
if ( $window.scrollTop() <= 0 ) {
$('.splashwrapper').removeClass('remove');
}
// hide/show splash screen
//=========================//
// chrome/FF
$('.splashwrapper').bind('DOMMouseScroll', function(e){
// get scroll direction
var direction = (function () {
var delta = (e.type === 'DOMMouseScroll' ?
e.originalEvent.detail * -40 :
e.originalEvent.wheelDelta);
return delta > 0 ? 0 : 1;
}());
if( direction === 0 ) {
// scroll up
} else {
// scroll down
$('.splashwrapper').addClass('remove');
}
//prevent page fom scrolling
return false;
});
//IE, Opera, Safari
$('.splashwrapper').bind('mousewheel', function(e){
// get scroll direction
var direction = (function () {
var delta = (e.type === 'DOMMouseScroll' ?
e.originalEvent.detail * -40 :
e.originalEvent.wheelDelta);
return delta > 0 ? 0 : 1;
}());
if( direction === 0 ) {
// scroll up
} else {
// scroll down
$('.splashwrapper').addClass('remove');
}
//prevent page fom scrolling
return false;
});
// touch device - uses touchSwipe.js
$(".splashwrapper").swipe({
swipeUp:function() {
$('.splashwrapper').addClass('remove');
}
});
});
I've tried to stop the users scroll if they go to <= 1 scroll top position but this doesn't seem to give the wanted affect.
// temporarily lock users scroll position
var $window = $(window), previousScrollTop = 1, scrollLock = false;
$window.scroll(function(event) {
if(scrollLock) {
$window.scrollTop(previousScrollTop);
}
previousScrollTop = $window.scrollTop();
});
// if at 1px from top, stop scroll from going up one time
if ( $window.scrollTop() <= 1 ) {
scrollLock = true;
$window.scrollTop( 1, 0);
setTimeout(function() {
scrollLock = false;
}, 1000);
}
Got this working by re-working the stop-point section - aka I was overthinking it.
Updated fiddle: http://jsfiddle.net/teejudp3/6/
var $window = $(window);
if ( $window.scrollTop() === 0 && $('.splashwrapper').hasClass('ready') ) {
$window.scrollTop(1);
$('.splashwrapper').removeClass('ready').removeClass('remove');
}
if ( $window.scrollTop() === 0 && !$('.splashwrapper').hasClass('ready') ) {
$window.scrollTop(1);
setTimeout(function() {
$('.splashwrapper').addClass('ready');
}, 500);
}

IE9 Defaulting to IE7, javascript bug

I have this javascript that seems to be forcing IE9 into computability mode and forcing it to IE7. There are other areas of the website working properly that do not have this javascript code working on them, which leads me to believe something in this script is not compatible with IE9/IE7.
Basically, the code creates a pop up box when your mouse hovers over it. However if the pop up box displays over an image, the image shows through the pop up box as if it has precedence. I have tried changing the z-index on that div but no luck.
Any suggestions?
jQuery('.bubbleInfo').each(function () {
if(jQuery.trim(jQuery(this).find('#dpop').html()) != ''){ // start
var totalHeight = jQuery(this).height();
var distance = 15;
var time = 250;
var hideDelay = 150;
var hideDelayTimer = null;
var beingShown = false;
var shown = false;
var trigger = jQuery('.trigger', this);
var info = jQuery('.popup', this).css('opacity', 0);
jQuery([trigger.get(0), info.get(0)]).mouseover(function () {
if (hideDelayTimer) clearTimeout(hideDelayTimer);
if (beingShown || shown) {
// don't trigger the animation again
return;
} else {
// reset position of info box
beingShown = true;
info.css({
top: (totalHeight+38),
left: -77,
display: 'block'd
}).animate({
top: '-=' + distance + 'px',
opacity: 1
}, time, 'swing', function() {
beingShown = false;
shown = true;
});
}
return false;
}).mouseout(function () {
if (hideDelayTimer) clearTimeout(hideDelayTimer);
hideDelayTimer = setTimeout(function () {
hideDelayTimer = null;
info.animate({
top: '-=' + distance + 'px',
opacity: 0
}, time, 'swing', function () {
shown = false;
info.css('display', 'none');
});
}, hideDelay);
return false;
});
} // end
IE < 10 have issue in the jquery library version although IE10 supports all the version's of jquery.

JavaScript is being triggered before its time, *only on Chrome & IE

I have a gallery of three Grids with images. The grid sizes changes depending on the screen size, and I have achieved that using Media-Query - ie, on desktop the grid's width will be 33% to make three columns view next to each other, and on tablet it will be 50% to make two columns view, and on phone it will be a 100% for each grid making one column view.
The reason I did this is to create a tiled gallery with images of different heights - and if I did it the normal way it will generate White-empty-spaces when floating.
So to fix this problem, and with the help of few members on this website, we have created a JavaScrip function that will MOVE all of the images that are inside Grid3 equally to Grid1 & Grid2 when screen size is tablet, so we get rid of the third grid making a view of fine two columns. Everything is working great!
Now, the problem is - on Chrome & IE - The function is being fired before its time for some reason that I need your help to help me find it! Please try it your self here: [http://90.195.175.51:93/portfolio.html][2]
Slowly on Chrome or IE - (try it on Firefox as well) - try to re-size the window from large to small, you will notice that BEFORE the top header changes to be a responsive Header (which indicate that you are on a small screen) the images have been sent to Grid1 and Grid 2! but a few px before the time. As on the function it says to fire it on <770.
Hope my question is clear enough for you to help me solve this issue which is stopping me from launching my website. Thanks.
Here is the JavaScrip:
//Gallery Grid System//
var testimonial = $(".testimonial, .galleryItem", "#grid3");
(function () {
$(document).ready(GalleryGrid);
$(window).resize(GalleryGrid);
})(jQuery);
function GalleryGrid() {
var grid3 = $('#grid3');
var width = $(window).width();
if (width < 1030 && width > 770) {
var grid1 = $('#grid1');
var grid2 = $('#grid2');
for (var i = 0; i < testimonial.length; i++) {
if (i < testimonial.length / 2) {
grid1.append(testimonial[i]);
} else {
grid2.append(testimonial[i]);
}
}
} else {
grid3.append(testimonial);
}
}
Note: The following is the whole page with all the functions:
$(document).ready(function () {
//Prevent clicking on .active links
$('.active').click(function (a) {
a.preventDefault();
});
//Allow :active on touch screens
document.addEventListener("touchstart", function () {}, true);
//Hide toolbar by default
window.addEventListener('load', function () {
setTimeout(scrollTo, 0, 0, 0);
}, false);
//Scroll-up button
$(window).scroll(function () {
if ($(this).scrollTop() > 100) {
$('.scrollup').fadeIn();
} else {
$('.scrollup').fadeOut();
}
});
$('.scrollup').click(function () {
$("html, body").animate({
scrollTop: 0
}, 600);
return false;
});
//StickyBox
$(function () {
$.fn.scrollBottom = function () {
return $(document).height() - this.scrollTop() - this.height();
};
var $StickyBox = $('.detailsBox');
var $window = $(window);
$window.bind("scroll resize", function () {
var gap = $window.height() - $StickyBox.height() - 10;
var footer = 288 - $window.scrollBottom();
var scrollTop = $window.scrollTop();
$StickyBox.css({
top: 'auto',
bottom: 'auto'
});
if ($window.width() <= 770) {
return;
$StickyBox.css({
top: '0',
bottom: 'auto'
});
}
if (scrollTop < 50) {
$StickyBox.css({
bottom: "auto"
});
} else if (footer > gap - 100) {
$StickyBox.css({
top: "auto",
bottom: footer + "px"
});
} else {
$StickyBox.css({
top: 80,
bottom: "auto"
});
}
});
});
//Change items location depending on the width of the screen//
$(function () { //Load Ready
function myFunction() {
var insert = $(window).width() <= 770 ? 'insertBefore' : 'insertAfter';
$('#home-sectionB img')[insert]($('#home-sectionB div'));
$('#home-sectionD img')[insert]($('#home-sectionD div'));
}
myFunction(); //For When Load
$(window).resize(myFunction); //For When Resize
});
//Contact Form//
$(".input").addClass('notSelected');
$(".input").focus(function () {
$(this).addClass('selected');
});
$(".input").focusout(function () {
$(this).removeClass('selected');
});
$(document).ready(function () {
GalleryGrid();
$(window).resize(GalleryGrid);
});
//Gallery Grid System//
var testimonial = $(".testimonial, .galleryItem", "#grid3");
(function () {
$(document).ready(GalleryGrid);
$(window).resize(GalleryGrid);
})(jQuery);
function GalleryGrid() {
var grid3 = $('#grid3');
var width = $(window).width();
if (width < 1030 && width > 770) {
var grid1 = $('#grid1');
var grid2 = $('#grid2');
for (var i = 0; i < testimonial.length; i++) {
if (i < testimonial.length / 2) {
grid1.append(testimonial[i]);
} else {
grid2.append(testimonial[i]);
}
}
} else {
grid3.append(testimonial);
}
}
//Testimonials Animation//
$(".testimonial").hover(function () {
$(".testimonial").addClass('testimonialNotActive');
$(this).removeClass('testimonialNotActive').addClass('testimonialActive');
},
function () {
$(".testimonial").removeClass('testimonialNotActive');
$(this).removeClass('testimonialActive');
});
//Portfolio Gallery Filter//
(function () {
var $portfolioGallerySection = $('#portfolio-sectionB'),
$filterbuttons = $('#portfolio-sectionA a');
$filterbuttons.on('click', function () {
var filter = $(this).data('filter');
$filterbuttons.removeClass('portfolio-sectionAClicked');
$(this).addClass('portfolio-sectionAClicked');
$portfolioGallerySection.attr('class', filter);
$('.galleryItem').removeClass('selectedFilter');
$('.galleryItem.' + filter).addClass('selectedFilter');
});
}());
});
Your problem is that CSS media queries and jQuery's $(window).width() do not always align.
function getCSSWidth() {
var e = window, a = 'inner';
if (!('innerWidth' in window )) {
a = 'client';
e = document.documentElement || document.body;
}
return e[ a+'Width' ];
}
Use this instead of $(window).width()
modified from http://andylangton.co.uk/articles/javascript/get-viewport-size-javascript/
I think this could solve your problem (but I'm not quite sure)
//Put that before the document ready event
(function($,sr){
// debouncing function from John Hann
// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
var debounce = function (func, threshold, execAsap) {
var timeout;
return function debounced () {
var obj = this, args = arguments;
function delayed () {
if (!execAsap)
func.apply(obj, args);
timeout = null;
};
if (timeout)
clearTimeout(timeout);
else if (execAsap)
func.apply(obj, args);
timeout = setTimeout(delayed, threshold || 100);
};
}
// smartresize
jQuery.fn[sr] = function(fn){ return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
})(jQuery,'smartresize');
// Here you call GalleryGrid (replace $(window).resize(GalleryGrid) with that):
$(window).smartresize(GalleryGrid);
http://www.paulirish.com/2009/throttled-smartresize-jquery-event-handler/
The reason is your vertical scrollbar. Your content is fixed at width=1030, but when the window size is 1030, the size of the viewport is actually: window size (1030) - vertical scroll bar
Try setting
<body style="overflow:hidden">
You will see that it works correctly when the scrollbar is removed. Or try setting:
<link href="assets/css/tablets-landscape.css" rel="stylesheet" type="text/css" media="screen and (max-width : 1045px)"/>
Set max-width:1045px to make up for scrollbar, you will see that it works correctly.
Your javascript should be like this:
var width = $(window).width() + verticalscrollbarWidth;

Categories

Resources