Jump to content

terminating setInterval within


boen_robot

Recommended Posts

From all examples of setInterval() I could find, intervals are always terminated outside of an interval, like so:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>TEST</title></head><body><script> function ShowMessage(){alert("Click!");}</script> <a href="java script:var ID = setInterval(ShowMessage, 2000);void(0);">Click here</a><br> <a href="java script:clearInterval(ID);">Kill alert</a></body></html>

But I need to remove it within the function at a certain time (or in other words - automatically). If the above exmple was to be reworked... say that I wanted the alert to be shown N times where N is not known in advance, but is rather dependant on, say, a number inputted by the user.Theoritically, I could use setTimeout() the amount of times I need to do something but that sounds unorthodox and unefficient for the kind of animation I'm after. It should also be possible to terminate an interval by triggering clearInterval() on a certain condition.The problem is I don't really get what the "this" keyword is within the callback function, nor what exactly does the argument of the callback function receives, and I found no references explaining those, and without them, I can't think of any way I could get N from outside the function.Also, what happens if setInterval is called on a certain node rather than from the window object (i.e. globally)? What happens to the code executed after the setInterval() call? Is it executed immediatly? If so, how could I possibly make a function that would only return a value after an interval has been cleared (if that's impossible, I guess I'll be using setTimeout() if I needed that)?(I'm new to this, so please, if there's a cross browser way of doing this, say that first and only then explain browser differences)

Link to comment
Share on other sites

