can't clear inverval called in function from other function - javascript

I can't figure out why this variable is becoming private to the function, even if i try to clear the interval from the console it can't find the variable, I must be doing something stupid. help please, code is below: I cut it down to the relevant parts.
function attack() {
$("#container").append("<div id='attacker' style='position:absolute; width: 128px; bottom:125px'> <img src='attacker.gif' ></div>");
$("#attacker").css("left", $( window ).width());
attackerCheck = setInterval( function() { moveNclear("#attacker", "attackerCheck"); }, 33 );
}
function moveNclear(objectName, intervalname) {
objectP = $(objectName).position();
$(objectName).css("left", (objectP.left - 10) + "px");
if(objectP.left < 0) {
console.log("done");
clearInterval(intervalname);
}
}

You can avoid having to pass the interval reference by making a suitable return from moveNclear(), and testing it back in the calling function.
Also, $("#attacker") (many times over) is horribly inefficient ... and avoidable.
Finally, don't forget to localise your variables with var. Don't use globals.
Try :
function attack() {
var $attacker = $("<div id='attacker' style='position:absolute; width:128px; bottom:125px'> <img src='attacker.gif' /></div>").appendTo("#container").css('left', $(window).width());
var attackerCheck = setInterval(function() {
if(moveNclear($attacker)) {
clearInterval(attackerCheck);
console.log("done");
}
}, 33);
}
function moveNclear($el) {
var pos = $el.position();
pos.left -= 10;
$el.css("left", pos.left + 'px');
return (pos.left <= 0);//true:animation complete; false:animation in progress
}

var attackerCheck = null;
function attack()
{
$("#container").append("<div id='attacker' style='position:absolute; width: 128px; bottom:125px'> <img src='attacker.gif' ></div>");
$("#attacker").css("left", $( window ).width());
attackerCheck = setInterval( function() { moveNclear("#attacker"); }, 33 );
}
function moveNclear(objectName)
{
objectP = $(objectName).position();
$(objectName).css("left", (objectP.left - 10) + "px");
if(objectP.left < 0)
{
console.log("done");
clearInterval(attackerCheck);
}
}

Related

