Central JS Timer - javascript

Looking at Secrets of the JavaScript Ninja, I took this code for a "Central Timer":
var timers = {
timerID: 0,
timers: [],
add: function(fn) {
this.timers.push(fn);
},
start: function() {
if(this.timerID) return;
(function runNext() {
if(timers.timers.length > 0) {
for (var i = 0; i < timers.length; i++) {
if(timers.timers[i]() === false) {
timers.timers.splice(i,1);
i--;
}
}
console.log("setting timeout.");
timers.timerID = setTimeout(runNext, 0);
}
})();
},
stop: function() {
clearTimeout(this.timerID);
this.timerID = 0;
}
};
Then, test it out.
var box = document.getElementById("box"), x = 0, y = 20;
timers.add(function() {
box.style.left = x + "px";
log.console("x:", x);
if(++x > 50) return false;
});
timers.add(function() {
box.style.top = y + "px";
y += 2;
log.console("y:", y);
if (++y > 120) return false;
});
console.log("starting timer.");
But, looking at my console, I see setting timeout scrolling endlessly without any increment to x or y.
What's going on here?
JsFiddle
EDIT Page # - 210/394.
Note - it's possible I made a copy/paste mistake - not blaming.

timers.length is undefined in the for loop in runNext, so the code does not actually iterate over the intended array. Either you transcribed it incorrectly or the code in the book has a bug. Regardless, this is the correct loop:
// ↓↓↓↓↓↓↓
for (var i = 0; i < timers.timers.length; i++) {
if(timers.timers[i]() === false) {
timers.timers.splice(i,1);
i--;
}
}
Fiddle

Related

This Javascript function keeps looping, how can i make it run once?

