Javascript clear timeout on mousemove - javascript

I have created an animated menu that opens when the users cursor is placed within 20px of the right hand side of their screen. I want to prevent the menu opening if the users cursor moves out of this region within 2 seconds but I'm struggling with the Javascript timeouts. My code looks like this so far:
HTML
Javascript
// Timer variable
var timer;
function openToolbar()
{
// Only execute for desktop
$('.no-touch').on('mousemove',function(event) {
// Toolbar and Window width
var tableToolbar = $('.ac-table-toolbar'),
winWidth = $(window).width();
// If cursor enters right hand side of the screen start the timer
// and execute after 2 seconds
if(event.pageX > (winWidth - 20)) {
// Timeout
timer = setTimeout(function()
{
// Add active class to toobar and css transition will animate it
// to open position
tableToolbar.addClass('active').removeClass('notActive');
}, 2000);
}
// If mouse pointer leaves right hand side of the screen and
// still has notActive class cancel the timeout to prevent
// the toolbar from opening
if(event.pageX < (winWidth - 20) && tableToolbar.hasClass('notActive'))
{
clearTimeout(timer);
}
// Toolbar has active class so we know its visible
if(tableToolbar.hasClass('active') && event.pageX < (winWidth - 220))
{
// Clear timeout (if needed?)
clearTimeout(timer);
// Remove active class and css transition will return it to docked position
tableToolbar.removeClass('active').addClass('notActive');
}
});
}
The animation is handled with CSS transitions that are triggered by the active notActive classes.
Please can anyone point me in the right direction. Many thanks in advance.

Too complex for this task. Big amount of mousemove events will slow down your page. Try to use another approach:
HTML:
<div id='rightActivateZone'></div>
CSS:
#rightActivateZone {
background-color: red; // change to transparent for your purpose
height: 100%;
width: 20px;
position: fixed;
top: 0;
right: 0;
}
JS:
var timer;
$('#rightActivateZone').on('mouseenter', function() {
timer = setTimeout(function() {
alert('fire!'); // your code to show menu is here
}, 2000);
});
$('#rightActivateZone').on('mouseleave', function() {
clearTimeout(timer);
});
JSFiddle demo

I agree with finelords answer. That is the best approach but to answer your question
Demo: http://jsfiddle.net/robschmuecker/EZJv6/
We had to do a check on the timer being in existence aswell, see comments below.
JS:
var timer = null;
function openToolbar() {
// Moved these out of event to prevent re-processing.
// Toolbar and Window width
var tableToolbar = $('.ac-table-toolbar'),
winWidth = $(window).width();
// Only execute for desktop
$('.no-touch').on('mousemove', function (event) {
// If cursor enters right hand side of the screen start the timer
// and execute after 2 seconds
// here you are setting a timer on every mousemove, even the ones when the cursor is over the active bar so we need to fix by checking if
if (event.pageX > (winWidth - 20) && tableToolbar.hasClass('notActive') && timer == null) {
// Timeout
console.log('setting timeout');
timer = setTimeout(function () {
// Add active class to toobar and css transition will animate it to open position
tableToolbar.addClass('active').removeClass('notActive');
}, 500);
}
// If mouse pointer leaves right hand side of the screen and
// still has notActive class cancel the timeout to prevent
// the toolbar from opening
if (event.pageX < (winWidth - 20) && tableToolbar.hasClass('notActive') && timer != null) {
clearTimeout(timer);
timer = null;
console.log('cancelling timeout 1');
}
// Toolbar has active class so we know its visible
if (tableToolbar.hasClass('active') && event.pageX < (winWidth - 20)) {
// Clear timeout (if needed?)
clearTimeout(timer);
timer = null;
console.log('cancelling timeout 2');
// Remove active class and css transition will return it to docked position
tableToolbar.removeClass('active').addClass('notActive');
}
});
}
openToolbar();

Rather than clearing the time out, perhaps let it run, but keep track of the latest mouse position
var currentX;
function openToolbar()
{
// Only execute for desktop
$('.no-touch').on('mousemove',function(event) {
currentX = event.pageX;
// Toolbar and Window width
var tableToolbar = $('.ac-table-toolbar'),
winWidth = $(window).width();
// If cursor enters right hand side of the screen start the timer
// and execute after 2 seconds
if(currentX > (winWidth - 20)) {
// Timeout
timer = setTimeout(function()
{
// check the mouse position after the timeout
if(currentX > (winWidth - 20)) {
// Add active class to toobar and css transition will animate it
// to open position
tableToolbar.addClass('active').removeClass('notActive');
}
}, 2000);
}
// Toolbar has active class so we know its visible
if(tableToolbar.hasClass('active') && currentX < (winWidth - 220))
{
// Remove active class and css transition will return it to docked position
tableToolbar.removeClass('active').addClass('notActive');
}
});
}

