How do I wait for an element to be displayed/seen by the user? I have the following function, but it only checks to see if the element exists and not whether it is visible to the user.
function waitForElementDisplay (selector, time) {
if (document.querySelector(selector) != null) {
return true;
} else if (timeLimit < timeSince) {
return false;
} else {
timeSince += time;
setTimeout(function () {
waitForElementDisplay(selector, time, timeLimit, timeSince);
}, time);
}
}
It's a bit confuse, are you trying a simple "sleep"?
You can wait element load using:
document.querySelector(selector).onload = function() {
// Your code ...
}
Some working snippet, run it:
// Triggering load of some element
document.querySelector("body").onload = function() {
// Setting the timeout and visible to another element
setTimeout(function () {
document.querySelector("#my_element").style.display = "block" /* VISIBLE */
}, 1000);
}
#my_element{
width: 100px;
height: 100px;
background-color: red;
display: none; /* INVISIBLE */
}
<div id="my_element"></div>
If you want to wait time as you have set to the function and the selector which should appear after this time.. You can mind about a simple setTimeout() and CSS.
Run the example below, hope it helps:
// Triggering, in my exemple i've used: WINDOW ONLOAD
window.onload = function() {
waitForElementDisplay("#my_element", 1000);
}
function waitForElementDisplay (selector, time) {
// Check the DOM Node in console
console.log(document.querySelector(selector));
// If it's a valid object
if (typeof document.querySelector(selector) !== "undifined") {
// Setting the timeout
setTimeout(function () {
document.querySelector(selector).style.display = "block" /* VISIBLE */
}, time);
}
}
#my_element{
width: 100px;
height: 100px;
background-color: red;
display: none; /* INVISIBLE */
}
<div id="my_element"></div>
You could use jQuery .ready()
selector.ready(function(){
// do stuff
})
Related
I'm using a .slideToggle to show a div once an image is clicked. I want the div to disappear 10 seconds after the last time the toggle is clicked. The problem is that if I click the image a few times, the duration is 10 seconds after the first click and not the last. If you view the fiddle (I used a shorter duration for testing) and click the image a few times you will see what I mean.
Does anyone have any idea how I can get this working as desired? Any help would be greatly appreciated!
Fiddle: https://jsfiddle.net/7fy536nv/
Requirements...
The div should show for 10 seconds then disappear
The div will disappear if the image is clicked again
The div will disappear if something outside the div is clicked
HTML
<div class="box-new">
<a href="box-link" id="box-link">
<img src="https://dummyimage.com/100x60/ff0000/fff.png">
</a>
</div>
<div id="empty-box">jkhsahg akjfhsajk fhas jklsad flkasd hfjkashd fjka sdkjfh adskjfhs dakjfh kafh sdah dhjaf</div>
CSS
body, html {
margin: 0;
}
#empty-box {
display: none;
position: absolute;
background: #000;
top: 60px;
width: 240px;
padding: 20px;
left: 0;
color: #fff;
text-align: center;
font-family: "open sans", "arial";
font-size: 14px;
font-weight: 600;
line-height: 18px;
z-index: 1;
}
JS
$('#box-link').click(function(event){
event.stopPropagation();
$("#empty-box").slideToggle(400);
setTimeout(function() {
$("#empty-box").slideUp();
}, 5000);
return false;
});
$("#empty-box").on("click", function (event) {
event.stopPropagation();
});
$(document).on("click", function () {
$("#empty-box").slideUp(400);
});
Assign your call to setTimeout to a variable declared in the outer scope and clear it with clearTimeout in every subsequent event:
var timeout;
$('#box-link').click(function(event){
clearTimeout(timeout);
event.stopPropagation();
$("#empty-box").slideToggle(400);
timeout = setTimeout(function() {
$("#empty-box").slideUp();
}, 5000);
return false;
});
The setTimeout function returns a value that you can cancel using clearTimeout.
So in your code, store the return value, and each time it is clicked, cancel the previous timeout and restart a new one.
var timeout = null;
function test()
{
if( timeout !== null )
clearTimeout(timeout);
timeout = setTimeout(..., 10000);
}
It's quite simple, really. The key is placing your setTimeout in a variable and calling clearTimeout(variable).
Example:
let someVar = false,
someTime = 5000,
msgTimer = document.getElementById('timer'),
timer,
current,
displayTimer = false;
$('.yourButton').on('click', someFunc)
function someFunc() {
if (someVar) {
clearTimeout(someVar) // <<< juice is here
console.log('cleared timeout!')
}
timer = performance.now()
someVar = setTimeout(function () {
clearInterval(displayTimer)
console.log(someTime / 1000 +
' seconds passed since last click...')
someVar = false
displayTimer = false
msgTimer.innerHTML = ''
}, someTime)
/**
* ¯\_(ツ)_/¯
* ignore past this point, rest is timer
*
* ˙ʇᴉ pǝǝu ʇ,uop no⅄ ˙ʎllɐǝɹ
**/
if (displayTimer) {
clearInterval(displayTimer)
displayTimer = false
}
displayTimer = setInterval(function () {
current = performance.now()
msgTimer.innerHTML = Math.max(timer + 5000 - current,0)
.toFixed(2) + 'ms'
}, 15)
}
#timer {
font-family: monospace;
text-align:right;
display: inline-block;
width: 100px;
font-size: 1.2rem;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="yourButton">Don't click. Think.</button>
<span id="timer"></span>
Reduced interval to 5 seconds for faster testing.
Here is a solution to show a div for a maximum of 10 seconds...
And hide it if user clicks anywhere before this delay.
There is no problem with the delay if user clicks often or repeately...
Because I took care of resets.
I created a CodePen, with a timer displayed aside the cart link, to show the time passing.
And here is a CodePen with the exact same code as in the below snippet, if your want to play with it.
var emptyCart = $("#emptyCart");
var cartTimer;
var carMaxTime = 10000;
// Function to hide the cart
var hideCart = function(){
emptyCart.dequeue().slideUp("slow");
}
// Function to show the cart
var showCart = function(){
emptyCart.dequeue().slideDown("slow");
clearTimeout(cartTimer); // Just to be sure We have only one timer running
cartTimer = setTimeout(function(){
hideCart();
},carMaxTime);
}
// Function to handle click on the cart link
$("#clickCart").click(function(){
$(document).off("click",hideCart()); // Just to prevent a slideUp which would counter-act a slideUp
if(emptyCart.is(":hidden")){
showCart();
}
setTimeout(function(){ // 1ms delay to use this event handler on next click.
$(document).on("click",function(){
hideCart();
$(document).off("click",hideCart()); // Unbind this handler once used.
});
},1);
});
#emptyCart{
display:none;
width:100px;
background:#000;
color:#fff;
height:30px;
margin-top:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="clickCart">
Cart
</div>
<div id="emptyCart">
No items
</div>
Here is my code;
$(window).resize(function() {
if ($(window).width() >= 760) {
$('.sidebar-left').one().css("display", "block")
} else if ($(window).width() <= 760) {
$('.sidebar-left').one().css("display", "none")
}
});
var m = $(".menu");
m.addClass('fa-bars');
m.on('click', function() {
if (m.hasClass('fa-bars')) {
m
.removeClass('fa-bars')
.addClass('fa-times');
$(".sidebar-left").css("display", "block")
} else {
m
.removeClass('fa-times')
.addClass('fa-bars');
$(".sidebar-left").css("display", "none")
}
});
.sidebar-left {
width: 100px;
position: fixed;
background-color: black;
height: 100%;
}
.menu.fa {
display: none;
}
#media handheld,
screen and (max-width: 760px) {
.sidebar-left {
display: none;
width: 100%;
background-color: black;
height: 100px;
}
.menu.fa {
display: inline-block;
font-size: 35px;
color: black;
}
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.2/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="menu fa"></div>
<div class="sidebar-left"></div>
And a plunk. (may be easier to test here)
So it's just a simple menu. The issue I have is that currently the resize event will run every time the page is resized, so if I have the menu displayed under 760 pixels it will automatically disappear on resize if I have it opened.
If I take the javascript resize function out completely and leave it to the css, the problem is if I open and close the menu in a smaller window, the display will be set to none when I make the window larger again.
I've tried a few other things, but can't seem to think of a way of doing this. The aim is to just to replicate what the $(window).resize function is doing currently, but for that event to fire only once the window has resized past the points.
Hope the problem is explained clearly.
Thanks
You could use jQuery's one() method, which gets activated exactly once:
$(window).one('resize', function() {
if ($(window).width() >= 760) {
$('.sidebar-left').one().css("display", "block")
} else if ($(window).width() <= 760) {
$('.sidebar-left').one().css("display", "none")
}
});
This will solve your question as asked, but i would actually recommend not disabling the resize event and just handling it better, i.e. checking whether it is proper to open/close the sidebar.
Here's a plugin I made, with a resize begin handler, a resize end handler and of course an on resize handler.
(function() {
var resizeEventHandler = function(resizeTarget, _maxResizeWaitTime, onResizeBeginCB, onResizeEndCB, onResizeCB) {
if(resizeTarget){
var resizeTimer = null;
var resizeBegun = false;
var maxResizeWaitTime = _maxResizeWaitTime || 500;
resizeTarget.addEventListener('resize', function() {
if (!resizeBegun) {
if(onResizeBeginCB){
onResizeBeginCB();
}
resizeBegun = true;
}
if(onResizeCB){
onResizeCB();
}
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
if(onResizeEndCB){
onResizeEndCB();
}
resizeBegun = false;
}, maxResizeWaitTime);
});
}else{
console.error('No resizeTarget specified!');
}
};
this.resizeEventHandler = resizeEventHandler;
}).call(this);
Call it like this:
function onResizeBegin() {
$("body").append('onResizeBegin<br/>')
}
function onResizeEnd() {
$("body").append('onResizeEnd<br/>')
}
function onResize() {
$("body").append('onResize<br/>')
}
var resizeEventHandler1 = new resizeEventHandler(window, 500, onResizeBegin, onResizeEnd, onResize);
JSfiddle: https://jsfiddle.net/sv52m1y2/3/
I'm working with this js fiddle here: http://jsfiddle.net/tPx6x/
The animation works like so
You hover over the text, a circle fades in & begins to pulse 1 second later for as long as your mouse is over the text.
When your mouse pointer leaves the text, the pulse stops after one second and the circle fades out.
The issue arises when you do this:
Put your mouse over the text, remove the pointer from the text, THEN place the pointer back over the text before the script has a chance to finish(1-1.4s).
You won't be able to make the circle appear properly agin...you will have to allow the script to reset. That is the problem.
What is the best way to tackle this issue?
Example code:
<div class='circle__title_project-management'>
<h1>project management</h1>
</div>
<div class='circle__project-management hidden'></div>
.circle__project-management, .circle__title_project-management
{
display: inline-block;
cursor: pointer;
}
.circle__project-management
{
margin-left: 8px;
vertical-align: -4.07px;
background-color: transparent;
border: 2px solid #00DBFF;
width: 30px;
height: 30px;
border-radius: 90px;
top: 280px;
left: 40px;
}
.hidden
{
visibility: hidden;
}
.visible
{
visibility: visible;
}
.animate-infinite
{
animation-iteration-count: infinite;
-moz-animation-iteration-count: infinite;
-webkit-animation-iteration-count: infinite;
}
var circleTitle = $('.circle__title_project-management h1');
var circle = $('.circle__project-management');
var initTimeout = 1000;
var initTimeoutPlus = 1400;
circleTitle.mouseover( function() {
circle.removeClass('hidden');
circle.addClass('animated fadeIn');
setTimeout( function() {
circle.addClass('pulse animate-infinite');
circle.removeClass('fadeIn');
}, initTimeout);
});
circleTitle.mouseleave( function() {
setTimeout( function() {
circle.stop().removeClass('pulse animate-infinite visibility');
circle.addClass('fadeOut');
}, initTimeout);
setTimeout( function() {
circle.removeClass('fadeOut');
circle.addClass('hidden');
}, 1400);
});
You should note that setTimeout has a return value. You want to clear previous timeouts before you start new ones; otherwise you can get a timeout queue which completely skews your animations. Something like this:
var myTimeout;
...
clearTimeout(myTimeout);
myTimeout = setTimeout(...);
Not sure if this is exactly what you were going for, but along these lines: http://jsfiddle.net/FYY38/
More info here: http://www.w3schools.com/js/js_timing.asp
Also, it looks like the circle.stop() call is doing nothing (as it's css-animated)
To avoid antagonist behaviours, maybe add a class to your element to tag it when the event is triggered and remove it when another is triggered.
That way you can stay in control of what's going on.
you can set time out to mouse over function to cover the time delay for mouseleave.
note that the first run must be without delay
var initTimeout = 1000;
var initTimeoutPlus = 1400;
var firstrun = true;
circleTitle.mouseover( function() {
if (firstrun) {
initTimeoutPlus = 0;
firstrun = false;
} else initTimeoutPlus = 1400;
setTimeout(function() {
circle.removeClass('hidden');
circle.addClass('animated fadeIn');
setTimeout( function() {
circle.addClass('pulse animate-infinite');
circle.removeClass('fadeIn');
}, initTimeout);
}, initTimeoutPlus);
});
Probably if you just add a key on mouseover, and toggle it after mouseleave, and before you trigger any mouseleave timeout events, check the key, if it is set, ignore, else go ahead and execute mouseleave
this way if the key is "on" it means a mouse over occurred, if it was off, it means the mouseleave occurred and it is still occurring
var key = false;
circleTitle.mouseover( function() {
key = true;
circle.removeClass('hidden');
circle.addClass('animated fadeIn');
setTimeout( function() {
circle.addClass('pulse animate-infinite');
circle.removeClass('fadeIn');
}, initTimeout);
});
circleTitle.mouseleave( function() {
key = false;
setTimeout( function() {
if (!key){
circle.stop().removeClass('pulse animate-infinite visibility');
circle.addClass('fadeOut');
}
}, initTimeout);
setTimeout( function() {
if (!key){
circle.removeClass('fadeOut');
circle.addClass('hidden');
}
}, 1400);
});
HTML Code:
<div id="slick-slidetoggle">wxyz</div>
<div id="slickbox" >abcd</div>
JavaScript:
var hoverVariable=false;
var hoverVariable2=false;
$('#slickbox').hide();
$('#slick-slidetoggle').mouseover(function() {
hoverVariable2=true;
$('#slickbox').slideToggle(600);
return false;
})
$('#slick-slidetoggle').mouseleave(function() {
hoverVariable2=false;
setTimeout(function (){
if(!hoverVariable && !hoverVariable2){
$('#slickbox').slideToggle(600);
return false;}
}, 1000);
})
$('#slickbox').mouseleave(function() {
hoverVariable=false;
setTimeout(function (){
if(!hoverVariable && !hoverVariable2){
$('#slickbox').slideToggle(600);
return false;}
return false;
}, 1000);
})
$('#slickbox').mouseover(function() {
hoverVariable2=false;
hoverVariable=true;
})
CSS Code:
#slickbox {
background: black;
width:100px;
height: 135px;
display: none;
cursor:pointer;
color:white;
}
#slick-slidetoggle{
background: yellow;
width:100px;
height: 135px;
cursor:pointer;
color:black;
}
Now the desired behaviour is that when mouse is slide over yellow div("wxyz") black div("abcd") should slide down and if mouse is moved out of yellow without moving on to black div, the black div should hide after two seconds.
This is happening. If mouse is moved over black div immediately after moving out of yellow div the black div should not hide as long as the mouse is on the black div. This is also happening.
Next steps are bit difficult to explain but I'll try, when mouse is moved over yellow div and black div comes out then mouse is moved over black div and within two seconds if it moved out of it(black div) then the whole animation goes haywire. Its behaviour is reversed. But if the mouse is kept on black div for more than two seconds and then it is moved out then the whole script runs fine.
This is the link to explain better. http://jsfiddle.net/HAQyK/381/
Try replacing slideToggle() with the appropriate slideUp() and slideDown() calls. http://jsfiddle.net/tppiotrowski/HAQyK/386/
var hoverVariable = false;
var hoverVariable2 = false;
$('#slickbox').hide();
$('#slick-slidetoggle').mouseover(function() {
hoverVariable2 = true;
$('#slickbox').slideDown(600);
return false;
})
$('#slick-slidetoggle').mouseleave(function() {
hoverVariable2 = false;
setTimeout(function() {
if (!hoverVariable && !hoverVariable2) {
$('#slickbox').slideUp(600);
return false;
}
}, 1000);
})
$('#slickbox').mouseleave(function() {
hoverVariable = false;
setTimeout(function() {
if (!hoverVariable && !hoverVariable2) {
$('#slickbox').slideUp(600);
return false;
}
return false;
}, 1000);
})
$('#slickbox').mouseover(function() {
hoverVariable2 = false;
hoverVariable = true;
})
I re-coded a solution. Checkout the fiddle here
var hideB;
var $black = $('#slickbox');
var $yellow = $('#slick-slidetoggle');
function showBlack() {
if( hideB ) window.clearTimeout( hideB );
$black.stop( true, true );
$black.slideDown(600);
}
function hideBlack() {
hideB = setTimeout( function( ) {
$black.stop( true, true );
$black.slideUp( 600 ); }
, 1000 );
}
$black.hide();
$yellow.mouseenter(function() {
showBlack();
})
$yellow.mouseleave(function() {
hideBlack();
});
$black.mouseleave( function( ) {
hideBlack();
});
$black.mouseenter( function( ) {
showBlack();
});
Your problem seems to be that the slideToggle in firing twice in quick succession because of your duplicate timeout functions. The cleanest way to deal with timeouts or intervals is to store them in a variable to give you the control of removing them when not needed:
// Defined in global scope
var timer;
$('#slick-slidetoggle').mouseleave(function() {
hoverVariable2=false;
// Timer set as function
timer = setTimeout(function (){
if(!hoverVariable && !hoverVariable2){
$('#slickbox').slideToggle(600);
// Timer no longer need and so cleared
clearTimeout(timer);
return false;}
}, 1000);
});
EDIT: Neglected to add the slideUp/slideDown instead of Toggle as per the correct answer above. See the updated jsFiddle which is now correct: http://jsfiddle.net/HAQyK/390/
Another way you could approach your script is to use jQuerys delay funciton and the stop(); method for animation. Wrap the divs in a container and you've got a much simpler block of code:
$('#slick-container').mouseenter(function() {
$('#slickbox').stop().slideDown(600);
}).mouseleave(function(){
$('#slickbox').stop().delay(1000).slideUp(600);
});
Check it out here: http://jsfiddle.net/HAQyK/387/
Normally, I’d set the interval to a variable and then clear it like var the_int = setInterval(); clearInterval(the_int); but for my code to work I put it in an anonymous function:
function intervalTrigger() {
setInterval(function() {
if (timedCount >= markers.length) {
timedCount = 0;
}
google.maps.event.trigger(markers[timedCount], "click");
timedCount++;
}, 5000);
};
intervalTrigger();
How do I clear this? I gave it a shot and tried var test = intervalTrigger(); clearInterval(test); to be sure, but that didn’t work.
Basically, I need this to stop triggering once my Google Map is clicked, e.g.
google.maps.event.addListener(map, "click", function() {
//stop timer
});
The setInterval method returns a handle that you can use to clear the interval. If you want the function to return it, you just return the result of the method call:
function intervalTrigger() {
return window.setInterval( function() {
if (timedCount >= markers.length) {
timedCount = 0;
}
google.maps.event.trigger(markers[timedCount], "click");
timedCount++;
}, 5000 );
};
var id = intervalTrigger();
Then to clear the interval:
window.clearInterval(id);
// Initiate set interval and assign it to intervalListener
var intervalListener = self.setInterval(function () {someProcess()}, 1000);
function someProcess() {
console.log('someProcess() has been called');
// If some condition is true clear the interval
if (stopIntervalIsTrue) {
window.clearInterval(intervalListener);
}
}
the_int=window.clearInterval(the_int);
Simplest way I could think of: add a class.
Simply add a class (on any element) and check inside the interval if it's there. This is more reliable, customisable and cross-language than any other way, I believe.
var i = 0;
this.setInterval(function() {
if(!$('#counter').hasClass('pauseInterval')) { //only run if it hasn't got this class 'pauseInterval'
console.log('Counting...');
$('#counter').html(i++); //just for explaining and showing
} else {
console.log('Stopped counting');
}
}, 500);
/* In this example, I'm adding a class on mouseover and remove it again on mouseleave. You can of course do pretty much whatever you like */
$('#counter').hover(function() { //mouse enter
$(this).addClass('pauseInterval');
},function() { //mouse leave
$(this).removeClass('pauseInterval');
}
);
/* Other example */
$('#pauseInterval').click(function() {
$('#counter').toggleClass('pauseInterval');
});
body {
background-color: #eee;
font-family: Calibri, Arial, sans-serif;
}
#counter {
width: 50%;
background: #ddd;
border: 2px solid #009afd;
border-radius: 5px;
padding: 5px;
text-align: center;
transition: .3s;
margin: 0 auto;
}
#counter.pauseInterval {
border-color: red;
}
<!-- you'll need jQuery for this. If you really want a vanilla version, ask -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="counter"> </p>
<button id="pauseInterval">Pause/unpause</button></p>