The code is meant to animated some text in a typing fashion. I want it to run once, but it keeps looping through the area of sentences. How would i go about stopping it looping through. The top of the code gathers the value of a input and puts into the array, this all works fine. It is just the looping i am having issues with.
var yourWord = document.getElementById("myText").value;
var yourtext = "I took the word " + yourWord + "...";
var infomation = [yourtext,
'I looked at the most relevant news article relating to it...',
'And created this piece of art from the words in the article! '
],
part,
i = 0,
offset = 0,
pollyLen = Polly.length,
forwards = true,
skip_count = 0,
skip_delay = 5,
speed = 100;
var wordflick = function () {
setInterval(function () {
if (forwards) {
if (offset >= infomation[i].length) {
++skip_count;
if (skip_count == skip_delay) {
forwards = false;
skip_count = 0;
}
}
} else {
if (offset == 0) {
forwards = true;
i++;
offset = 0;
if (i >= pollyLen) {
i = 0;
}
}
}
part = infomation[i].substr(0, offset);
if (skip_count == 0) {
if (forwards) {
offset++;
} else {
offset--;
}
}
$('#pollyInfo').text(part);
}, speed);
};
$(document).ready(function () {
wordflick();
});
Modify the line:
setInterval(function () {
into:
var interval = setInterval(function () {
and then clear the interval where you are setting i=0;
if (i >= pollyLen) {
i = 0;
}
to:
if (i >= pollyLen) {
i = 0;
clearInterval(interval);
}
This should do the job!

JavaScript function dosen't work correct

I have a function that checking board is there a searching element.
First script creating a board with different elements. Element in the square next to board is element that user has to find on the board and click them.
User has to click (as quick as possible) all searching elements. After click on element function checking a board, is there more elements. If yes then nothing happen and user has to click another one. If on board there isn’t searching elements then function display a time and new board create.
But some reason function works correct only first time after page load. Latter function ignore more then one element on board or see element that doesn’t exist on board any more.
Can you tell me what is wrong.
Part of code bellow and there is a link to testing page.
http://doomini2.linuxpl.info/font/
Thank you
function secondStage() {
createBoxes(59);
var usingSet = [];
var i = 0;
var boxList = document.querySelectorAll("#board > div");
createSet(usingSet, 20, shapes);
(function paint() {
if (i <= 59) {
var curentBox = boxList[i];
curentBox.className = usingSet[draw(20)];
curentBox.style.color = colors[draw(colors.length - 5)];
timeStop = setTimeout(paint, 50);
i++;
} else {
var findShape = boxList[draw(59)];
toFind.className = findShape.className;
toFind.style.color = findShape.style.color;
findBoxes(boxList);
clearTimeout(timeStop);
}
})();
}
//function checks boxes to find a proper shape
function findBoxes(boxList) {
startTime = Date.now();
board.addEventListener("mousedown", function (e) {
if ((e.target.className === toFind.className)) {
e.target.className = "correct";
e.target.innerHTML = "OK";
checkBoard();
} else if (e.target.id === "board" || e.target.className === "correct") {
} else {
e.target.className = "false";
e.target.innerHTML = "NO";
}
}, false);
function checkBoard() {
var condition = false;
console.log(condition);
for (var x = 0; x < boxList.length; x++) {
if ((boxList[x].className === toFind.className)) {
condition = true;
console.log(condition);
}
}
if (condition === false) {
var clickTime = Date.now();
var timeResult = parseFloat(((clickTime - startTime) / 1000).toFixed(3));
lastResult.innerHTML = timeResult + "s";
secondResult[secondResult.length] = timeResult;
console.log(secondResult);
displayResult(secondStage);
}
}
}
//function displaig results after every single round
function displayResult(stage) {
cover.className = "";
TweenMax.to("#lastResultDiv", 1, {ease: Back.easeOut, right: (winWidth / 4), });
TweenMax.to("#go", 1, {ease: Back.easeOut, top: (winWidth / 3), onComplete: function () {
goButton.addEventListener("click", function () {
clear();
}, false);
}});
//clear board and return to play
function clear() {
TweenMax.to("#lastResultDiv", 1, {ease: Back.easeIn, right: winWidth, });
TweenMax.to("#go", 1, {ease: Back.easeOut, top: -100, onComplete: function () {
cover.className = "hide";
lastResultDiv.style.right = "-592px";
toFind.className = "";
board.innerHTML = "";
if (firstStageRound === 10) {
secondStage();
} else if (secondStageRound === 5) {
thirdStage();
} else {
stage();
}
}});
}
}
Not Load this File http://cdnjs.cloudflare.com/ajax/libs/gsap/1.17.0/TweenMax.min.js
try to local path

Looking for thoughts on improvement of my javascript (jquery) code. Recursive function

I have made this code that makes some visual "tiles" that fades in and out.
But at the moment I'm having a little performance problem.
Though most browers are running the code okay (especially firefox), some like safari have problems after a while (a while = like 15 seconds).
I think its due to my recursive function (the function named changeopacity that calls itself forever on a delay)? or is it?
But anyways the problem is that this code is really heavy for most browsers. Is there, or more how can I make this code perform any better? any thoughts? (code examples would be nice) thanks :-)
The actual code:
$(document).ready(function () {
var aniduration = 2000;
var tilesize = 40;
createtable(tilesize);
$(".tile").each(function (index, domEle) {
var randomdelay = Math.floor(Math.random() * 3000);
setTimeout(function () {
changeopacity(aniduration, domEle);
}, randomdelay);
});
$("td").click(function () {
clickanimation(this, 9);
});
$("td").mouseenter(function () {
var element = $(this).find("div");
$(element).clearQueue().stop();
$(element).animate({opacity: "0.6"}, 800);
});
$("td").css("width", tilesize + "px").css("height", tilesize + "px");
});
function createtable(tilesize) {
var winwidth = $(window).width();
var winheight = $(window).height();
var horztiles = winwidth / tilesize;
var verttiles = winheight / tilesize;
for (var y = 0; y < verttiles; y++)
{
var id = "y" + y;
$("#tbl").append("<tr id='" + id + "'></tr>");
for (var x = 0; x < horztiles; x++)
{
$("#" + id).append("<td><div class='tile' style='opacity: 0; width: " + tilesize + "px; height: " + tilesize + "px;'></div></td>");
}
}
}
function changeopacity(duration, element){
var randomnum = Math.floor(Math.random() * 13);
var randomopacity = Math.floor(Math.random() * 7);
var randomdelay = Math.floor(Math.random() * 1000);
if ($(element).css("opacity") < 0.3)
{
if (randomnum != 4)
{
if ($(element).css("opacity") != 0)
animation(element, 0, duration, randomdelay);
}
else
{
animation(element, randomopacity, duration, randomdelay);
}
}
else
{
animation(element, randomopacity, duration, randomdelay);
}
setTimeout(function () {
return changeopacity(duration, element);
}, duration + randomdelay);
}
function animation(element, randomopacity, duration, randomdelay){
$(element).clearQueue().stop().delay(randomdelay).animate({opacity: "0." + randomopacity}, duration);
}
function clickanimation(column, opacitylevel) {
var element = $(column).find("div");
$(element).clearQueue().stop();
$(element).animate({"background-color": "white"}, 200);
$(element).animate({opacity: "0." + opacitylevel}, 200);
$(element).delay(200).animate({opacity: "0.0"}, 500);
//$(element).delay(600).animate({"background-color": "black"}, 500);
}
The number one issue is that you are creating one setTimeout for every single cell on your page. The only browser capable of handling that is Internet Explorer, and then it fails due to the many CSS changes causing slow redraws.
I would strongly suggest programming your own event scheduler. Something like this, which I used in a university project:
var timer = {
length: 0,
stack: {},
timer: null,
id: 0,
add: function(f,d) {
timer.id++;
timer.stack[timer.id] = {f: f, d: d, r: 0};
timer.length++;
if( timer.timer == null) timer.timer = setInterval(timer.run,50);
return timer.id;
},
addInterval: function(f,d) {
timer.id++;
timer.stack[timer.id] = {f: f, d: d, r: d};
timer.length++;
if( timer.timer == null) timer.timer = setInterval(timer.run,50);
return timer.id;
},
remove: function(id) {
if( id && timer.stack[id]) {
delete timer.stack[id];
timer.length--;
if( timer.length == 0) {
clearInterval(timer.timer);
timer.timer = null;
}
}
},
run: function() {
var x;
for( x in timer.stack) {
if( !timer.stack.hasOwnProperty(x)) continue;
timer.stack[x].d -= 50;
if( timer.stack[x].d <= 0) {
timer.stack[x].f();
if( timer.stack[x]) {
if( timer.stack[x].r == 0)
timer.remove(x);
else
timer.stack[x].d = timer.stack[x].r;
}
}
}
}
};
Then, instead of using setTimeout, call timer.add with the same arguments. Similarly, instead of setInterval you can call timer.addInterval.
This will allow you to have as many timers as you like, and they will all run off a single setInterval, causing much less issues for the browser.
Nice animation :-) However, I found some bugs and possible improvements:
Your table is not rebuilt on window resizes. Not sure if bug or feature :-)
Use delegated events. You have a lot of elements, and every event handler is costly. Sadly, this won't work for the non-bubbling mouseenter event.
It would be nice if you would not use inline styles for with and height - those don't change. For the divs, they are superflouos anyway.
I can't see a reason for all those elements to have ids. The html-string building might be more concise.
Cache the elements!!! You are using the jQuery constructor on nearly every variable, building a new instance. Just reuse them!
Your changeopacity function looks a bit odd. If the opacity is lower than 0.3, there is 1-in-13-chance to animate to zero? That might be expressed more stringent. You also might cache the opacity to a variable instead of reading it from the dom each time.
There is no reason to pass the duration and other constants as arguments, they do never change and can be used from the global scope.
Instead of using the timeout, you should use the complete callback of the animate method. Timeouts are never accurate, they may even interfere here causing (minor) problems.
var duration = 2000,
tilesize = 40,
clickopacity = 0.9;
$(document).ready(function () {
filltable($("#tbl"), tilesize)
.on("click", "td", clickanimation);
$(".tile").each(function() {
changeopacity($(this));
});
$("#tbl div").mouseenter(function () {
$(this).clearQueue()
.stop()
.animate({opacity: "0.6"}, 800);
});
});
function filltable(tbl, tilesize) {
var win = $(window).width();
var horztiles = win.width() / tilesize;
var verttiles = win.height() / tilesize;
for (var y = 0; y < verttiles; y++) {
var tr = "<tr>";
for (var x = 0; x < horztiles; x++)
tr += "<td style='width:"+tilesize+"px;height:"+tilesize+"px;'><div class='tile' style='opacity:0;'></div></td>");
tbl.append(tr+"</tr>");
}
return tbl;
}
function changeopacity(element) {
var random = Math.floor(Math.random() * 13);
var opacity = Math.floor(Math.random() * 7);
var delay = Math.floor(Math.random() * 1000);
if (element.css("opacity") < 0.3 && random != 4)
opacity = 0;
element.clearQueue().stop().delay(delay).animate({
opacity: "0." + opacity
}, duration, function() {
changeopacity(element);
});
}
function clickanimation() {
$(this.firstChild)
.clearQueue()
.stop()
.animate({"background-color": "white"}, 200)
.animate({opacity: "0." + clickopacity}, 200)
.delay(200).animate({opacity: "0.0"}, 500);
//.delay(600)
//.animate({"background-color": "black"}, 500);
}

