I am trying to create a Navigation Bar that slides in and out when clicked on using JavaScript and Greensock. For some reason, the on click action is randomly not working when clicked on at different sizes but sometimes it works perfectly fine.
My code is below, you can find a live example of this navigation at: http://www.kramergraphicdesign.com/Maura_Website/
var resize = function(){
var viewportWidth = $(window).width();
var lastLiWith = $('#logo').width();
console.log(openOrShut + " this is the true false var");
if ($(window).width() >= 0 && $(window).width() <= 639 ) {
console.log("mobile");
$("#logo, #close, .arrow-right").click(function()
{
console.log("mobile-click");
if(openOrShut === false)
{
TweenLite.to("#custom-nav",".5",{x:viewportWidth-lastLiWith});
openOrShut = true;
}
else{
TweenLite.to("#custom-nav",".5",{x:0});
openOrShut = false;
}
});
}
else if ($(window).width() >= 640 ) {
console.log("tablet");
$("#logo, #close, .arrow-right").click(function()
{
console.log("tablet-click");
if(openOrShut === false)
{
TweenLite.to("#custom-nav",".5",{x:400});
openOrShut = true;
}
else{
TweenLite.to("#custom-nav",".5",{x:0});
openOrShut = false;
}
});
}
else if ($(window).width() >= 1025 && $(window).width() <= 10000 ) {
console.log("dekstop");
$("#logo, #close, .arrow-right").click(function()
{
console.log("desktop-click");
if(openOrShut === false)
{
TweenLite.to("#custom-nav",".5",{x:400});
openOrShut = true;
}
else{
TweenLite.to("#custom-nav",".5",{x:0});
openOrShut = false;
}
});
}
};
$(document).ready(resize);
$(window).resize(function(){
resize();
});
First of all, the resize event can occur an awful lot, especially during a drag to resize the window. This means two things:
Minimise the amount of work you do so it runs fast, or debounce the function (e.g. using Lodash) so it only runs after you stop receiving resize events for a short time.
More importantly, you are adding a new click handler every single time.
So the reason it "randomly" doesn't do anything is that whenever you click, you actually run your function to toggle the menu many, many times if you have previously resized the window at all. If that number of times happens to be even, then there is no net effect.
There are probably a number of ways to fix this, but here are two:
Attach a click handler once, but check the width inside the handler to determine how far to animate it to / how to respond differently to different sizes.
Unregister existing click events first (using jQuery's .off()) before re-adding them, so there is only ever the one handler registered. I recommend using an event namespace so you can deregister everything on the namespace at once.
Bonus observation: your condition for the tablet widths means the desktop code will never run, because there is no <= 1024 condition for the tablet block.
Related
I know there are many similar posts, but still I haven't get to the code I need.
Basically, I want to make a presentation the first time the user scrolls down. For that, I want to prevent the default action of scroll and (if it's scrolling down) make an animation to the next div.
window.scrolledToRed = false
window.scrolledToGreen = false
window.scrollTo = (to, guard ) =>
$('html, body').animate({
scrollTop: $(to).offset().top
}, 1000, =>
window[guard] = true
)
window.addEventListener 'wheel', (e) ->
if (e.wheelDelta < 0)
if (!window.scrolledToRed)
scrollTo('.red', 'scrolledToRed')
else if (!window.scrolledToYellow)
scrollTo('.green', 'scrolledToGreen')
I've created a Fiddle that represents the problem:
https://jsfiddle.net/pn6zqgwu/2/
When the user scrolls down the first time I want to take him to the red div and the next time to the green one.
None of the solutions I've tried really worked, since it was both "jumping" and scrolling where I want.
Any idea of how to solve the problem?
Thanks in advanced
Maybe you need to call e.preventDefault() to prevent browser default scroll behavior
I have made a fiddle for you, you can make more checks and add animations
var redTouched = false;
var greenTouched = false;
function scrollCb() {
event.preventDefault();
console.log(event.wheelDelta)
if (event.wheelDelta < 0) {
if (!redTouched) {
$(window).scrollTop($('.red').position().top);
redTouched = true;
} else if (redTouched && !greenTouched) {
$(window).scrollTop($('.green').position().top);
greenTouched = true;
} else if (redTouched && greenTouched) {
window.removeEventListener('mousewheel', scrollCb)
}
} else {
window.removeEventListener('mousewheel', scrollCb)
}
window.addEventListener('mousewheel', scrollCb);
https://jsfiddle.net/jacobjuul/b0k03wtr/
I'd like to set up Hammer.js so that I can respond to horizontal pan events. My first attempt looks like this:
var mc = new Hammer(document.body);
mc.on("panleft panright", runBind(this, 'updatePosition'));
mc.on("panend", runBind(this, 'finalisePosition'));
This almost gets the behaviour that I'm looking for: if I pan left or right, the updatePosition function is called, and when I stop panning the finalisePosition function is called.
But these functions are also triggered if the gesture drifts left or right while scrolling vertically. For example, suppose I touch near the top of the screen then drag my finger down half the screen: that should register as a scroll event. Now suppose that I continue by dragging diagonally: downwards and to the left. In this scenario, I'd like to ignore the horizontal part of the gesture and treat the gesture as a vertical scroll event only, but Hammer.js is triggering the panright and panleft events as before.
My next attempt looks like this:
var mc = new Hammer(document.body, {
recognizers: [
[Hammer.Swipe],
[
Hammer.Pan,
{event: 'panvertical', direction: Hammer.DIRECTION_VERTICAL}
],
[
Hammer.Pan, // RecognizerClass
{direction: Hammer.DIRECTION_HORIZONTAL}, // options
['swipe'], // recognizeWith
['panvertical'] // requireFailure
],
]
});
mc.on("panleft panright", runBind(this, 'updatePosition'));
mc.on("panend", runBind(this, 'finalisePosition'));
This specifies that the horizontal pan events should only be triggered if the panvertical event has failed. Sure enough, this prevents the problem I described above. If I begin a vertical scrolling gesture then start to move horizontally, the panleft and panright events are not triggered. But this version has a more serious probelem: the default scroll behaviour doesn't happen! As a result it's impossible to scroll the app.
Can anyone suggest a better solution?
I'm using Hammer.js version 2.0.4.
I've had the same problem and
I've managed to make it work with this really ugly code
var first = false, lock = false;
var containerHandler = function(event) {
if(event.type == 'panend' || event.type == 'pancancel') {
// iOS bug fix
lock = false;
first = false;
} else if(event.type == 'swipe' && event.direction & Hammer.DIRECTION_VERTICAL) {
// iOS bug fix
lock = true;
first = true;
}
else if(event.type == 'panmove') {
// iOS bug fix ...
if(first === false && event.direction & Hammer.DIRECTION_VERTICAL) {
lock = true;
}
first = true;
if(lock === true)
return;
//your code etc...
};
var focusMC = new Hammer.Manager(mainContainer[0], {domEvents:true});
var pan = new Hammer.Pan({threshold: 5, direction:Hammer.DIRECTION_HORIZONTAL});
focusMC.add( pan );
focusMC.on('panstart panmove panend pancancel swipe', containerHandler);
small threshold is important..
As far as I remember this issue was only on iOS Safari, but on WP it worked correctly even without this code
Edit: Instead of the solution above, try forcing the touch-action property
to pan-y
I'm keeping both answers as they should all work
Hammer.js will automatically infer a touch-action property based on your recognizers. This might make the application more responsive, but it won't prevent vertical page scrolling just because a user is interacting with the element.
I had the same problem you were having, and found a dead simple solution that works pretty well for a workaround.
var isScrolling = false;
var galleryHammer = new Hammer(element, {
recognizers: [
[Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL }]
]
});
// Making the event listener passive means we don't get any delays between
// the user scrolling and the browser having checked if it should prevent
// that event
window.addEventListener('scroll', function() {
isScrolling = true;
}, { passive: true });
window.addEventListener('touchend', function() {
isScrolling = false;
});
galleryHammer.on('pan', function(event) {
if (isScrolling) {
return;
}
// Normal logic...
});
This worked for me (the code comes from a class, and this.hm is simply an Hammer instance):
this.hm.on('panleft', function(e){ // ...and same for panright
if(e.pointerType == 'touch' && (Math.abs(e.deltaY) > Math.abs(e.deltaX))){ return false; }
// do stuff
}
The extra pointerType check is there because I didn't have any issue on desktop computer (mouse events). So the whole is applied ony on touch devices/events.
Good day all.
I'm having some problems with hoverintent.js a jquery plugin that handle the mouseOver events in a different way than normal.
Due to some complications, I can't modifiy anything but the js of this plugin, but I need to make it compliant with touch events and not only with mouseOver and mouseLeave.
after some debugs, I have managed to recognize this part of the code to be the one to modify:
var handleHover = function(e) {
// next three lines copied from jQuery.hover, ignore children onMouseOver/onMouseOut
var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
while ( p && p != this ) { try { p = p.parentNode; } catch(e) { p = this; } }
if ( p == this ) { return false; }
// copy objects to be passed into t (required for event object to be passed in IE)
var ev = jQuery.extend({},e);
var ob = this;
// cancel hoverIntent timer if it exists
if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
// else e.type == "onmouseover"
if (e.type == "mouseover") {
// set "previous" X and Y position based on initial entry point
pX = ev.pageX; pY = ev.pageY;
// update "current" X and Y position based on mousemove
$(ob).bind("mousemove",track);
// start polling interval (self-calling timeout) to compare mouse coordinates over time
if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
// else e.type == "onmouseout"
} else {
// unbind expensive mousemove event
$(ob).unbind("mousemove",track);
// if hoverIntent state is true, then call the mouseOut function after the specified delay
if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
}
}
};
// bind the function to the two event listeners
return this.mouseover(handleHover).mouseout(handleHover);
what I've done so far is to make the function working different with mobiles:
var handleHover = function(e) {
isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if(isMobile){
console.log("Ismobile");
}else{
... Same code as before here ...
}
// bind the function to the two event listeners
return this.mouseover(handleHover).mouseout(handleHover);
and now i'm struck. I would like it to "change" its behavior to handle the touch, and not the mouse over event, so on mobiles I will need to touch the element, instead to hovering on it. May someone give me an help? Am I on the right way? Is it the right way to think of it?
unluckily I have only the possibility to change this file and some few more.
Recently i bumped into several problems with changing hoverIntent.js, and ended up in writing my own plugin: hoverDelay.js (much simpler, and less code). see if you can use it, and modify it to your own needs (and maybe contribute the mobile code to it :-)
I'm trying to detect orientation changes on mobile devices and trigger a function once an orientation has been completed, but the code inside the function is continuously firing in Android and I'm not sure why. Here's my code:
var supportsOrientationChange = "onorientationchange" in window;
var orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";
window.addEventListener(orientationEvent,
function() { alert ('orientation changed'); },
false
);
Does anyone know how to write this so that it only triggers once, after orientation change has been completed?
I used a work-around for this in my app. I added an event listener for orientationchange and then set a timeout so the orientationchange could occur and I could get a new width value that actually reflected the width after the orientation change.
var device_width = 0;
$(document).ready(function () {
var oc_timer;
$(window).bind('orientationchange', function () {
clearTimeout(oc_timer);
oc_timer = setTimeout(function () {
device_width = $(window).width();
}, 500);
});
});
I am not positive this will solve your continuously firing function problem but this is a solution I found to work.
I found a way to do this in case others are still having the same issue, check it out:
function onOrientationChange() {
if (typeof(this.orientation) === "undefined"){
if (window.orientation == -90 || window.orientation == 90) {
this.orientation = "landscape";
} else {
this.orientation = "portrait";
}
// alert ("in 1");
// you code here for change
}
if ((window.orientation == -90 || window.orientation == 90) && (this.orientation == "portrait")) {
this.orientation = "landscape";
// alert ("in 2");
// you code here for change
}
if ((window.orientation == 0 || window.orientation == 180) && (this.orientation == "landscape")) {
this.orientation = "portrait";
// alert ("in 3");
// you code here for change
}
}
Simple solution for less detailed applications
//bind orientation change event
$(window).bind("orientationchange", orientationChange);
//later,
function orientationChange(){
//whatever needs to happen when it changes.
alert("changed");
}
You could also use $.on() instead if you need to pass information.
In browsers that do not support it, the event does not fire. You could then write a custom detection script as a fallback to fire the event if the orientation changes on a device that does not support onorientationchange.
I'm creating a scrolling effect using JQuery and I'm wondering if it's possible to distinguish between the user scrolling vs. programmatically scrolling.
I have something like this:
$('#element').on('scroll',function(e){
$('#element').stop(true); // stop previous scrolling animation
$('#element').animate({ // start new scrolling animation (maybe different speed, different direction, etc)
scrollTop:...
});
});
However, this event is triggered during every step of the animation. How can I tell if this event was triggered by the user or by the animation?
Use a variable to determine when you are scrolling programmatically
Example:
var programScrolling = false;
$('#element').on('scroll',function(e){
if (programScrolling) {
return;
}
$('#element').stop(true); // stop scrolling animation
programScrolling = true;
$('#element').animate({
scrollTop:...
});
programScrolling = false;
});
Not sure if that is exactly what you want, but the concept should work.
I would make functions for different kinds of scrollings to detect them and call a scroll handler for all of them, like so:
JS Fiddle
$(window).bind('mousewheel DOMMouseScroll', function(event){
var direction;
if (event.originalEvent.wheelDelta > 0 || event.originalEvent.detail < 0) {
direction = 'up';
}
else {
direction = 'down';
}
scrollHandler(direction, 'mouseWheel');
event.preventDefault();
});
var scrollHandler = function(direction, origin) {
var height = $(document).scrollTop();
var movement = (direction == 'up') ? -100 : 100;
console.log(origin);
$('body').stop(true);
$('body').animate({
scrollTop: height + movement
}, 250);
};
Then you can do different stuff according to the origin of the event!
You could also check if the user scrolls to the same direction that the screen is scrolling and do something different, or whatever you want with the info passed by the mousewheel event.
Original mousewheel event function copied from THIS answer
I would suggest possibly using the .originalEvent method. The downside is, this is very browser dependent. See here. Hopefully the following helps:
$('#element').scroll(function(e){
var humanScroll = e.originalEvent === undefined;
if(humanScroll) {
$(this).stop(true);
}
})