[...] But I need to remove it within the function at a certain time (or in other words - automatically). If the above exmple was to be reworked... say that I wanted the alert to be shown N times where N is not known in advance, but is rather dependant on, say, a number inputted by the user.Theoritically, I could use setTimeout() the amount of times I need to do something but that sounds unorthodox and unefficient for the kind of animation I'm after. It should also be possible to terminate an interval by triggering clearInterval() on a certain condition.The problem is I don't really get what the "this" keyword is within the callback function, nor what exactly does the argument of the callback function receives, and I found no references explaining those, and without them, I can't think of any way I could get N from outside the function.
1) No cross-browser issues that I'm aware of.2) To remove from within the function, you could add 1 to a global variable inside the function, and check its value. Example below.3) To remove from outside the function, same check of global variable.
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>TEST</title></head><body><script>var ID;var num = 0;function ShowMessage(){if (++num > 5){	clearInterval(ID);	return;}//alert("Click!");document.getElementById('message').innerHTML += '=';}</script><a href="java script:ID = setInterval(ShowMessage, 2000);void(0);">Click here</a><br><a href="java script:clearInterval(ID);">Kill alert</a><div id='message'></div></body></html>

If you are using very small interval then you may encounter the issue that the function is called more than once before it has called clearInterval. I don't know. In that case you could use an extra global variable (isactive) which you set to false when clearing the interval, and check it within the function. (I was also going to ask why people use "java script" which doesn't work but I just realised it's the forum posting that corrupts the text in the same way as it sometimes turns < into < and I wonder why these things happen!) :)

Link to comment
Share on other sites

O...k... this answers a few question, but raises a few more, some of which were also asked already.I'll be using setInterval within a function, or so is my current plan. Inside that function, I'll be executing another (anonymous) function in an interval. I can terminate the inner function with "return;" but how would I only after that terminate the outer function? Checking a variable, regardless of whether it's global or not is not an option if the code executes asynchronously (i.e. before the interval has ended).Consider the example:

<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>TEST</title></head><body><script>function startCounting() {var element = document.getElementById('message');var ID = setInterval(function() {if (element.innerHTML.length > 5){	clearInterval(ID);	return;}//alert("Click!");element.innerHTML += '=';}, 500);	return 'Animation is over';}</script><a href="java script:document.getElementById('message').innerHTML(startCounting());">Start Animation</a><div id='message'></div></body></html>

How could I make it so that the "Start Animation" link gets the value "Animation is over" once... well.. the animation is over. For that particular case, I could use

document.getElementsByTagName('a')[0].innerHTML = 'Animation is over';

after I've cleared the interval, but for the real killer I need to use a return value from the outer (startCounting()) function.

Link to comment
Share on other sites

O...k... this answers a few question, but raises a few more, some of which were also asked already [...]
I'm being deliberately selective for clarity (then and again now). What you're presenting is quite complex, and also a little abstract in that it's not completely obvious what you actually want to achieve by this, just from the example we're using, so I've tried to steer a path between your questions (then and again now) to guess at something that I think will help. Others will respond too, as they see fit. :)
[...]How could I make it so that the "Start Animation" link gets the value "Animation is over" once... well.. the animation is over. For that particular case, I could use
document.getElementsByTagName('a')[0].innerHTML = 'Animation is over';

after I've cleared the interval, but for the real killer I need to use a return value from the outer (startCounting()) function.

The short answer is, you can't use the startCounting() return value, because setInterval() itself returns immediately, and so startCounting() returns before the event of interest occurs.Why not pass the element to startCounting(), so that it's available to the anonymous function:
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>TEST</title></head><body><script>function startCounting(srclink) {var element = document.getElementById('message');var ID = setInterval(function() {if (element.innerHTML.length > 5){	srclink.innerHTML = 'Animation is over';	clearInterval(ID);	return;}//alert("Click!");element.innerHTML += '=';}, 500);   // return 'Animation is over';}</script><a id="theSrcLink" href="java script:startCounting(document.getElementById('theSrcLink'));">Start Animation</a><div id='message'></div></body></html>

Link to comment
Share on other sites

I was afraid you're going to say that... well... the app I'm trying to implement this in is the one from the last bigger issue I've had. I'm trying to make the whole thing animated, as right now, it's a little stiff.One of the animations I'm trying to make (and which is the first one I'll try to make) is for the remove() function to first gradually reduce the height of the row to 0, and only then remove it. And a similar thing for the insert then - create the row with 0 height, and gradually increace it up to... 1em I think (whatever represents the complete default height).The real problem here is that the remove() function still needs to return "false" in order for the form not to be submitted, or is there a way for that not to be needed?Note that the complete JS has changed a lot since then. Here's the new one:

var tableContents = document.getElementsByTagName('tbody')[0];function addEntry() {	var entryRef = 'entry[';	var entries = tableContents.childNodes.length;	var newRow = document.createElement('tr');	var newHeader = document.createElement('th');	var newCell = document.createElement('td');	var newInput = document.createElement('input');	var newCtrlCell = newCell.cloneNode(true);	newCtrlCell.className = 'control';	var newCtrlBtn = newInput.cloneNode(true);	newCtrlBtn.type = 'submit';	newCtrlBtn.name = 'action['+entries+']';	newInput.type = 'text';	newInput.size = 12;	newHeader.appendChild(newInput.cloneNode(true));	var newHeaderField = newHeader.childNodes[0];	newHeaderField.value = 'Гориво';	newHeaderField.name = entryRef+entries+'][0]';	newHeaderField.onkeydown = recover;	newRow.appendChild(newHeader);	newCell.appendChild(newInput.cloneNode(true));	var newCellField = newCell.childNodes[0];	newCellField.value = 'Цена';	newCellField.name = entryRef+entries+'][1]';	newRow.appendChild(newCell);		newCtrlBtnRemove = newCtrlBtn.cloneNode(true);	newCtrlBtnRemove.value = '−';	newCtrlBtnRemove.title = 'Премахни';	newCtrlBtnRemove.onclick = remove;		newCtrlBtnMoveUp = newCtrlBtn.cloneNode(true);	newCtrlBtnMoveUp.value = '▲';	newCtrlBtnMoveUp.title = 'Премести нагоре';	newCtrlBtnMoveUp.onclick = moveUp;	if (entries <= 0) {		newCtrlBtnMoveUp.disabled = true;	}		newCtrlBtnInsert = newCtrlBtn.cloneNode(true);	newCtrlBtnInsert.value = '+';	newCtrlBtnInsert.title = 'Копирай';	newCtrlBtnInsert.onclick = insertAt;		newCtrlBtnMoveDown = newCtrlBtn.cloneNode(true);	newCtrlBtnMoveDown.value = '▼';	newCtrlBtnMoveDown.title = 'Премести нагодлу';	newCtrlBtnMoveDown.disabled = true;	newCtrlCell.appendChild(newCtrlBtnRemove);	newCtrlCell.appendChild(newCtrlBtnMoveUp);	newCtrlCell.appendChild(document.createElement('br'));	newCtrlCell.appendChild(newCtrlBtnInsert);	newCtrlCell.appendChild(newCtrlBtnMoveDown);		newRow.appendChild(newCtrlCell);	tableContents.appendChild(newRow);	if (entries > 0) {		tableContents.childNodes[entries - 1].cells[2].childNodes[4].disabled = false;	}	tableContents.childNodes[entries].cells[0].childNodes[0].select();	tableContents.childNodes[entries].cells[0].childNodes[0].focus();	return false;}function remove() {	var entry = this.parentNode.parentNode.rowIndex - 1;	tableContents.deleteRow(entry);	var entryRef = 'entry[';	var entries = tableContents.childNodes.length;	for(i=entry - 1;i<entries;i++) {		if (i<0) {			continue;		}		var currRow = tableContents.childNodes[i];		currRow.cells[0].childNodes[0].name = entryRef+i+'][0]';		currRow.cells[1].childNodes[0].name = entryRef+i+'][1]';		var ctrlCell = currRow.cells[2];		var entryAction = 'action['+i+']';		ctrlCell.childNodes[0].name = entryAction;		ctrlCell.childNodes[1].name = entryAction;		ctrlCell.childNodes[3].name = entryAction;		ctrlCell.childNodes[4].name = entryAction;		if (i != 0) {			ctrlCell.childNodes[1].disabled = false;		}else {			ctrlCell.childNodes[1].disabled = true;		}		if (i != entries - 1) {			ctrlCell.childNodes[4].disabled = false;		}else {			ctrlCell.childNodes[4].disabled = true;		}	}	if (entries > 0 && entry != 0) {		tableContents.childNodes[entry - 1].cells[2].childNodes[0].focus();	}	return false;}function moveUp() {	var entry = this.parentNode.parentNode.rowIndex - 1;	var entries = tableContents.childNodes.length;	var pEntry = entry - 1;	var oldRow = tableContents.childNodes[pEntry].cloneNode(true);	var newRow = tableContents.childNodes[entry].cloneNode(true);	var entryRef = 'entry[';	var entryRef0 = '][0]';	var entryRef1 = '][1]';	var oldField0 = oldRow.childNodes[0].childNodes[0];	oldField0.name = entryRef+entry+entryRef0;	oldField0.onkeydown = recover;	var oldField1 = oldRow.childNodes[1].childNodes[0];	oldField1.name = entryRef+entry+entryRef1;	oldField1.onkeyup = numbers;		var oldCtrlCell = oldRow.childNodes[2];	oldCtrlCell.childNodes[0].onclick = remove;	oldCtrlCell.childNodes[1].onclick = moveUp;	oldCtrlCell.childNodes[1].disabled = false;	oldCtrlCell.childNodes[3].onclick = insertAt;	if (entry != entries - 1) {		oldCtrlCell.childNodes[4].onclick = moveDown;	}else {		oldCtrlCell.childNodes[4].disabled = true;	}		var newCtrlCell = newRow.childNodes[2];	newCtrlCell.childNodes[0].onclick = remove;	if (pEntry != 0) {		newCtrlCell.childNodes[1].onclick = moveUp;	}else {		newCtrlCell.childNodes[1].disabled = true;	}	newCtrlCell.childNodes[3].onclick = insertAt;	newCtrlCell.childNodes[4].onclick = moveDown;	newCtrlCell.childNodes[4].disabled = false;	newRow.childNodes[0].childNodes[0].name = entryRef+pEntry+entryRef0;	var newField1 = newRow.childNodes[1].childNodes[0];	newField1.name = entryRef+pEntry+entryRef1;	newField1.onkeyup = numbers;	tableContents.replaceChild(newRow,tableContents.childNodes[pEntry]);	tableContents.replaceChild(oldRow,tableContents.childNodes[entry]);		if (pEntry > 0) {		tableContents.childNodes[pEntry].cells[2].childNodes[1].focus();	}else {		tableContents.childNodes[pEntry].cells[2].childNodes[4].focus();	}	return false;}function insertAt() {	var entry = this.parentNode.parentNode.rowIndex - 1;	var pEntry = entry + 1;	var newRow = tableContents.childNodes[entry].cloneNode(true);	if (entry != tableContents.childNodes.length - 1) {		tableContents.insertBefore(newRow,tableContents.childNodes[pEntry]);	}else {		tableContents.appendChild(newRow);	}	var entryRef = 'entry[';	var entries = tableContents.childNodes.length;	for(i=entry;i<entries;i++) {		var currRow = tableContents.childNodes[i];		var currField0 = currRow.cells[0].childNodes[0];		currField0.name = entryRef+i+'][0]';		currField0.onkeydown = recover;		currRow.cells[1].childNodes[0].name = entryRef+i+'][1]';		currRow.cells[1].childNodes[0].onkeyup = numbers;		var ctrlCell = currRow.cells[2];		ctrlCell.childNodes[0].onclick = remove;		ctrlCell.childNodes[1].onclick = moveUp;		ctrlCell.childNodes[3].onclick = insertAt;		ctrlCell.childNodes[4].onclick = moveDown;		var entryAction = 'action['+i+']';		ctrlCell.childNodes[0].name = entryAction;		ctrlCell.childNodes[1].name = entryAction;		ctrlCell.childNodes[3].name = entryAction;		ctrlCell.childNodes[4].name = entryAction;		if (i != 0) {			ctrlCell.childNodes[1].disabled = false;		}		if (i != entries - 1) {			ctrlCell.childNodes[4].disabled = false;		}else {			ctrlCell.childNodes[4].disabled = true;		}	}	tableContents.childNodes[pEntry].cells[0].childNodes[0].select();	tableContents.childNodes[pEntry].cells[0].childNodes[0].focus();	return false;}function moveDown() {	var entry = this.parentNode.parentNode.rowIndex - 1;	var pEntry = entry + 1;	var oldRow = tableContents.childNodes[pEntry].cloneNode(true);	var newRow = tableContents.childNodes[entry].cloneNode(true);	var entryRef = 'entry[';	var entryRef0 = '][0]';	var entryRef1 = '][1]';	var oldField0 = oldRow.childNodes[0].childNodes[0];	oldField0.name = entryRef+entry+entryRef0;	oldField0.onkeydown = recover;	var oldField1 = oldRow.childNodes[1].childNodes[0];	oldField1.name = entryRef+entry+entryRef1;	oldField1.onkeyup = numbers;		var oldCtrlCell = oldRow.childNodes[2];	oldCtrlCell.childNodes[0].onclick = remove;	var oldCtrlCellU = oldCtrlCell.childNodes[1];	if (entry != 0) {		oldCtrlCellU.onclick = moveUp;		oldCtrlCellU.disabled = false;	}else {		oldCtrlCellU.disabled = true;	}	oldCtrlCell.childNodes[3].onclick = insertAt;	oldCtrlCell.childNodes[4].onclick = moveDown;	oldCtrlCell.childNodes[4].disabled = false;		var newCtrlCell = newRow.childNodes[2];	newCtrlCell.childNodes[0].onclick = remove;	newCtrlCell.childNodes[1].onclick = moveUp;	newCtrlCell.childNodes[1].disabled = false;	newCtrlCell.childNodes[3].onclick = insertAt;	var newCtrlCellU = newCtrlCell.childNodes[4];		var entries = tableContents.childNodes.length - 1;	if (pEntry < entries) {		newCtrlCellU.onclick = moveDown;		newCtrlCellU.disabled = false;	}else {		newCtrlCellU.disabled = true;	}	var newField0 = newRow.childNodes[0].childNodes[0];	newField0.name = entryRef+pEntry+entryRef0;	newField0.onkeydown = recover;	var newField1 = newRow.childNodes[1].childNodes[0];	newField1.name = entryRef+pEntry+entryRef1;	newField1.onkeyup = numbers;	tableContents.replaceChild(newRow,tableContents.childNodes[pEntry]);	tableContents.replaceChild(oldRow,tableContents.childNodes[entry]);		if (pEntry < entries) {		tableContents.childNodes[pEntry].cells[2].childNodes[4].focus();	}else {		tableContents.childNodes[pEntry].cells[2].childNodes[1].focus();	}	return false;}function numbers() {	var valNum = this.value.match('[0-9]+(\\x2E[0-9]*)?');	this.value = (valNum != null ? valNum[0] : '');}function recover() {	if (this.className = 'error') {		for(i=0;i<tableContents.childNodes.length;i++) {			var thisField = tableContents.childNodes[i].cells[0].childNodes[0];			if (this.value == thisField.value) {				thisField.className = '';			}		}	}}tableContents.onerror = true;tableContents.onkeyup = function() {	var entries = tableContents.childNodes.length;	var err = 'error';	for(i=0;i<entries;i++) {		var currField = tableContents.childNodes[i].cells[0].childNodes[0];		for(e=0;e<entries;e++) {			var currFieldCopy = tableContents.childNodes[e].cells[0].childNodes[0];			if (currField.value == currFieldCopy.value && e!=i) {				currField.className = err;				currFieldCopy.className = err;				var statusDiv = (document.getElementById('status') == null ? document.createElement('div') :document.getElementById('status'));				statusDiv.className = err;				statusDiv.innerHTML = 'Съществуват две или повече еднакви горива. Първите открити са маркирани.';				if (document.getElementById('status') == null) {					statusDiv.id = 'status';					tableContents.parentNode.parentNode.insertBefore(statusDiv,tableContents.parentNode);				}				return true;			}		}	}};document.getElementById('addEntry').onclick = addEntry;for(i=0;i<tableContents.childNodes.length;i++) {	tableContents.childNodes[i].cells[0].childNodes[0].onkeydown = recover;	tableContents.childNodes[i].cells[1].childNodes[0].onkeyup = numbers;	var ctrlCell = tableContents.childNodes[i].cells[2];	ctrlCell.childNodes[0].onclick = remove;	ctrlCell.childNodes[1].onclick = moveUp;	ctrlCell.childNodes[3].onclick = insertAt;	ctrlCell.childNodes[4].onclick = moveDown;}

I've tried to tune it up for the best possible execution time (I'll run this thru "packer" for the live site for better downloading time's sake), though bare in mind I don't have a weak machine to test on and I'm not sure how else, besides Firebug, to make any performance benchmarks.

Link to comment
Share on other sites

[...]One of the animations I'm trying to make (and which is the first one I'll try to make) is for the remove() function to first gradually reduce the height of the row to 0, and only then remove it.[...]
I understand the problem now: you want a processing loop that pauses between each iteration, but javascript does not have a sleep() function.If you google "javascript sleep" you'll find various things people have done, with varying degrees of success, to implement a "sleep" function in one way or another. Perhaps you'll find one of these useful.
Link to comment
Share on other sites

You might consider setting a main interval that is always executing a particular function - called "animate" or something - that is never cleared. Then, all of your other functions that would require something to be animated simply toggle the "isAnimating" status of an object to true. Then, the next time your interval is called, it'll see that there is an object that needs to be animated and it does so. If nothing is set to "isAnimating", then the main interval does nothing and essentially sleeps until the next interval is fired.

Link to comment
Share on other sites

I understand the problem now: you want a processing loop that pauses between each iteration, but javascript does not have a sleep() function.If you google "javascript sleep" you'll find various things people have done, with varying degrees of success, to implement a "sleep" function in one way or another. Perhaps you'll find one of these useful.
Wouldn't they require that I specify the exact sleeping time in miliseconds, and if so, wouldn't that create problems if the reduction and removal process takes a different (longer) amount of time.How would I integrate such a sleep function, so that it works without knowing the miliseconds in advanced?
You might consider setting a main interval that is always executing a particular function - called "animate" or something - that is never cleared. Then, all of your other functions that would require something to be animated simply toggle the "isAnimating" status of an object to true. Then, the next time your interval is called, it'll see that there is an object that needs to be animated and it does so. If nothing is set to "isAnimating", then the main interval does nothing and essentially sleeps until the next interval is fired.
Wouldn't that have even a bigger performance penalty than setTimeout()'s?
Link to comment
Share on other sites

Wouldn't they require that I specify the exact sleeping time in miliseconds, and if so, wouldn't that create problems if the reduction and removal process takes a different (longer) amount of time.How would I integrate such a sleep function, so that it works without knowing the miliseconds in advanced?Wouldn't that have even a bigger performance penalty than setTimeout()'s?
I have the feeling you're making it more complicated than it needs to be! By rethinking the approach, you may find you can achieve what you want with a combination of
  • setTimeout/setInterval
  • global variables
  • global functions, passed a reference to the triggering element to give context
  • (if necessary) anonymous functions, passed a reference to the triggering element to give context (as I gave an example of above)

But if that's really not enough, then perhaps you're pushing the boundaries of javasript (not bad for a self-titled javascript "beginner"!!) and would do better to consider something other than client scripting to implement your animation (Java???).

Link to comment
Share on other sites

Wouldn't that have even a bigger performance penalty than setTimeout()'s?
Hah, probably.I rarely ever use setInterval and stick to setTimeout instead. If you want to iterate through some array while pausing between iterations, you could accomplish that like this:
<html><body><div id="test"></div><script type="text/javascript">var theArray = [0,1,2,3,4,5,6,7,8,9];var idx = 0;function process(){	// do something with the element in the array	document.getElementById("test").innerHTML += theArray[idx];	// increment the index	idx++;	// and, set the timeout if the index is less than 	// the number of elements in the array	if(idx < theArray.length)	{		setTimeout("process();", 1000);	}}process();</script></body></html>

This way, you don't ever have to worry about clearing any intervals or timeouts and the animation will only process for as long as it needs to.

Link to comment
Share on other sites

I have the feeling you're making it more complicated than it needs to be! By rethinking the approach, you may find you can achieve what you want with a combination of
  • setTimeout/setInterval
  • global variables
  • global functions, passed a reference to the triggering element to give context
  • (if necessary) anonymous functions, passed a reference to the triggering element to give context (as I gave an example of above)

But if that's really not enough, then perhaps you're pushing the boundaries of javasript (not bad for a self-titled javascript "beginner"!!) and would do better to consider something other than client scripting to implement your animation (Java???).

Well, I am not a beginner at other languages you know :) . I'm used to languages which up until this point, I have been used to more "powerful" languages, so pushing a weaker language to its limits sounds like a doable thing by someone like me :) .The plan for the remove() function seemed pretty staright forward at the beginning1. gradually reduce the size of the row to 02. remove the row3. readjust the names of the rest of the rows4. Prevent the form from being submittedRight now, the script implements all but the first, and in this order. In order to add the first, I was first thinking of the recursive setTimeout() approach, but I wanted to look for a solution with setInterval(). Right now, I'll be happy with anything that does all of the above.
Hah, probably.I rarely ever use setInterval and stick to setTimeout instead. If you want to iterate through some array while pausing between iterations, you could accomplish that like this:
<html><body><div id="test"></div><script type="text/javascript">var theArray = [0,1,2,3,4,5,6,7,8,9];var idx = 0;function process(){	// do something with the element in the array	document.getElementById("test").innerHTML += theArray[idx];	// increment the index	idx++;	// and, set the timeout if the index is less than 	// the number of elements in the array	if(idx < theArray.length)	{		setTimeout("process();", 1000);	}}process();</script></body></html>

This way, you don't ever have to worry about clearing any intervals or timeouts and the animation will only process for as long as it needs to.

That's exactly the alternative approach I was thinking about ever since I started the topic. But if the row is, say 1em high, this means 100 setTimeout() invocations (or 50 or 10 if we suppose I speed things up a bit). With setInterval() and clearInterval() it's only 2 funciton invocations, the rest of the processeses being sort of "cached". Seems like a lot of performance impovement to me, though again, this is only an assumption since I can't make much performance analyses.To both...In either case, the problem remains that I need to stop the form from being submitted after the animation and removal has been done. THAT is the main problem, mainly created by the fact setTimeout() and setInterval() execute asyncroniously. If it executed syncrouniously, this topic was probably never going to be created.As I said, I'm now so "given up" on this, that I'd be happy with ANYTHING that does the above 4 step plan.
Link to comment
Share on other sites

That's exactly the alternative approach I was thinking about ever since I started the topic. But if the row is, say 1em high, this means 100 setTimeout() invocations (or 50 or 10 if we suppose I speed things up a bit). With setInterval() and clearInterval() it's only 2 funciton invocations, the rest of the processeses being sort of "cached". Seems like a lot of performance impovement to me, though again, this is only an assumption since I can't make much performance analyses.
I'm not sure how setInterval could only be two function invocations where setTimeout would be 10/50/100. Can you elaborate on that a little for me in case I'm missing something?
In either case, the problem remains that I need to stop the form from being submitted after the animation and removal has been done. THAT is the main problem, mainly created by the fact setTimeout() and setInterval() execute asyncroniously. If it executed syncrouniously, this topic was probably never going to be created.
Something along these lines?
// global variable to keep track of whether or not it is currently ok to submit// the form.  This could be called something like "isAnimating" where the boolean// value is reversed (i.e. if isAnimating, it would not be OK to submit).var OKToSubmit = true;// Some function that is run that tells the rest of the script that the animation// is about to start.  This sets the OKToSubmit to false.function startAnimation(){	OKToSubmit = false;}// Some function that is run that tells the rest of the script that the animation// is about to end.  This sets the OKToSubmit to true.function endAnimation(){	OKToSubmit = true;}// Assign an event handler to the onsubmit event of the form that checks the value// of OKToSubmit and, if true, submits the form, and, if false, returns false to the browser// indicating that the form is not to be submitted.document.getElementById("MyForm").onsubmit = function() { if(OKToSubmit) this.submit(); else return false; }

Link to comment
Share on other sites

Well, I am not a beginner at other languages you know :) . I'm used to languages which up until this point, I have been used to more "powerful" languages, so pushing a weaker language to its limits sounds like a doable thing by someone like me :blink: .
:) From your regular, authoritative xsl posts this could not be in doubt. You certainly don't seem like a beginner to me (even in javascript). In any case, looking back, what you actually said was
(I'm new to this, so please, if there's a cross browser way of doing this, say that first and only then explain browser differences)
and I now think by "this" you didn't mean javascript anyway, but just setInterval. (So appropriate, don't you think, in a conversation about javascript, to have a misunderstanding over the value of this! :))About the form you want to prevent from being submitted. I don't think I've seen where that actually fits into your code? Would I be right in assuming there's a submit button, which when pressed, should result in the reducing animation, followed by the submit? In which case don't forget you can submit a form from javascript. So the submit could be done by the code once the animation is complete, as opposed to being prevented until that point. May help.
Link to comment
Share on other sites

:) From your regular, authoritative xsl posts this could not be in doubt. You certainly don't seem like a beginner to me (even in javascript). In any case, looking back, what you actually said wasand I now think by "this" you didn't mean javascript anyway, but just setInterval. (So appropriate, don't you think, in a conversation about javascript, to have a misunderstanding over the value of this! :))About the form you want to prevent from being submitted. I don't think I've seen where that actually fits into your code? Would I be right in assuming there's a submit button, which when pressed, should result in the reducing animation, followed by the submit? In which case don't forget you can submit a form from javascript. So the submit could be done by the code once the animation is complete, as opposed to being prevented until that point. May help.
That reminds me...Have you heared of the difference between jokes about blondes and jokes about programmers? Jokes about blondes can be understood by anyone but blondes, whereas jokes about programmers can't be undersootd by anyone but programmers :) .Khm... as for where this fits. I have made the form to degrade gracefully with JS off. All buttons, including the ones for the removal, insertions and moving up and down, are infact submit buttons. With JS off, or if there's an error somewhere in the JS (read - crappy JS implementations), the form is expected to be submitted to the server, which will perform the action, and return a new page with that change. With JS on, I do theese things with JavaScript so that I don't go the server for anything, other than saving the new data (saving bandwidth, server load and increasing user experience all at once). In order for the actual submit button not to submit the form, I know the function from an onclick event must return false. That's why I need a way to prevent the form from being submitted.
I'm not sure how setInterval could only be two function invocations where setTimeout would be 10/50/100. Can you elaborate on that a little for me in case I'm missing something?Something along these lines?
// global variable to keep track of whether or not it is currently ok to submit// the form.  This could be called something like "isAnimating" where the boolean// value is reversed (i.e. if isAnimating, it would not be OK to submit).var OKToSubmit = true;// Some function that is run that tells the rest of the script that the animation// is about to start.  This sets the OKToSubmit to false.function startAnimation(){	OKToSubmit = false;}// Some function that is run that tells the rest of the script that the animation// is about to end.  This sets the OKToSubmit to true.function endAnimation(){	OKToSubmit = true;}// Assign an event handler to the onsubmit event of the form that checks the value// of OKToSubmit and, if true, submits the form, and, if false, returns false to the browser// indicating that the form is not to be submitted.document.getElementById("MyForm").onsubmit = function() { if(OKToSubmit) this.submit(); else return false; }

This approach I like... a lot. Or should I say that about what's in my head now thanks to this. I wish I thought of it earlier :blink: . Simple, doable with setInterval(), and I don't have to care what remove() or the other functions return, as long as they set that global variable properly. I'll try this one out, thanks a lot.Note to self (for future reference): clicking a submit button fires "onclick" AND "onsubmit" events, unless the "onclick" handler returned "false".
Link to comment
Share on other sites

It worked! But before I move on, there's one edge case bug I'd like to fix.If you click on the remove button, and while the animation is going click on another, there are errors in the error console. I haven't noticed any "visible" signs of something going wrong, but I'd like to avoid errors in the code from occuring.Odd enough, that error doesn't occur in IE. It's Firefox and Opera that give errors. From Opera's error console:

JavaScript - ../?uri=prices/Event thread: clickError:name: TypeErrormessage: Statement on line 228: Cannot convert undefined or null to ObjectBacktrace: Line 228 of linked script ../editTable.js: In function moveDown var oldRow = tableContents.childNodes[pEntry].cloneNode(true); ...
and from Firebug:
tableContents.childNodes[pEntry] has no propertiesmoveDown()editTable.js (line 228)[break on this error] var oldRow = tableContents.childNodes[pEntry].cloneNode(true);
That is if I press the moveDown button during the animation. The message is slightly different if I press the remove button on another row during the animation:
JavaScript - ../?uri=prices/Timeout thread: delay 10 msError:name: TypeErrormessage: Statement on line 106: Cannot convert undefined or null to ObjectBacktrace: Line 106 of linked script ../editTable.js: ctrlCell.childNodes[1].name = entryAction; ...JavaScript - ../?uri=prices/Timeout thread: delay 10 msError:name: TypeErrormessage: Statement on line 101: Cannot convert undefined or null to ObjectBacktrace: Line 101 of linked script ../editTable.js: currRow.cells[0].childNodes[0].name = entryRef+i+'][0]'; ...
and
ctrlCell.childNodes[1] has no properties(no name)()editTable.js (line 106)[break on this error] ctrlCell.childNodes[1].name = entryAction;editTable.js (line 106)currRow has no properties(no name)()editTable.js (line 101)[break on this error] currRow.cells[0].childNodes[0].name = entryRef+i+'][0]';
respectively.The new JS code is:
var tableContents = document.getElementsByTagName('tbody')[0];var submitForm = true;function addEntry() {	var entryRef = 'entry[';	var entries = tableContents.childNodes.length;	var newRow = document.createElement('tr');	var newHeader = document.createElement('th');	var newCell = document.createElement('td');	var newInput = document.createElement('input');	var newCtrlCell = newCell.cloneNode(true);	newCtrlCell.className = 'control';	var newCtrlBtn = newInput.cloneNode(true);	newCtrlBtn.type = 'submit';	newCtrlBtn.name = 'action['+entries+']';	newInput.type = 'text';	newInput.size = 12;	newHeader.appendChild(newInput.cloneNode(true));	var newHeaderField = newHeader.childNodes[0];	newHeaderField.value = 'Гориво';	newHeaderField.name = entryRef+entries+'][0]';	newHeaderField.onkeydown = recover;	newRow.appendChild(newHeader);	newCell.appendChild(newInput.cloneNode(true));	var newCellField = newCell.childNodes[0];	newCellField.value = 'Цена';	newCellField.name = entryRef+entries+'][1]';	newRow.appendChild(newCell);		newCtrlBtnRemove = newCtrlBtn.cloneNode(true);	newCtrlBtnRemove.value = '−';	newCtrlBtnRemove.title = 'Премахни';	newCtrlBtnRemove.onclick = remove;		newCtrlBtnMoveUp = newCtrlBtn.cloneNode(true);	newCtrlBtnMoveUp.value = '▲';	newCtrlBtnMoveUp.title = 'Премести нагоре';	newCtrlBtnMoveUp.onclick = moveUp;	if (entries <= 0) {		newCtrlBtnMoveUp.disabled = true;	}		newCtrlBtnInsert = newCtrlBtn.cloneNode(true);	newCtrlBtnInsert.value = '+';	newCtrlBtnInsert.title = 'Копирай';	newCtrlBtnInsert.onclick = insertAt;		newCtrlBtnMoveDown = newCtrlBtn.cloneNode(true);	newCtrlBtnMoveDown.value = '▼';	newCtrlBtnMoveDown.title = 'Премести нагодлу';	newCtrlBtnMoveDown.disabled = true;	newCtrlCell.appendChild(newCtrlBtnRemove);	newCtrlCell.appendChild(newCtrlBtnMoveUp);	newCtrlCell.appendChild(document.createElement('br'));	newCtrlCell.appendChild(newCtrlBtnInsert);	newCtrlCell.appendChild(newCtrlBtnMoveDown);		newRow.appendChild(newCtrlCell);	tableContents.appendChild(newRow);	if (entries > 0) {		tableContents.childNodes[entries - 1].cells[2].childNodes[4].disabled = false;	}	tableContents.childNodes[entries].cells[0].childNodes[0].select();	tableContents.childNodes[entries].cells[0].childNodes[0].focus();	submitForm = false;}function remove() {	var thisRow = this.parentNode.parentNode;	var oD = '<div>';	var cD = '</div>';	thisRow.cells[0].innerHTML = oD+thisRow.cells[0].innerHTML+cD;	thisRow.cells[1].innerHTML = oD+thisRow.cells[1].innerHTML+cD;	thisRow.cells[2].innerHTML = oD+thisRow.cells[2].innerHTML+cD;	var entry = thisRow.rowIndex - 1;	var entryRef = 'entry[';	var entries = tableContents.childNodes.length;	var rowHeight = '1em';	var reduce = setInterval(function() {		var currHeight = new Number(rowHeight.substr(0,rowHeight.length - 2)).valueOf();		//alert(typeof currHeight);		if(currHeight >= 0.2 && currHeight <= 1 && typeof currHeight == 'number') {			rowHeight = new String(currHeight - 0.1).concat('em');			//alert(rowHeight);			thisRow.cells[0].childNodes[0].style.height = rowHeight;			thisRow.cells[1].childNodes[0].style.height = rowHeight;			thisRow.cells[2].childNodes[0].style.height = rowHeight;		}else {			clearInterval(reduce);			tableContents.deleteRow(entry);			for(var i=entry - 1;i<entries - 1;i++) {				if (i<0) {					continue;				}				var currRow = tableContents.childNodes[i];				currRow.cells[0].childNodes[0].name = entryRef+i+'][0]';				currRow.cells[1].childNodes[0].name = entryRef+i+'][1]';				var ctrlCell = currRow.cells[2];				var entryAction = 'action['+i+']';				ctrlCell.childNodes[0].name = entryAction;				ctrlCell.childNodes[1].name = entryAction;				ctrlCell.childNodes[3].name = entryAction;				ctrlCell.childNodes[4].name = entryAction;				if (i != 0) {					ctrlCell.childNodes[1].disabled = false;				}else {					ctrlCell.childNodes[1].disabled = true;				}				if (i != entries - 1) {					ctrlCell.childNodes[4].disabled = false;				}else {					ctrlCell.childNodes[4].disabled = true;				}			}			if (entries > 0 && entry != 0) {				tableContents.childNodes[entry - 1].cells[2].childNodes[0].focus();			}		}	}, 10);	submitForm = false;}function moveUp() {	var entry = this.parentNode.parentNode.rowIndex - 1;	var entries = tableContents.childNodes.length;	var pEntry = entry - 1;	var oldRow = tableContents.childNodes[pEntry].cloneNode(true);	var newRow = tableContents.childNodes[entry].cloneNode(true);	var entryRef = 'entry[';	var entryRef0 = '][0]';	var entryRef1 = '][1]';	var oldField0 = oldRow.childNodes[0].childNodes[0];	oldField0.name = entryRef+entry+entryRef0;	oldField0.onkeydown = recover;	var oldField1 = oldRow.childNodes[1].childNodes[0];	oldField1.name = entryRef+entry+entryRef1;	oldField1.onkeyup = numbers;		var oldCtrlCell = oldRow.childNodes[2];	oldCtrlCell.childNodes[0].onclick = remove;	oldCtrlCell.childNodes[1].onclick = moveUp;	oldCtrlCell.childNodes[1].disabled = false;	oldCtrlCell.childNodes[3].onclick = insertAt;	if (entry != entries - 1) {		oldCtrlCell.childNodes[4].onclick = moveDown;	}else {		oldCtrlCell.childNodes[4].disabled = true;	}		var newCtrlCell = newRow.childNodes[2];	newCtrlCell.childNodes[0].onclick = remove;	if (pEntry != 0) {		newCtrlCell.childNodes[1].onclick = moveUp;	}else {		newCtrlCell.childNodes[1].disabled = true;	}	newCtrlCell.childNodes[3].onclick = insertAt;	newCtrlCell.childNodes[4].onclick = moveDown;	newCtrlCell.childNodes[4].disabled = false;	newRow.childNodes[0].childNodes[0].name = entryRef+pEntry+entryRef0;	var newField1 = newRow.childNodes[1].childNodes[0];	newField1.name = entryRef+pEntry+entryRef1;	newField1.onkeyup = numbers;	tableContents.replaceChild(newRow,tableContents.childNodes[pEntry]);	tableContents.replaceChild(oldRow,tableContents.childNodes[entry]);		if (pEntry > 0) {		tableContents.childNodes[pEntry].cells[2].childNodes[1].focus();	}else {		tableContents.childNodes[pEntry].cells[2].childNodes[4].focus();	}	submitForm = false;}function insertAt() {	var entry = this.parentNode.parentNode.rowIndex - 1;	var pEntry = entry + 1;	var newRow = tableContents.childNodes[entry].cloneNode(true);	if (entry != tableContents.childNodes.length - 1) {		tableContents.insertBefore(newRow,tableContents.childNodes[pEntry]);	}else {		tableContents.appendChild(newRow);	}	var entryRef = 'entry[';	var entries = tableContents.childNodes.length;	for(i=entry;i<entries;i++) {		var currRow = tableContents.childNodes[i];		var currField0 = currRow.cells[0].childNodes[0];		currField0.name = entryRef+i+'][0]';		currField0.onkeydown = recover;		currRow.cells[1].childNodes[0].name = entryRef+i+'][1]';		currRow.cells[1].childNodes[0].onkeyup = numbers;		var ctrlCell = currRow.cells[2];		ctrlCell.childNodes[0].onclick = remove;		ctrlCell.childNodes[1].onclick = moveUp;		ctrlCell.childNodes[3].onclick = insertAt;		ctrlCell.childNodes[4].onclick = moveDown;		var entryAction = 'action['+i+']';		ctrlCell.childNodes[0].name = entryAction;		ctrlCell.childNodes[1].name = entryAction;		ctrlCell.childNodes[3].name = entryAction;		ctrlCell.childNodes[4].name = entryAction;		if (i != 0) {			ctrlCell.childNodes[1].disabled = false;		}		if (i != entries - 1) {			ctrlCell.childNodes[4].disabled = false;		}else {			ctrlCell.childNodes[4].disabled = true;		}	}	tableContents.childNodes[pEntry].cells[0].childNodes[0].select();	tableContents.childNodes[pEntry].cells[0].childNodes[0].focus();	submitForm = false;}function moveDown() {	var entry = this.parentNode.parentNode.rowIndex - 1;	var pEntry = entry + 1;	var oldRow = tableContents.childNodes[pEntry].cloneNode(true);	var newRow = tableContents.childNodes[entry].cloneNode(true);	var entryRef = 'entry[';	var entryRef0 = '][0]';	var entryRef1 = '][1]';	var oldField0 = oldRow.childNodes[0].childNodes[0];	oldField0.name = entryRef+entry+entryRef0;	oldField0.onkeydown = recover;	var oldField1 = oldRow.childNodes[1].childNodes[0];	oldField1.name = entryRef+entry+entryRef1;	oldField1.onkeyup = numbers;		var oldCtrlCell = oldRow.childNodes[2];	oldCtrlCell.childNodes[0].onclick = remove;	var oldCtrlCellU = oldCtrlCell.childNodes[1];	if (entry != 0) {		oldCtrlCellU.onclick = moveUp;		oldCtrlCellU.disabled = false;	}else {		oldCtrlCellU.disabled = true;	}	oldCtrlCell.childNodes[3].onclick = insertAt;	oldCtrlCell.childNodes[4].onclick = moveDown;	oldCtrlCell.childNodes[4].disabled = false;		var newCtrlCell = newRow.childNodes[2];	newCtrlCell.childNodes[0].onclick = remove;	newCtrlCell.childNodes[1].onclick = moveUp;	newCtrlCell.childNodes[1].disabled = false;	newCtrlCell.childNodes[3].onclick = insertAt;	var newCtrlCellU = newCtrlCell.childNodes[4];		var entries = tableContents.childNodes.length - 1;	if (pEntry < entries) {		newCtrlCellU.onclick = moveDown;		newCtrlCellU.disabled = false;	}else {		newCtrlCellU.disabled = true;	}	var newField0 = newRow.childNodes[0].childNodes[0];	newField0.name = entryRef+pEntry+entryRef0;	newField0.onkeydown = recover;	var newField1 = newRow.childNodes[1].childNodes[0];	newField1.name = entryRef+pEntry+entryRef1;	newField1.onkeyup = numbers;	tableContents.replaceChild(newRow,tableContents.childNodes[pEntry]);	tableContents.replaceChild(oldRow,tableContents.childNodes[entry]);		if (pEntry < entries) {		tableContents.childNodes[pEntry].cells[2].childNodes[4].focus();	}else {		tableContents.childNodes[pEntry].cells[2].childNodes[1].focus();	}	submitForm = false;}function numbers() {	var valNum = this.value.match('[0-9]+(\\x2E[0-9]*)?');	this.value = (valNum != null ? valNum[0] : '');}function recover() {	if (this.className = 'error') {		for(var i=0;i<tableContents.childNodes.length;i++) {			var thisField = tableContents.childNodes[i].cells[0].childNodes[0];			if (this.value == thisField.value) {				thisField.className = '';			}		}	}}tableContents.onerror = true;document.forms[0].onsubmit = function() {	return submitForm;}tableContents.onkeyup = function() {	var entries = tableContents.childNodes.length;	var err = 'error';	for(var i=0;i<entries;i++) {		var currField = tableContents.childNodes[i].cells[0].childNodes[0];		for(var e=0;e<entries;e++) {			var currFieldCopy = tableContents.childNodes[e].cells[0].childNodes[0];			if (currField.value == currFieldCopy.value && e!=i) {				currField.className = err;				currFieldCopy.className = err;				var statusDiv = (document.getElementById('status') == null ? document.createElement('div') : document.getElementById('status'));				statusDiv.className = err;				statusDiv.innerHTML = '✘ Съществуват две или повече еднакви горива. Първите открити са маркирани.';				if (document.getElementById('status') == null) {					statusDiv.id = 'status';					tableContents.parentNode.parentNode.insertBefore(statusDiv,tableContents.parentNode);				}				return true;			}		}	}};document.getElementById('addEntry').onclick = addEntry;for(var i=0;i<tableContents.childNodes.length;i++) {	tableContents.childNodes[i].cells[0].childNodes[0].onkeydown = recover;	tableContents.childNodes[i].cells[1].childNodes[0].onkeyup = numbers;	var ctrlCell = tableContents.childNodes[i].cells[2];	ctrlCell.childNodes[0].onclick = remove;	ctrlCell.childNodes[1].onclick = moveUp;	ctrlCell.childNodes[3].onclick = insertAt;	ctrlCell.childNodes[4].onclick = moveDown;}

HTML untouched.What could be the problem? I mean... isn't there a way the two animations could execute similtaniously, or am I missing something else?

Link to comment
Share on other sites

For this line:var oldRow = tableContents.childNodes[pEntry].cloneNode(true);tableContents is an array from getElementsByTagName, so it wouldn't have a childNodes property I don't think. You would need to specify an index in tableContents. I think it's the same with the other errors, they are both trying to use tableContents.childNodes.

Link to comment
Share on other sites

For this line:var oldRow = tableContents.childNodes[pEntry].cloneNode(true);tableContents is an array from getElementsByTagName, so it wouldn't have a childNodes property I don't think. You would need to specify an index in tableContents. I think it's the same with the other errors, they are both trying to use tableContents.childNodes.
The definition of tableContents from the top is
var tableContents = document.getElementsByTagName('tbody')[0];

And I don't recall overriding it anywhere.Doesn't that mean tableContents is infact the first tbody element of the getElementsByTagName array, and thus has the childNodes property?Also, if I simply click the moveDown button, that error doesn't occur. It only occurs if I click it during the animation.

Link to comment
Share on other sites

I believe the error is indicating that are attempting to reference an item in an array using an index that is out of range.This is the code (in the moveDown function):

	var entry = this.parentNode.parentNode.rowIndex - 1;	var pEntry = entry + 1;	var oldRow = tableContents.childNodes[pEntry].cloneNode(true);

Are you certain that there are "this.parentNode.parentNode.rowIndex" elements in your childNodes array?

Link to comment
Share on other sites

I believe the error is indicating that are attempting to reference an item in an array using an index that is out of range.This is the code (in the moveDown function):
	var entry = this.parentNode.parentNode.rowIndex - 1;	var pEntry = entry + 1;	var oldRow = tableContents.childNodes[pEntry].cloneNode(true);

Are you certain that there are "this.parentNode.parentNode.rowIndex" elements in your childNodes array?

During the animation, there is. There isn't after the animation, but the button gets disabled after it. I suppose I'll be disabling it before the animation begins, and I'll be over with it, since it wouldn't make sence anyway. Or to be more precise, when the first item is removed, I'll disable the modeUp() from the one below it, and when the bottom one is removed (as in my tests), I'll disable modeDown().But what about the remove() funciton then? If you click remove() and during the animation remove another element, you get two errors, which judging by the error messages come from both the former and laterly pressed buttons. Unlike moveDown() and modeUp(), remove() makes sence on both rows before and after the removal, or so it seems to me.
Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...