Related

JavaScript – detect if element stays in viewport for n seconds

I’m need to push a data layer event whenever a content block of a certain css class is visible for 5 seconds (a sign that the user is reading the content.
Ive used something like this:
$(window).on(‘scroll resize’, function() {
$(‘.myClass’).each(function(element) {
If (isInViewport(element)) {
setTimeout(function() {
if (isInViewport(element)) {
... // Push the data layer event.
}
}, 5000);
}
});
});
function isInViewport(element) {
... // Returns true if element is visible.
};
Just wrote this from memory, so it may not be 100% correct, but the gist is I try to:
Test visibility on every myClass element on scroll/resize
If one is visible, wait 5 seconds and check the same element one more time.
Trouble is, element is undefined when setTimeout runs isInViewport. Maybe jQuery’s .each and setTimeout are a bad match?
I managed to do this using the intersection observer. My requirements were to check if the element was 50% in view for at least a second and if so then trigger an event.
let timer;
const config = {
root: null,
threshold: 0.5 // This was the element being 50% in view (my requirements)
};
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
timer = setTimeout(() => {
//... push to data layer
}, 1000);
} else {
clearTimeout(timer);
}
});
}, config);
observer.observe(YourElement);
I used the jquery-visible plugin to achieve a script that will output the time (in seconds) since a particular element is in view. The output uses an interval of X seconds... out of the scroll handler.
On stop scrolling, we check all the monitored elements to know if they're in the viewport.
If an element is, we check if it already was logged in the visible_begins array on a previous scroll stop. If it isn't, we push an object containing its id and the actual time in milliseconds.
Still on scroll stop, if an element isn't in the viewport, we check if it was logged in the visible_begins and if it's the case, we remove it.
Now on an interval of X seconds (your choice), we check all the monitored elements and each that is still in viewport is outputed with the time differential from now.
console.clear();
var scrolling = false;
var scrolling_timeout;
var reading_check_interval;
var reading_check_delay = 5; // seconds
var completePartial = false; // "true" to include partially in viewport
var monitored_elements = $(".target");
var visible_begins = [];
// Scroll handler
$(window).on("scroll",function(){
if(!scrolling){
console.log("User started scrolling.");
}
scrolling = true;
clearTimeout(scrolling_timeout);
scrolling_timeout = setTimeout(function(){
scrolling = false;
console.log("User stopped scrolling.");
// User stopped scrolling, check all element for visibility
monitored_elements.each(function(){
if($(this).visible(completePartial)){
console.log(this.id+" is in view.");
// Check if it's already logged in the visible_begins array
var found = false;
for(i=0;i<visible_begins.length;i++){
if(visible_begins[i].id == this.id){
found = true;
}
}
if(!found){
// Push an object with the visible element id and the actual time
visible_begins.push({id:this.id,time:new Date().getTime()});
}
}
});
},200); // scrolling delay, 200ms is good.
}); // End on scroll handler
// visibility check interval
reading_check_interval = setInterval(function(){
monitored_elements.each(function(){
if($(this).visible(completePartial)){
// The element is visible
// Check all object in the array to fing this.id
for(i=0;i<visible_begins.length;i++){
if(visible_begins[i].id == this.id){
var now = new Date().getTime();
var readTime = ((now-visible_begins[i].time)/1000).toFixed(1);
console.log(visible_begins[i].id+" is in view since "+readTime+" seconds.")
}
}
}else{
// The element is not visible
// Remove it from thevisible_begins array if it's there
for(i=0;i<visible_begins.length;i++){
if(visible_begins[i].id == this.id){
visible_begins.splice(i,1);
console.log(this.id+" was removed from the array.");
}
}
}
});
},reading_check_delay*1000); // End interval
.target{
height:400px;
border-bottom:2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-visible/1.2.0/jquery.visible.min.js"></script>
<div id="one" class="target">1</div>
<div id="two" class="target">2</div>
<div id="three" class="target">3</div>
<div id="four" class="target">4</div>
<div id="five" class="target">5</div>
<div id="six" class="target">6</div>
<div id="seven" class="target">7</div>
<div id="eight" class="target">8</div>
<div id="nine" class="target">9</div>
<div id="ten" class="target">10</div>
Please run the snippet in full page mode, since there is a couple console logs.
CodePen
You can use this function to check if an element is in the viewport (from this answer):
function isElementInViewport (el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
);
}
<input id="inViewport"/>
<span style="margin-left: 9999px;" id="notInViewport">s</span>
<script>
function isElementInViewport (el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
);
}
console.log("#inViewport in viewport: "+isElementInViewport(document.getElementById("inViewport")));
console.log("#notInViewport in viewport: "+isElementInViewport(document.getElementById("notInViewport")));
</script>
You can try using Waypoints, its a library that allows you to determine when a element enters or leaves that viewport. You pass it an event handler that accepts a direction parameter. The direction tells you whether the tracked element entered or exited the screen. Once you detect the element has entered the screen then start a timer. If you don't see and event for when the element exited the viewport then you know it has been on screen for that period of time.