javascript countdown with showing milliseconds

I want to do a count down and want to show like format as Minutes:Seconds:Milliseconds. I made a count down with jquery plug-in countdown but it shows just Minutes:Seconds format.
Is there any way to make it right?
Many Thanks!
Hi guys I have developed a code for my self use the following code
counter for 20 seconds
var _STOP =0;
var value=1999;
function settimer()
{
var svalue = value.toString();
if(svalue.length == 3)
svalue = '0'+svalue;
else if(svalue.length == 2)
svalue = '00'+svalue;
else if(svalue.length == 1)
svalue = '000'+svalue;
else if(value == 0)
svalue = '0000';
document.getElementById('cn1').innerHTML = svalue[0];
document.getElementById('cn2').innerHTML = svalue[1];
document.getElementById('cn3').innerHTML = svalue[2];
document.getElementById('cn4').innerHTML = svalue[3];
value--;
if (_STOP==0 && value>=0) setTimeout("settimer();", 10);
}
setTimeout("settimer()", 10);
Try this: http://jsfiddle.net/aamir/TaHtz/76/
HTML:
<div id="timer"></div>
​
JS:
var el = document.getElementById('timer');
var milliSecondsTime = 10000;
var timer;
el.innerHTML = milliSecondsTime/1000;
timer = setInterval(function(){
milliSecondsTime = milliSecondsTime - 1000;
if(milliSecondsTime/1000 == 0) {
clearTimeout(timer);
el.innerHTML = 'BOOOOM';
}
else {
el.innerHTML = milliSecondsTime/1000;
}
},1000);
​
If you want to make your own timer.
read this earlier question
How to create a JQuery Clock / Timer
Try setting the format parameter - http://keith-wood.name/countdownRef.html#format
On further reading, this plugin doesn't do milliseconds. At this point, you either have to edit the actual plugin code or find a new plugin.
I completely agree with #Matt Ball's comment.It may also cause the browser to crash.
Why don't you try this solution instead
jQuery 1 minute countdown with milliseconds and callback
I did it like this (generic counter from N to X (X > N)):
var dynamicCounterAddNewValue = 20;
var currentDynamicUpdater;
function dynamicCounterForValueForControlUpdater(_updaterData) {
_updaterData.from += dynamicCounterAddNewValue;
if (_updaterData.from > _updaterData.to) {
_updaterData.from = _updaterData.to;
}
_updaterData.c.html(_updaterData.from.toString());
if (_updaterData.from < _updaterData.to) {
currentDynamicUpdater = setTimeout(
dynamicCounterForValueForControlUpdater,
10,
{
c: _updaterData.c,
from: _updaterData.from,
to: _updaterData.to
}
);
}
else {
clearTimeout(currentDynamicUpdater);
}
return;
}
// _c -> jQuery object (div,span)
// _from -> starting number
// _to -> ending number
function dynamicCounterForValueForControl(_c, _from, _to) {
clearTimeout(currentDynamicUpdater);
dynamicCounterForValueForControlUpdater(
{
c: _c,
from: _from,
to: _to
}
);
return;
}
EDIT: Updated version (more flexible - for N elements one after another):
(input element is Array of elements for making them dynamic-counts)
var dynamicCounterTimeout = 10;
var currentDynamicUpdater;
function odcArray(_odca) {
this.odca = _odca;
return;
}
function odc(_c, _from, _to) {
this.c = _c; // $('#control_id')
this.from = _from; // e.g. N
this.to = _to; // e.g. M => (M >= N)
var di = parseInt(_to / 45, 10);
if (di < 1) {
di = 1;
}
this.dynamicInc = di;
return;
}
function dynamicCounterForValueForControlUpdater(_odca) {
if (
_odca.odca === null
||
!_odca.odca.length
) {
clearTimeout(currentDynamicUpdater);
return;
}
var o = _odca.odca[0];
o.from += o.dynamicInc;
if (o.from > o.to) {
o.from = o.to;
_odca.odca.shift(); // Remove first element
}
o.c.html(o.from.toString());
currentDynamicUpdater = setTimeout(
dynamicCounterForValueForControlUpdater,
dynamicCounterTimeout,
_odca
);
return;
}
function dynamicCounterForValueForControl(_odca) {
clearTimeout(currentDynamicUpdater);
// SETUP all counters to default
for (var i = 0; i < _odca.odca.length; i++) {
_odca.odca[i].c.html(_odca.odca[i].from.toString());
}
dynamicCounterForValueForControlUpdater(
_odca
);
return;
}