JQuery: setInterval not working with each() method. Fault of "this" keyword or a bug in code? [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 5 years ago.
I wrote simple JQuery function to count from 0 to specified number in a range of divs with certain class name:
<div class="number">50%</div>
<div class="number">75%</div>
this is JQuery code:
$(document).ready(function () {
$(".number").each( function() {
var percent = $(this).text().replace("%","");
console.log("test " + percent);
function countToPercent(percent) {
var interval = setInterval(counter,25);
var n = 0;
function counter() {
if (n >= percent) {
clearInterval(interval);
}
else {
n += 1;
// console.log(n);
$(this).text(n + "%");
}
}
}
countToPercent(percent);
})
});
unfortunetly it isn't working now - there is probably a problem within this is line:
$(this).text(n + "%")
i debbuged it using console and commented console.log(n) works - it counts from 0 to specified number. so it has to be problem with selecting correct DOM element.
is it forbidden to use setInterval() with .each() method or with "this" keyword? or is there some bug in the code?
Your problem is that this keyword does not exist in context of interval scope.
Wrap interval stuff to another scope by providing injecting variables and call it:
$(function () {
$(".number").each( function() {
var $this = $(this);
var percentage = parseFloat($this.text().replace("%",""));
console.log("test " + percentage);
(function(percentage, jqObject) {
var n = 0;
var interval = setInterval(function() {
if(n >= percentage) return clearInterval(interval);
n++;
jqObject.text(n + '%');
}, 25);
})(percentage, $this);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="number">50%</div>
<div class="number">75%</div>
or You may have external function that does same thing:
$(function () {
function animatePercentageProgress(percentage, jqObject) {
var n = 0;
var interval = setInterval(function() {
if(n >= percentage) return clearInterval(interval);
n++;
jqObject.text(n + '%');
}, 25);
}
$(".number").each( function() {
var $this = $(this);
var percentage = parseFloat($this.text().replace("%",""));
console.log("test " + percentage);
animatePercentageProgress(percentage, $this);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="number">50%</div>
<div class="number">75%</div>
or more "functional way":
$(function () {
function animatePercentageProgress(idx, jqObjectPointer) {
var jqObject = $(jqObjectPointer);
var percentage = parseFloat(jqObject.text().replace("%",""));
var n = 0;
var interval = setInterval(function() {
if(n >= percentage) return clearInterval(interval);
n++;
jqObject.text(n + '%');
}, 25);
}
$(".number").each(animatePercentageProgress);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="number">50%</div>
<div class="number">75%</div>
You need to save a context to use it in setInterval
$(document).ready(function () {
$(".number").each( function() {
var percent = $(this).text().replace("%","");
console.log("test " + percent);
var self = this; // fixing context;
function countToPercent(percent) {
var n = 0;
var interval = setInterval(() => {
if (n >= percent) {
clearInterval(interval);
}
else {
n += 1;
// console.log(n);
$(self).text(n + "%");
}
},25);
}
countToPercent(percent);
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="number">50%</div>
<div class="number">75%</div>

javascript: how to exit a loop by clicking a button

I did some searching and I'm not even sure if what I want to do is good javascript practice.
I have a while loop that I would like to exit from early if a stop button is clicked.
$( "#go" ).click(function() {
var gotime = 1;
while (gotime < 100) {
for(i = 0; i < 2; i++){
var divName = "floatName" + i;
console.log(divName);
$( "#" + divName ).animate({
left: Math.random()*500 + "px",
top: Math.random()*500 + "px"
}, 500, function() {
// Animation complete.
});
};
gotime += 1;
$( "stop" ).click(function() {
gotime = 101;
});
};
});
This doesn't work though. I originally had an endless loop (not incrementing gotime).
http://jsfiddle.net/cvoxe465/
Actually it stops if you wait for some time. The problem is you execute animation very often and $.animate have to queue it. There is $.stop method that allow you to stop the currently-running animation. DEMO
$( "#stop" ).click(function() {
gotime = 101;
$('#floatName0, #floatName1').stop(true, true);
});
EDIT:
Note that in the code that you provided there is mistake. Instead $("stop") you need to use $("#stop").
You may use setInterval:
Js:
var interval;
$("#go").click(function () {
var gotime = 1;
interval = setInterval(function () {
for (i = 0; i < 2; i++) {
var divName = "floatName" + i;
console.log(divName);
$("#" + divName).css({
left: Math.random() * 500 + "px",
top: Math.random() * 500 + "px"
});
};
gotime += 1;
if (gotime > 100) {
clearInterval(interval);
}
}, 500)
});
$("#stop").on('click', function () {
clearInterval(interval);
});
css:
#randomFloat {
color: red;
}
#floatName1, #floatName0 {
transition : 0.5s left, 0.5s top;
}
Fiddle
animate doesn't block the loop. The animations are stacked up and then executed, but the loop finishes a lot earlier. Here is something that works:
var loopAllowed = false;
$('#go').click(function(){
loopAllowed = true;
var max = 2;
var loop = function(){
for(var i = 0; i < max; i++){
var divName = "floatName" + i;
$( "#" + divName ).animate({
left: Math.random()*500 + "px",
top: Math.random()*500 + "px"
}, 500, i === max - 1 && loopAllowed ? loop : undefined);
}
};
loop();
});
$('#stop').click(function(){
loopAllowed = false;
});
JSFiddle. We manually call the loop function after the animation has ended (by passing it as the callback function). If loopAllowed is false (e.g. set to false by clicking #stop), then it won't be passed as the callback function and the looping stops.

Pure JavaScript fade in function

Hi friends i want to fade in a div when i click on another div and for that i am using following code. Code1 works fine but i require to use the Code2.
I know there is jQuery but i require to do this in JavaScript
Can you guide me that what kind of mistake i am doing or what i need change...
Code1 --- Works Fine
function starter() { fin(); }
function fin()
{
for (i = 0; i <= 1; i += 0.01)
{
i=Math.round(i*100)/100;
setTimeout("seto(" + i + ")", i * 1000);
}
}
function seto(opa)
{
var ele = document.getElementById("div1");
ele.style.opacity = opa;
}
Code2 --- Does not work
function starter()
{
var ele = document.getElementById("div1");
fin(ele);
}
function fin(ele)
{
for (i = 0; i <= 1; i += 0.01)
{
i=Math.round(i*100)/100;
setTimeout("seto(" + ele + "," + i + ")", i * 1000);
}
}
function seto(ele,opa)
{
ele.style.opacity = opa;
}
Based on this site
EDIT-1
Added the functionality so that user can specify the animation duration(#Marzian comment)
You can try this:
function fadeIn(el, time) {
el.style.opacity = 0;
var last = +new Date();
var tick = function() {
el.style.opacity = +el.style.opacity + (new Date() - last) / time;
last = +new Date();
if (+el.style.opacity < 1) {
(window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16);
}
};
tick();
}
var el = document.getElementById("div1");
fadeIn(el, 3000); //first argument is the element and second the animation duration in ms
DEMO
Update:
It seems that people enjoy my minimalistic and elegant approach, Updated for 2022:
No need for complex mechanisms. Just use CSS, which has it out of the box and has better performance overall.
Basically you achieve it with CSS by setting a transition for the opacity. In JavaScript that would be:
const div = document.querySelector('#my-div');
div.style.transition='opacity 1s';
and as a trigger you just set opacity to 0:
div.style.opacity=0;
This will create a 1 second fade out effect and you can use the trigger anywhere. The inverse can also be done to achieve a fade in effect.
Here's a working example:
const div = document.querySelector('#my-div');
div.style.transition='opacity 1s';
// set opacity to 0 -> fade out
setInterval(() => div.style.opacity=0, 1000);
// set opacity to 1 -> fade in
setInterval(() => div.style.opacity=1, 2000);
#my-div { background-color:#FF0000; width:100%; height:100%; padding: 10px; color: #FFF; }
<div id="my-div">Hello!</div>
Seems like your attempting to convert your element, to a string. Try this instead
function starter()
{
var ele = document.getElementById("div1");
fin(ele);
}
function fin(ele)
{
for (i = 0; i <= 1; i += 0.01)
{
i=Math.round(i*100)/100;
setTimeout(function() { setto(ele,i); }, i * 1000);
}
}
function seto(ele,opa)
{
ele.style.opacity = opa;
}
What happens here is, that i call a anonnymous function when the timer hits, and from that function, execute my functioncall to setto.
Hope it helps.
Jonas
The problem here is you are using the pass-a-string method of using setTimeout. Which is basically just a hidden eval.
It's worth noting that this is a bad practice, slow performer, and security risk.
(see questions such as this: setTimeout() with string or (anonymous) function reference? speedwise)
The reason this is causing your problem is because "seto(" + ele + "," + i + ")" is going to evaluate to "seto('[object HTMLDivElement]', 1)". You really want to pass reference to the ele object -- but the value's being cast to a string when you tried concatenating an object onto a string. You can get around this by using the pass-a-function method of using setTImeout.
setTimeout(function() { seto(ele, i); }, i * 1000);
I believe making this change will make your Code2 behavior equivalent to Code1.
Below are the complete answers to my question
ANS1 --- DEMO
function fin() {
var i = 0;
var el = document.getElementById("div1");
fadeIn(el,i);
}
function fadeIn(el,i) {
i = i + 0.01;
seto(el,i);
if (i<1){setTimeout(function(){fadeIn(el,i);}, 10);}
}
function seto(el,i) {
el.style.opacity = i;
}
ANS2 --- DEMO
function fin(){
var i = 0;
var el = document.getElementById("div1");
fadeIn(el,i);
}
function fadeIn(el,i) {
var go = function(i) {
setTimeout( function(){ seto(el,i); } , i * 1000);
};
for ( i = 0 ; i<=1 ; i = i + 0.01) go(i);
}
function seto(el,i)
{
el.style.opacity = i;
}
My version
function fadeIn($element){
$element.style.display="block";
$element.style.opacity=0;
recurseWithDelayUp($element,0,1);
}
function fadeOut($element){
$element.style.display="block";
$element.style.opacity=1;
recurseWithDelayDown($element,1,0);
}
function recurseWithDelayDown($element,startFrom,stopAt){
window.setTimeout(function(){
if(startFrom > stopAt ){
startFrom=startFrom - 0.1;
recurseWithDelayDown($element,startFrom,stopAt)
$element.style.opacity=startFrom;
}else{
$element.style.display="none"
}
},30);
}
function recurseWithDelayUp($element,startFrom,stopAt){
window.setTimeout(function(){
if(startFrom < stopAt ){
startFrom=startFrom + 0.1;
recurseWithDelayUp($element,startFrom,stopAt)
$element.style.opacity=startFrom;
}else{
$element.style.display="block"
}
},30);
}
function hide(fn){
var hideEle = document.getElementById('myElement');
hideEle.style.opacity = 1;
var fadeEffect = setInterval(function() {
if (hideEle.style.opacity < 0.1)
{
hideEle.style.display='none';
fn();
clearInterval(fadeEffect);
}
else
{
hideEle.style.opacity -= 0.1;
}
}, 20);
}
function show(){
var showEle = document.getElementById('myElement');
showEle.style.opacity = 0;
showEle.style.display='block';
var i = 0;
fadeIn(showEle,i);
function fadeIn(showEle,i) {
i = i + 0.05;
seto(showEle,i);
if (i<1){setTimeout(function(){fadeIn(showEle,i);}, 25);}
}
function seto(el,i)
{
el.style.opacity = i;
}
}
hide(show);
I just improved on laaposto's answer to include a callback.
I also added a fade_out function.
It could be made more efficient, but it works great for what i'm doing.
Look at laaposto's answer for implementation instructions.
You can replace the JS in his fiddle with mine and see the example.
Thanks laaposto!
This really helped out for my project that requires zero dependencies.
let el = document.getElementById( "div1" );
function fade_in( element, duration, callback = '' ) {
element.style.opacity = 0;
let last = +new Date();
let tick = function() {
element.style.opacity = +element.style.opacity + ( new Date() - last ) / duration;
last = +new Date();
if ( +element.style.opacity < 1 )
( window.requestAnimationFrame && requestAnimationFrame( tick ) ) || setTimeout( tick, 16 );
else if ( callback !== '' )
callback();
};
tick();
}
function fade_out( element, duration, callback = '' ) {
element.style.opacity = 1;
let last = +new Date();
let tick = function() {
element.style.opacity = +element.style.opacity - ( new Date() - last ) / duration;
last = +new Date();
if ( +element.style.opacity > 0 )
( window.requestAnimationFrame && requestAnimationFrame( tick ) ) || setTimeout( tick, 16 );
else if ( callback !== '' )
callback();
};
tick();
}
fade_out( el, 3000, function(){ fade_in( el, 3000 ) } );
Cheers!

Need to add a delay on mouse-out to this code

This is some javascript for a drupal 6.x module called "Views Popup".
https://drupal.org/project/views_popup
I can't seem to set a delay on the popup when the mouse moves off the link that triggers the popup. I have the title, teaser text and a more link in the popup and users need to be able to move the mouse off the link (image) in order to click on the "read more" link. I've tried adjusting all the settings in the code below, but none seem to relate to this. I'm not a coder, but I think something needs to be added to make this work. Any suggestions would be greatly appreciated.
Here's the code:
var popup_time = 0;
var popup_elem = 0;
var popup_show_timer = 0;
var popup_reset_timer = 0;
$(function() {
popup_reset();
$(".views-popup").appendTo("body");
});
Drupal.behaviors.viewsPopup = function(context) {
$(".views-popup-row").mouseover(function() {
popup_show(this);
})
.mouseout(function() {
popup_hide(this);
})
.mousemove(function(e) {
popup_move(this,e);
});
}
function popup_move(me,evt){
var e, top, left;
if (Drupal.settings.views_popup.follow_mouse){
left = evt.pageX + 15;
top = evt.pageY;
$("#views-popup-" + $(me).attr("id")).css({
left: left + 'px',
top: top + 'px'
});
}
}
function popup_show(me) {
var p, e, top, left, pos ;
var x = $(me).attr("id");
e = $("#views-popup-" + $(me).attr("id"));
if (e == popup_elem) {
return ; // already handled
}
if (! Drupal.settings.views_popup.follow_mouse){
pos = $(me).offset();
left = 20 + pos.left - $(document).scrollLeft();
top = 2 + pos.top + $(me).outerHeight() - $(document).scrollTop();
$(e).css({
left: left + 'px',
top: top + 'px'
});
}
popup_clear_show_timer();
if (popup_elem) {
popup_elem.hide();
popup_time = 500 ;
}
popup_elem = e;
if ( popup_time == 0 ) {
popup_show_now();
} else {
popup_show_timer = setTimeout("popup_show_now();",popup_time);
}
}
function popup_show_now() {
popup_show_timer = 0 ;
if(popup_elem) {
popup_elem.show();
clearTimeout(popup_reset_timer);
popup_time = 0;
}
}
function popup_clear_show_timer(){
if (popup_show_timer) {
clearTimeout(popup_show_timer);
popup_show_timer = 0;
}
}
function popup_hide(me) {
e = $("#views-popup-" + $(me).attr("id"));
popup_clear_show_timer();
clearTimeout(popup_reset_timer);
e.hide();
if(e == popup_elem) {
popup_elem = 2;
}
popup_reset_timer = setTimeout('popup_reset()',Drupal.settings.views_popup.reset_time);
}
function popup_reset(){
popup_time = Drupal.settings.views_popup.popup_delay;
}
So, assuming the above code works how you want -- and that you want to set a delay for the popup to hide, what you can do is call javascript's setTimeout(function, delay) function, which initiates a callback after delay milliseconds.
function popup_hide(me) {
e = $("#views-popup-" + $(me).attr("id"));
popup_clear_show_timer();
clearTimeout(popup_reset_timer);
var delay = 1000; // ms
setTimeout(e.hide, delay); // <------- here
if(e == popup_elem) {
popup_elem = 2;
}
popup_reset_timer = setTimeout('popup_reset()',Drupal.settings.views_popup.reset_time);
}
This will call e.hide (the function) after 1 second has passed.

jQuery rearrange DOM elements

I'm a little confused why this is not working how it's supposed to.
I have a list of <div>s that wrap around based on the size of the page. But clicking the div it .animate() the width of the clicked div and animates the divs after it closer together and stacks them.
This all works except the last stacked div, despite having plenty of room still gets knocked down to the next row.
please see my code on jsfiddle:
http://jsfiddle.net/ZHYRq/2/
$.each($('.employee-box'), function(i, el) {
$(el).addClass("top-" + (Math.round($(el).offset().top)));
return $(el).css('position', 'relative').attr('data-left', Math.round($(el).offset().left));
});
$('.employee-image').hover(function(e) {
var $employee;
$employee = $(e.target).parents('.employee-box');
if (e.type === 'mouseenter') {
return $($employee.find('a.bio')).addClass('highlight');
} else {
return $($employee.find('a.bio')).removeClass('highlight');
}
});
$('.employee-image, a.bio').click(function(e) {
var $employee, is_expanded, speed;
speed = 150;
$employee = $(e.target).parents('.employee-box');
is_expanded = $employee.hasClass('bio-expanded');
if ($('.bio-expanded').length > 0) {
$.when(collapse_previous_bio(speed)).then(function() {
if (!is_expanded) {
return expand_bio_box($employee, speed);
}
});
} else {
expand_bio_box($employee, speed);
}
return false;
});
var collapse_previous_bio = function(speed) {
var klass;
klass = "." + $('.bio-expanded').attr('class').match(/top-\d{1,5}/)[0];
$('.bio-expanded .bio-block').fadeOut(speed, function() {
$('.bio-expanded').animate({
width: "185px"
}, speed);
$(klass).animate({
left: '0px'
}, speed);
$('.bio-expanded').removeClass('bio-expanded');
});
};
var expand_bio_box = function($employee, speed) {
var curr_left, klass;
klass = "." + $employee.attr('class').match(/top-\d{1,5}/)[0];
curr_left = parseInt($employee.data('left'));
// comment out the $.when block and un-comment out the collapse_others() to see the other elements collapse as they should
$.when(collapse_others(klass, curr_left)).then(function() {
$employee.animate({
width: "392px"
}, speed, function() {
$employee.find('.bio-block').fadeIn(speed);
$employee.addClass('bio-expanded');
});
});
// collapse_others(klass, curr_left)
};
var collapse_others = function(klass, curr_left) {
var left_pos;
left_pos = 0;
$.each($(klass), function(i, el) {
var el_left;
el_left = parseInt($(el).data('left'));
$(el).css({
zIndex: 100 - i
});
if (el_left > curr_left) {
$(el).animate({
left: "-" + left_pos + "px"
}, 100);
left_pos += 100;
}
});
};
I'm not sure what is wrong here. Any thoughts?

Categories

Resources