mouse over event within certain window y range

I am working on a project that requires the full length logo shrink to short initial in 2 situations:
A) when page scroll down past 300px.
and
B) if page hasn't scroll past 300px (meaning full length logo still showing), shrink the full length logo to initial to accommodate pulldown menu when mouse over the top menu items.
Here is the code I tried:
it is working but when page scroll past 300px the mouse out should not happen. It should keep the logo as the smaller initial format. Right now the mouse out will happen no matter the page is scroll past 300px or not.
/* shrink logo when page scroll past 300px by adding .smaller class to #logo. */
window.onscroll = function() {myFunction()};
function myFunction() {
if (document.body.scrollTop > 300 || document.documentElement.scrollTop > 300) {
document.getElementById("logo").className = "smaller";
} else {
document.getElementById("logo").className = "";
}
}
/* shrink logo when mouse over top menu items (.showlp) only if page has NOT scroll past 300px */
$(document).ready(function(){
$(".showlp").mouseover(function(){
if (document.body.scrollTop < 300 || document.documentElement.scrollTop < 300) {
document.getElementById("logo").className = "smaller";
}
})
$(".showlp").mouseout(function(){
if (document.body.scrollTop < 300 || document.documentElement.scrollTop < 300) {
document.getElementById("logo").className = "";
}
})
});
Any help is appreciated.
Pass parameters via data attribute in the body tag and access that value via you javascript code.
Eg.
then on your javascript
var ctrl=
$("body").attr("
data-var");
then on your window scroll function set the attribute value to 1, aand listen to it on your mouse over function to know when to
add the functionality.
Eg.
Function
myfunction(){
if(document.bo
dy.scrollTop>3
00){
// swap urclass
// then do
$("body").attr("
data-var", 1);
}
then on your mouse over function do:
$(document).re
ady(function(){
$(".showIp").mo
usover(functio
n(){
var ctrl=
$("body").attr("
data-var");
if(ctrl==1){
//swap class
}
else{
//keep swap
}
});
});

Doing something on second click with Snap.svg

I am writing a basic code for an animation on click with Snap.svg. It looks like this:
var s = Snap(500, 500);
var circle = s.rect(100,100,100,100);
circle.click(function(){
var width = circle.attr('width');
var height = circle.attr('height');
circle.animate({
width: width/2,
height :height/2
}, 2000);
});
I make a rectangle in the top left corner of the container and animate it's width on a click. THEN, however, I want to do something different on the second click, return it to its original state for example.
I'd also be glad to learn how do you handle this second click in Javascript in general.
For example: press this button once and the slide navigation opens. Tap it second time and the navigation dissappears.
Thanks in advance!
You can do that by using the event.detail property. In your case, that would be:
circle.click(function(e) {
var width = circle.attr('width');
var height = circle.attr('height');
if (e.detail == 1) {
circle.animate({
width: width/2,
height :height/2
}, 2000);
} else if (e.detail == 2) {
circle.animate({ //example
width:width,
height:height
}, 2000);
}
});
There, the animation to change back to the original sizes plays when the user performs a double click (so 2x fast). If you basically want to toggle the element, instead of reverting it on doubleclick, you can simply check if the element has a width or height style other than its initial width or height:
circle.click(function(e) {
var width = circle.attr('width');
var height = circle.attr('height');
if (parseInt(this.style.width) == parseInt(width) || !this.style.width) {
circle.animate({
width: width/2,
height :height/2
}, 2000);
} else {
circle.animate({ //example
width:width,
height:height
}, 2000);
}
});
Then the if() will return true when either the width attribute is equal to the width style, or when the width style is empty/not defined.
You'd want to store what state of the click.
There are many different ways to go about this, but I'll choose two:
Create a counter variable (say, counter) and increment it each time the click handler runs. Then, each time, to decide what to do, see if the number is even or odd:
var counter = 0;
circle.click(function(){
if(counter % 2 == 0){
//action1
}else{
//action2
}
counter++;
});
Alternatively, you can use a Boolean that changes each time to keep track of which action to perform.
var flag = true;
circle.click(function(){
if(flag){
//action1
flag = false;
}else{
//action2
flag = true;
}
});

jQuery - stop autoscrolling div when hovered

This is a follow-up post to a previous question: jQuery - scroll down every x seconds, then scroll to the top
I have refined the scrip a little further, but am having a little trouble with the last step.
I have a div that automatically 50px at a time until it reaches the bottom, at which point it scrolls to the top and starts again. I have this working perfectly thanks to the above question and with a little add work.
I need to make all scrolling stop when the div is hovered. I have done part of this already (there is no incremental scrolling down on hover) but I cannot get the full picture. The div will still scroll to the top even when hovered.
Here is my jQuery and a fiddle to go along with it: http://jsfiddle.net/wR5FY/1/
var scrollingUp = 0;
var dontScroll = 0;
window.setInterval(scrollit, 3000);
function scrollit() {
if(scrollingUp == 0 && dontScroll == 0) {
$('#scroller').animate({ scrollTop: $("#scroller").scrollTop() + 50 }, 'slow');
}
}
$('#scroller').bind('scroll', function () {
if (dontScroll == 0) {
if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
scrollingUp = 1;
$('#scroller').delay(2000).animate({ scrollTop: 0 }, 1000, function() {
scrollingUp = 0;
});
}
}
});
$('#scroller').bind('mouseenter', function() {
dontScroll = 1;
});
$('#scroller').bind('mouseleave', function() {
dontScroll = 0;
});
​
In the fiddle, try hovering the scroller div when the yellow square is visible. You will see that it scrolls to the top.
A couple of notes:
You will notice I have used mouseenter and mouseleave rather than hover and mouseout. This was the best way I could find to ensure all child elements within the div didn't have an adverse affect.
A potential problem area is the fact that I have binded to the scroll event for my function that scrolls to the top. I think this might cause some additional problems when a user is manually scrolling through the items, with my jQuery trying to scroll against the user.
I did a little experimenting with killing setInterval, but I didn't find this to be very helpful as the function that triggers isn't the problem area.
My overall goal here is to lock down all automatic scrolling when a user is hovering or manually scrolling through the list. This is 90% there. If they happen to scroll to the bottom, NOTHING should happen until they move the mouse elsewhere - this is the problem.
Keep it easier ;)
The problem was that you first evaluate wheter dontScroll is zero, then start the timer.
When the timer has ended, it doesnt evaluate anymore, whether dontScroll STILL is zero.
Just pulled that into your scrollIt function:
var scrollingUp = 0;
var dontScroll = 0;
window.setInterval(scrollit, 2000);
function scrollit() {
if(dontScroll == 0){
if ($('#scroller').scrollTop() + $('#scroller').innerHeight() >= $('#scroller')[0].scrollHeight) {
scrollingUp = 1;
$('#scroller').animate({ scrollTop: 0 }, 1000, function() {
scrollingUp = 0;
});
} else if(scrollingUp == 0) {
$('#scroller').animate({ scrollTop: $("#scroller").scrollTop() + 50 }, 'slow');
}
}
}
$('#scroller').bind('mouseenter', function() {
dontScroll = 1;
});
$('#scroller').bind('mouseleave', function() {
dontScroll = 0;
});

In Responsive Web Design, how can I use Javascript or jQuery to stop a function and reset when the users downsizes their window?

I have a responsive layout and I've created a fading panels animated element with jQuery. I set the jQuery function to only activate if the user is above a certain screen size. However, if I scale the window down, the function still runs.
Here's what I'd like to achieve:
When the user scrolls bellow a browser width of 1440px, stop that fading panels animation.
Once the animation is stopped, I want to reset the area to display the 1st panel.
If the user scrolls back up above that screen size, start the animation again.
Here is my code, thanks in advance for your time:
// Get the viewport size!
var viewport = $(document).width();
if (viewport >= 1140) {
var InfiniteRotator =
{
init: function()
{
// hard set height of container
$('#circles').height('286.717px');
// initial fade-in time
var initialInterval = 3000;
// interval between items
var itemInterval = 5000;
// cross-fade time
var fadeTime = 2000;
// count number of items
var numberOfItem = $('.rotating-item').length;
// set current item
var currentItem = 0;
// create loop
var infiniteLoop = setInterval(function() {
// initial fade out
$('.rotating-item').eq(currentItem).fadeOut(fadeTime);
// set counter
if (currentItem == numberOfItem -1) {
currentItem = 0;
} else {
currentItem++;
}
// next item fade in
$('.rotating-item').eq(currentItem).fadeIn(fadeTime);
}, itemInterval);
}
}
// go Go GO!
InfiniteRotator.init();
}
Please note: I used this great tutorial to create the fading panels: http://trendmedia.com/news/infinite-rotating-images-using-jquery-javascript/
window.onresize = function() {
clearInterval(infiniteLoop)
}
Note that you must still be in your init function in order to get the infiniteLoop variable.
You can use
window.onresize = function(){ ... }
$(window).resize(function() {
//stop and reset animation in here.
});

Categories

Resources