i want a delay of 100millisec while running each loop

for(i=100;i>=0;i--){
icon.style.filter="alpha(opacity=0)";
}
this is my for loop ... wat i want is tat each time for loop completes 1 loop it should wait or sleep for 100millisec so tat it can give a fadding effect....
You should call setTimeout, which will execute a function after a given delay.
For example:
function fadeOut(i) {
i = i || 100;
icon.style.filter = "alpha(opacity=" + i + ")";
icon.style.opacity = i / 100;
i--;
if (i > 0)
setTimeout(function() { fadeOut(i) }, 100); //Call fadeOut in 100 milliseconds
}
You can also call setInterval, which will keep calling the function until you call clearInterval.
For example:
function fadeOut() {
var i = 100;
var timer = setInterval(function() {
icon.style.filter = "alpha(opacity=" + i + ")";
icon.style.opacity = i / 100;
i--;
if (i <= 0)
clearInterval(timer);
}, 100);
}
You can do this much more easily using the jQuery library, like this:
$(icon).fadeOut();
John Resig wrote very cool JavaScript functions for fading in and out (Editing slightly):
function fadeOut(elem, time)
{
var t = time / 100;
var c = 0;
for (var b = 100; b >= 0; b -= 5)
{
c +=5;
(function(){
var pos = b;
setTimeout(function(){
setOpacity(elem, pos);
}, ((c + 1) * t));
})();
}
}
function fadeOut(elem, time)
{
show(elem);
var t = time / 100;
var c = 0;
for (var b = 100; b >= 0; b -= 5)
{
c +=5;
(function(){
var pos = b;
setTimeout(function(){
setOpacity(elem, pos);
}, ((c + 1) * t));
})();
}
}
function show(elem)
{
elem.style.display = '';
}
function setOpacity(elem, level)
{
if (elem.filters)
{
elem.style.filters = 'alpha(opacity=' + level + ')';
}
else
{
elem.style.opacity = level / 100;
}
}
You would then use it like:
var el = document.getElementById("#element");
fadeIn(el,1000); //Fade in over 1 second
fadeOut(el,1000); //Fade out over 1 second
EDIT:
Would be easier with jQuery, but you would learn how it works using normal JavaScript
$("#element").fadeOut();
$("#element").fadeIn();

Categories