I have a gridview and a dropdownlist in my ASP in the C# i have functionality that will allow the user to select which dropdownlist item will apply to each gridview row.
so in the JavaScript i want slideshow like functionality for each dropdownlist item and count how many gridview rows contain that item.
I'm attempting to get the code to run at a consistent speed (2 seconds per dropdownlist item)
Below is my JavaScript
$(document).ready(function () {
loop();
});
function loop() {
var ddl = document.getElementById("cphMain_ddlInc");
for (var j = 1; j < ddl.options.length; j++) {
setTimeout(shippedEmbryos(j), 2000);
}
}
function shippedEmbryos(j) {
var st = $("input[id*=txtShipped]");
var grid = document.getElementById("cphMain_gvFertEntry");
var ddl = document.getElementById("cphMain_ddlInc");
var embryos = 0;
var ddlValue = ddl.options[j].innerHTML;
for (var i = 1; i < grid.rows.length - 1; i++) {
if (ddlValue == grid.rows[i].cells[2].innerHTML) {
var txtbox = $("input[id*=txtShipped]");
var x = txtbox[i].value;
if (x == "") {
x = 0;
}
}
else {
x = 0;
}
embryos += parseInt(x);
}
var label = document.getElementById("cphMain_lblShippedEmbryos");
label.innerHTML = "Embryos Shipped for " + ddlValue + ": " + embryos;
};
JavaScript doesn't work like that ...
The language is single-threaded, which means that if you sleep for two seconds everything is locked up while you do. That includes your current window -- everything you are trying to draw on screen.
In many browsers all windows freeze.
You need to do event-driven programming here where you fire off an event to draw the next lie.
There are a number of work-arounds depending on if you can use ES7 await/async through a cross compiler or if you can use ES6's Promises. Lastly ES5 gives you setTimeout
What is the JavaScript version of sleep()?
If you want to wait some time in JS, you can't keep looping because it would block thread.
You could use setTimeout method instead or you can use Window.requestAnimationFrame() method, it would fit in your current code better.
Here is draft(modified from MDN sample code)
var start;
function step(timestamp) {
var progress;
if (start === null) start = timestamp;
progress = timestamp - start;
// trigger every 2000ms, equal 2s
if (progress >= 2000) {
//reset start time
start = timestamp;
// do whatever you want ..., such as call shippedEmbryos()
//start another loop
requestAnimationFrame(step);
}
}
MDN:Window.requestAnimationFrame()- This method call every frame and it is browser friendly API(would not affect page rendering performance).
It has one argument : timestamp, browser system would pass current timestamp in automatically.
look like this: 55378.47799999872 55395.189000002574 55411.900000006426....
Then you can keep call the method like recursive.
If you want to stop, just simply not call the method.
i have made a JSFiddle to show the problem
http://jsfiddle.net/molokoloco/yvTje/
In few words, i put some listener on the animationStart event, add a new class to the element and then, in a plugin i do, i need to check if the element have an animation (can be associated with the class) before doing, or not, something.
My probleme is i have to wait 25/80 millisecond before it's possible to check if an animation is started or not...
Something i do bad or any suggestion ?
var animationStarted = false,
s = '';
var listener = function(e) {
switch (e.type) {
case "animationstart":
case "webkitAnimationStart":
// BUT "animationstart" do not trigger instantaneously
// For the moment i compute 25 milliseconds on my Chrome & FF
var diff = (new Date().getTime()) - s;
console.log('animationStarted after ' + diff + ' ms'); // HERE THE RESULT : 30ms
break;
}
};
var setup = function() {
var e = document.getElementById("watchme");
e.addEventListener("animationstart", listener, false);
e.addEventListener("webkitAnimationStart", listener, false);
// HERE WE ADD THE CLASS
e.className = "slidein";
s = new Date().getTime(); // Time at witch the class (with anim) is applyed
};
setup();
Use feature detection even before you try to start the animation.
var div = document.createElement("DIV");
div.style["animation"] = "animName 5s infinite";
div.style["WebkitAnimation"] = "animName 5s infinite";
if (div.style["animationDuration"] == "5s") {
// Supports animation
} else (div.style["WebkitAnimationDuration"] == "5s") {
// Supports -webkit-animation
}
This tests for the browser's ability to set individual style attributes using shorthand. If you just set a value and read it back the same way, you'll get false positives.
EDIT: Sorry, misunderstood the problem.
Regarding using events to check for stuff, you will probably have to wait a few milliseconds, no matter what you do... You could check the style definitions themselves using document.styleSheets, but that would be pretty exhausting, depending on the specificity of the css.
var animationName = "";
for (var i = 0; i < document.styleSheets.length; ++i) {
var sheet = document.styleSheets[i];
var rules = sheet.cssRules || sheet.rules;
for (var j = 0; j < rules.length; ++j) {
var rule = rules[j];
if (theElement.matchesSelector(rule.selectorText)) {
var theStyle = rule.style;
animationName = theStyle["WebkitAnimationName"];
}
}
}
It's a pretty crude way of doing this, I know...
this.plotted = [jQuery('#img1'), jQuery('#img2'), jQuery('#img3')];
Blah.prototype.animate = function()
{
if (!this.plotted.length)
throw 'Blah::animate - No points have been plotted';
// fix the scope
var _this = this;
var animateOn = function(image)
{
image.attr('src', _this.options.pointActive);
setTimeout(function() { animateOff(point); }, 700);
}
var animateOff = function(image)
{
image.attr('src', _this.options.pointDefault);
setTimeout(function() { animateOn(point); }, 700);
}
for (var i = 0; i < this.plotted.length; i++)
{
var point = this.plotted[i];
setTimeout(function() { animateOn(point); }, 700);
}
}
I'm trying to animate these 3 images by switching their src between an 'on' and 'off' image. I don't want this to be 'sequential'. Ie, I don't want to see the first image change, then the second and then the third.
I'm using setTimeout to accomplish this. Well, I'm trying to...
Firstly, the problem I'm having is with the setTimeout inside the for loop.
for (var i = 0; i < this.plotted.length; i++)
{
var point = this.plotted[i];
console.log(point); // this correctly shows each image point
setTimeout(function()
{
console.log(point); // this shows the same, first, point
animateOn(point);
}, 700);
}
I have no idea what the inner point isn't matching the outer point :/
Also, I would like to know if this method is, well, stupid. Will these nested function calls continually build onto the stack and eventually cause me to run out of RAM? Is there a better way to approach this?
This doesn't work because of how closures work.
I'd do it like this:
var makeAnimateStarter = function(point) {
return function() {
animateOn(point);
};
};
for (var i = 0; i < this.plotted.length; i++)
{
var point = this.plotted[i];
setTimeout(makeAnimateStarter(point), 700);
}
And it's not a problem from a stack point of view. Every time a timeout is executed, it's in a new call stack. That's why you require _this. setTimeout() is not suspending the thread at that point and then resuming it's executing the function fresh.
This question already has answers here:
javascript: Clear all timeouts?
(13 answers)
Closed 6 years ago.
I'm working on an ajax web appliation which contains many running timeouts and intervals. And now I need to clear all running timeouts and intervals sometimes. Is there a simple way to stop everything without need to store every timeout and interval ID and iterate through them and clear them?
Sometimes it's possible to save the timer Id / Handle to clear it later which would be the best solution. So this is a second best. But I wanted to give a better understanding of what's going on. It basically grabs the highest timer id and clears everything less than that. But it's also possible to clear other timers that you do not want to clear!
It is a little hackish, so be warned!
// Set a fake timeout to get the highest timeout id
var highestTimeoutId = setTimeout(";");
for (var i = 0 ; i < highestTimeoutId ; i++) {
clearTimeout(i);
}
Updated answer after reading the duplicate I closed this question with -
It works and tested in Chrome on OSX
// run something
var id1 = setInterval(function() { console.log("interval", new Date())}, 1000);
var id2 = setTimeout(function() { console.log("timeout 1", new Date())}, 2000);
var id3 = setTimeout(function() { console.log("timeout 2", new Date())}, 5000); // not run
setTimeout(function() { console.log("timeout 3", new Date())}, 6000); // not run
// this will kill all intervals and timeouts too in 3 seconds.
// Change 3000 to anything larger than 10
var killId = setTimeout(function() {
for (var i = killId; i > 0; i--) clearInterval(i)
}, 3000);
console.log(id1, id2, id3, killId); // the IDs set by the function I used
NOTE: Looked at window objects that had a typeof number - funnily enough IE assigns an 8 digit number, FF a single digit starting with 2
Here is a workaround.
window.timeoutList = new Array();
window.intervalList = new Array();
window.oldSetTimeout = window.setTimeout;
window.oldSetInterval = window.setInterval;
window.oldClearTimeout = window.clearTimeout;
window.oldClearInterval = window.clearInterval;
window.setTimeout = function(code, delay) {
var retval = window.oldSetTimeout(code, delay);
window.timeoutList.push(retval);
return retval;
};
window.clearTimeout = function(id) {
var ind = window.timeoutList.indexOf(id);
if(ind >= 0) {
window.timeoutList.splice(ind, 1);
}
var retval = window.oldClearTimeout(id);
return retval;
};
window.setInterval = function(code, delay) {
var retval = window.oldSetInterval(code, delay);
window.intervalList.push(retval);
return retval;
};
window.clearInterval = function(id) {
var ind = window.intervalList.indexOf(id);
if(ind >= 0) {
window.intervalList.splice(ind, 1);
}
var retval = window.oldClearInterval(id);
return retval;
};
window.clearAllTimeouts = function() {
for(var i in window.timeoutList) {
window.oldClearTimeout(window.timeoutList[i]);
}
window.timeoutList = new Array();
};
window.clearAllIntervals = function() {
for(var i in window.intervalList) {
window.oldClearInterval(window.intervalList[i]);
}
window.intervalList = new Array();
};
It works for set/clear timeout/interval functions called after these lines are executed. Try and see it works:
setInterval('console.log(\'a\')', 1000);
setInterval('console.log(\'b\')', 500);
setInterval('console.log(\'c\')', 750);
setTimeout('clearAllIntervals()', 10000);
Proxying does the magic.
var noofTimeOuts = setTimeout('');
for (var i = 0 ; i < noofTimeOuts ; i++) clearTimeout(i);
var max = setTimeout(function(){ /* Empty function */ },1);
for (var i = 1; i <= max ; i++) {
window.clearInterval(i);
window.clearTimeout(i);
if(window.mozCancelAnimationFrame)window.mozCancelAnimationFrame(i); // Firefox
}
There's nothing built-in, but it's pretty easy to blast through all currently outstanding deferred execution functions by calling this clearAll() function:
function clearAll() {
for (var i = setTimeout(function() {}, 0); i > 0; i--) {
window.clearInterval(i);
window.clearTimeout(i);
if (window.cancelAnimationFrame) window.cancelAnimationFrame(i);
}
}
If you are in charge of the page you run, and can wrap the native deferred execution functions in wrappers that do the house keeping for of course equip each setter function with a corresponding .clearAll() too:
(function(deferFunctions) {
for (var setter in deferFunctions) (function(setter, clearer) {
var ids = [];
var startFn = window[setter];
var clearFn = window[clearer];
function clear(id) {
var index = ids.indexOf(id);
if (index !== -1) ids.splice(index, 1);
return clearFn.apply(window, arguments);
}
function set() {
var id = startFn.apply(window, arguments);
ids.push(id);
return id;
}
set.clearAll = function() { ids.slice(0).forEach(clear); };
if (startFn && clearFn) {
window[setter] = set;
window[clearer] = clear;
}
})(setter, deferFunctions[setter]);
})(
{ setTimeout: 'clearTimeout'
, setInterval: 'clearInterval'
, requestAnimationFrame: 'cancelAnimationFrame'
});
To try that it works, you could then try doing this, for instance, which will remain silent, as none of the callbacks end up firing before they're cancelled again:
// Some example timers of all types:
requestAnimationFrame(console.error);
setInterval(console.info, 1000, 'interval');
setTimeout(alert, 0, 'timeout');
// Now you can clear all deferred functions
// by execution type, whenever you want to:
window.setTimeout.clearAll();
window.setInterval.clearAll();
window.requestAnimationFrame.clearAll();
A little hack added to Gokhan Ozturk's answer
If you are using third party libraries which uses Timeouts and Intervals then they will also be cleared, so I added one parameter to notify function that this interval is to be push'ed or not to array.
window.setTimeout = function(code, delay, toBeAdded) {
var retval = window.oldSetTimeout(code, delay);
var toBeAdded = toBeAdded || false;
if(toBeAdded) {
window.timeoutList.push(retval);
}
return retval;
};
... // likewise for all functions.
You might be better off creating a scheduler. Take a look at this approach by Nader Zeid:
https://www.onsip.com/blog/avoiding-javascript-settimeout-and-setinterval-problems
It's an approach that help create some determinacy (because "the time interval argument of each of those functions really only establishes that the given function will execute after at least that amount of time. So a timed event can miss its target by literally any amount of time.").
Specifically, to the question you raise here, you can easily add and remove functions from the queue. While this response is long after the question was raised, hopefully it's helpful to any who find themselves struggling with Timeouts and Intervals.
You cannot clear any timeouts and intervals you don't know about.
You'd need something like getTimeoutList which isn't in the DOM3 spec, or even planned, AFAIK.
The previous proxying trick is nice, but if you have a lot of timeouts and intervals, I would not fill the arrays with consecutive numbers [1,2,3....], but with intervals. For example, instead of having [1,2,3,7,8,9], you would have maybe something like ['1-3','7-9'] or [[1,3],[7,9]], as a memory optimization. Of course this trick is only suited if you have a lot of timeouts and intervals and also if you would not stop arbitrary intervals that often.
I am creating a simple listbox filter that takes the user input and returns the matching results in a listbox via javascript/jquery (roughly 5000+ items in listbox). Here is the code snippet:
var Listbox1 = $('#Listbox1');
var commands = document.getElementById('DatabaseCommandsHidden'); //using js for speed
$('#CommandsFilter').bind('keyup', function() {
Listbox1.children().remove();
for (var i = 0; i < commands.options.length; i++) {
if (commands.options[i].text.toLowerCase().match($(this).val().toLowerCase())) {
Listbox1.append($('<option></option>').val(i).html(commands.options[i].text));
}
}
});
This works pretty well, but slows down somewhat when the 1st/2nd char's are being typed since there are so many items.
I thought a solution I could use would be to add a delay to the textbox that prevents the 'keyup' event from being called until the user stops typing. The problem is, I'm not sure how to do that, or if its even a good idea or not.
Any suggestions/help is greatly appreciated.
You can do a delay like this:
$('#CommandsFilter').keyup(function() {
clearTimeout($.data(this, 'timer'));
var wait = setTimeout(search, 500);
$(this).data('timer', wait);
});
function search() {
var temp = $("<select />");
for (var i = 0; i < commands.options.length; i++) {
if (commands.options[i].text.toLowerCase().match($(this).val().toLowerCase())) {
$('<option></option>', { val: i, html: commands.options[i].text }).appendTo(temp);
}
}
Listbox1.empty().append(temp.children());
}
This stores a timeout on the element you're typing in, if 500ms (adjust as needed) passes between keystrokes, a search executes. Also this appends the elements in a document fragment then into the DOM (still preserving encoding, etc). Depending on the number of items, this may be a decent performance boost as well.
If the commands drop-down isn't changing, I'd suggest the following (note I've dropped jQuery for better performance and compatibility). There are several improvements:
Timer to delay updating the filtered list once half a second has elapsed since the last keypress
List of command texts is pre-cached
Unnecessary use of match replaced with indexOf
Uses fast native DOM manipulation that works in all scriptable browsers since the 1990s
A quick test suggests that for a drop-down with 5000 options containing short strings, it's between 10 and 30 times faster than the jQuery equivalent in most browsers.
Code:
var commands = document.getElementById("DatabaseCommandsHidden");
var filteredDropDown = document.getElementById("Listbox1");
var filterInput = document.getElementById("CommandsFilter");
var timer;
// Create a cached list of the lower case text of the commands drop-down
var commandTexts = [], commandText;
for (var i = 0, len = commands.options.length; i < len; ++i) {
commandText = commands.options[i].text;
commandTexts.push({original: commandText, lower: commandText.toLowerCase()});
}
function populateFilteredDropDown() {
timer = null;
var val = filterInput.value.toLowerCase(), commandText;
var opts = filteredDropDown.options;
filteredDropDown.length = 0;
for (var i = 0, len = commandTexts.length; i < len; ++i) {
commandText = commandTexts[i];
if (commandText.lower.indexOf(val) > -1) {
opts[opts.length] = new Option(commandText.original);
}
}
}
filterInput.onkeyup = function() {
if (timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(populateFilteredDropDown, 500);
};