Prevent function from invoking until flag get set to true - javascript

I have created pretty easy gallery. The elements gets their transform position increased or decreased on click using
function pushIt(max, target, index, count) {
if (count == max ) {
target[index -1].addEventListener("transitionend",turnOf,false);
return;
}
var tmp = target[index];
var matrix = window.getComputedStyle(tmp).getPropertyValue("transform");
var translate_left = matrix.split(",")[4];
var translate_top = matrix.split(",")[5].split(")")[0]-215;
tmp.style.transform = "translate3d(" + translate_left + "px," + translate_top + "px,0)";
setTimeout(function(){
pushIt( max, target, index + 1, count + 1 );
},50)
}
function turnOf(){
running = false;
this.removeEventListener(turnOf);
}
Everything is working fine , but the problem is , when i click xxx time rly fast , it gets destroyed and does unwanted behavior. I am using flag, so the function can be called only when "running" is false , which i return back to false when the transition of the last element that should be moved is over. It works on the first few clicks , but fast clicking ruins it and break whole script.
Live demo ( click rly fast xxx times to see the behavior )
What could cause this? The flag is only set only when the transition ends, so why the function gets invoked ? Is there a way how to fix it , or shoud i use brute force ( promises ) for this?

This seems to be your problem:
function turnOf(){
running = false;
//this.removeEventListener(turnOf);
this.removeEventListener("transitionend", turnOf);
}

Related

Clearing svg multiple children with a loop not working

I am currently trying to create a reset code so that when the player collides with the enemy, it clears all of the collectibles (I called them pellets) from the svg and then remakes them with a loop I have. For some reason it is clearing every pellet but one. It also does not recreate the pellets like it is supposed to for some reason. The svg variables are score, player, ghost(the enemy) and pellet.
This is the reset code:
function destroyPlayer()
{
alert("Game Over");
score = 0;
scoreElement.textContent = 'score: ' + score;
pelletCount=0;
constantCount = 1;
//need code to take out all pellets from svg
for(i = 0; i < 100; i++)
{
if(svg.children[i].id != "ghost" &&
svg.children[i].id != "score" &&
svg.children[i].id != "player")
{
svg.removeChild(svg.children[i]);
}
}
positionPellet();
positionGhost();
}
And this is the code that remakes the pellets: (the position pellet method)
function positionPellet()
{
while(pelletCount < constantCount*3)
{
var pellet = document.createElementNS( xmlns, 'circle' );
pellet.setAttribute( 'cx', Math.random() * 900 );
pellet.setAttribute( 'cy', Math.random() * 400 );
pellet.setAttribute( 'r' , 10 );
pellet.className.baseVal = "pelClass";
pelletCount++;
svg.appendChild(pellet);
}
}
Have you checked your browser developer console (press F12)? It should be displaying errors it finds in your Javascript.
If that's not helping much, you can log variables to the console to see why your code isn't working.
For instance, I can see immediately why your positionPellet() function isn't working. As a hint try adding some logging to check the value of pelletCount and constantCount.
function positionPellet()
{
console.log("pelletCount = "+pelletCount);
console.log("constantCount= "+constantCount);
while(pelletCount < constantCount*3)
{
...
When you run the code again, you should see those debugging lines with values showing as undefined. Why would that be?
Your other problem is a little more subtle. I'll give you a hint and say that when you remove element 0 from an array, the array is updated immediately. The array item that used to be at children[1] is now at children[0]. But in your next time through the loop, you will be looking at children[1], which won't be the next child any more, it will actually be the one that was after that (originally children[2]).

JavaScript Automated Clicking

So, here's my issue.
I need to write a script to be run in the console (or via Greasemonkey) to automate clicking of certain links to check their output.
Each time one of these links is clicked, they essentially generate an image in a flash container to the left. The goal here is to be able to automate this so that the QC technicians do not have to click each of these thumbnails themselves.
Needless to say, there needs to be a delay between each "click" event and the next so that the user can view the large image and make sure it is okay.
Here is my script thus far:
function pausecomp(ms) {
ms = ms + new Date().getTime();
while (new Date() < ms){}
}
var itemlist, totalnumber, i;
itemlist = document.getElementsByClassName("image");
totalnumber = parseInt(document.getElementById("quickNavImage").childNodes[3].firstChild.firstChild.nodeValue.replace(/[0-9]* of /, ""));
for(i = 0; i < totalnumber; i = i + 1) {
console.log(i);
itemlist[i].childNodes[1].click();
pausecomp(3000);
}
Now, totalnumber gets me the total number of thumbnails, obviously, and then itemlist is a list of get-able elements so I can access the link itself.
If I run itemlist[0].childNodes[1].click() it works just fine. Same with 1, 2, 3, etc. However, in the loop, it does nothing and it simply crashes both Firefox and IE. I don't need cross-browser capability, but I'm confused.
There is a built-in JS function "setInterval(afunction, interval)" that keeps executing a given function every "interval" miliseconds (1000 = 1s).
This fiddle shows how to use setTimeout to work through an array. Here is the code:
var my_array = ["a", "b", "c", "d"];
function step(index) {
console.log("value of my_array at " + index + ":", my_array[index]);
if (index < my_array.length - 1)
setTimeout(step, 3000, index + 1);
}
setTimeout(step, 3000, 0);
Every 3 seconds, you'll see on the console something like:
value of my_array at x: v
where x is the index in the array and v is the corresponding value.
The problem with your code is that your pausecomp loop is a form of busy waiting. Let's suppose you have 10 items to go through. Your code will click an item, spin for 3 seconds, click an item, spin for 3 seconds, etc. All your clicks are doing is queuing events to be dispatched. However, these events are not dispatched until your code finishes executing. It finishes executing after all the clicks are queued and (roughly) 30 seconds (in this hypothetical scenario) have elapsed. If the number of elements is greater that's even worse.
Using setTimeout like above allows the JavaScript virtual machine to regain control and allows dispatching events. The documentation on setTimeout is available here.
People were correct with SetInterval.
For the record, here's the completed code:
/*global console, document, clearInterval, setInterval*/
var itemlist, totalnumber, i, counter;
i = 0;
function findmepeterpan() {
"use strict";
console.log("Currently viewing " + (i + 1));
itemlist[i].scrollIntoView(true);
document.getElementById("headline").scrollIntoView(true);
itemlist[i].style.borderColor = "red";
itemlist[i].style.borderWidth = "thick";
itemlist[i].childNodes[1].click();
i = i + 1;
if (i === totalnumber) {
clearInterval(counter);
console.log("And we're done! Hope you enjoyed it!");
}
}
function keepitup() {
"use strict";
if (i !== 0) {
itemlist[i - 1].style.borderColor = "transparent";
itemlist[i - 1].style.borderWidth = "medium";
}
findmepeterpan();
}
itemlist = document.getElementsByClassName("image");
totalnumber = parseInt(document.getElementById("quickNavImage").childNodes[3].firstChild.firstChild.nodeValue.replace(/[0-9]* of /, ""), 10);
counter = setInterval(keepitup, 1500);

jQuery .click command order

This is a very simple question/problem, and I can easily work around it, but since I'm learning javascript I was very eager to know WHY exactly this particular problem is happening.
$("#go").click(function() {
$("p").append(array[x] + " ")
functionlist[array[x]]()
x++
})​
This does not work as I expect it to. I want it to write the current content of array, perform a simple animation that is associated with a certain function name, and then increment x. Everything works, except it doesn't increment x.
If I do this:
$("#go").click(function() {
$("p").append(array[x] + " ")
//functionlist[array[x]]()
x++
})​
x is incremented successfully.
So my question is, why does this happen?
Here is a link to a jsfiddle that I am using: http://jsfiddle.net/mxy6N/3/
Well, if you check your script console (F12 is most browsers), you'll see that functionlist[array[x]]() throws an error something like:
Object has no method "smooch"
This is because array[x] is equal to "smooch", and functionlist["smooch"] is undefined, so it errors out before it makes it to your x++.
Other things going on in this code:
x is declared inside of your function, therefore it will always be 0 at the time it is used.
even if it were declared outside of your function, as you increment it, it will run out of items to look at in your array. You'll need to use a modulo operator or two here.
You're not referencing an .js files that have a definition for $.fn.transition, so your transition calls will also error out.
flip and flop both have the same rotateY value, so once it "flips" it won't "flop"
Here is something that might do what you're looking to do: http://jsfiddle.net/g5mJd/3/
And the updated code:
var array = ["smooch", "jab", "flip"];
var move = "flip";
var x = 0;
var functionlist = {
flip: function() {
$("#block").transition({
perspective: '0px',
rotateY: '180deg'
}, 1000);
},
flop: function() {
$("#block").transition({
perspective: '0px',
rotateY: '0deg'
}, 100);
}
};
$("#go").click(function() {
var functionName = (x % 2 == 0) ? 'flip' : 'flop';
var fn = functionlist[functionName];
if($.isFunction(fn)) {
fn();
}
var say = array[x % array.length];
$('p').append(say + ' ');
x++;
})​;
EDIT: Updated with $.isFunction() check.

Javascript character limit counter function for text input

I've made a counter with javascript that shows a user how characters are remaining (from a set limit) for some text input or text area. Here's the code:
<script type="text/javascript">
function CountRemaining()
{
var limit = 1000;
var count = document.getElementById('press-form-body').value.length;
document.getElementById('counter').innerHTML = ((limit-count) + " characters left");
var timer = setTimeout("CountRemaining()",50);
}
</script>
My abomination above works fine but my problem is that I need to use this multiple times and making a separate function for every time I need it would be impractical to say the least.
I tried this and it didn't work:
<script type="text/javascript">
function CountRemaining(string, targetcounter, limit)
{
var count = document.getElementById(string).value.length;
document.getElementById(targetcounter).innerHTML = ((limit-count) + " characters left");
var timer = setTimeout("CountRemaining()",50);
}
I then figured I put the wrong statement for the timer so I changed it to this but still didn't work:
var timer = setTimeout("CountRemaining(string, targetcounter, limit)",50);
I'm lost. Any help would be highly appreciated. Thank you!
I think a better idea would be to use the "onchange" event for those types of elements.
Basically as soon as the text area / text input loses focus and is changed, you can bind a function to count how many characters are left.
document.getElementById('press-form-body').onchange = function() {
// your stuff (double check this to make sure the "this" value is right
// use this as an example
document.getElementById(targetcounter).innerHTML = this.value.length - 1000
}
Another solution would be to use the "key" events to listen to any keypress in the inputs.
document.getElementById('press-form-body').onkeypress = function() {
// your stuff (double check this to make sure the "this" value is right
// use this as an example
document.getElementById(targetcounter).innerHTML = this.value.length - 1000
}
function limittxt()
{
var tval = document.getElementById('press-form-body').value;
tlength = tval.length;
set = 100;
remain = parseInt(set - tlength);
document.getElementById('counter').innerHTML = remain + " characters left";
if (remain <= 0) {
document.getElementById('press-form-body').value = tval.substring(0, tlength - Math.abs(remain)))
}
}
An call this function in the input element like the following :
<input type='text' onkeypress='limittxt()' onkeyup='limittxt()' onkeydown='limittxt()'>
I suppose the error is with following line:
var timer = setTimeout("CountRemaining(string, targetcounter, limit)",50);
Here i think it should come like:
var str = "CountRemaining(" + string + "," + targetcounter + "," + limit + ")";
var timer = setTimeout(str,50);
If you want to proceed along the lines of using the timer to run the function at regular intervals, then you would need code similar to the following (hat-tip to #Sameera Thilakasiri for the inspiration):
function CountRemaining(string, targetcounter, limit){
var count = document.getElementById(string).value.length;
document.getElementById(targetcounter).innerHTML = ((limit-count) + " characters left");
}
setInterval(function() {
// call the function for each of the inputs on the page you need a counter for
CountRemaining('press-form-body', 'counter', 1000);
// etc
}, 50);
However, I believe #amchang87's approach is better overall, so I recommend you go with that if possible.
Tracking the number of characters left is always a little difficult. A good event to use is keyup or keypress, but that doesn't cover text that is dragged and dropped into the element, so people end up using a timer.
If you have many elements to monitor, consider putting them into an array, then call the timer at each interval and check all of the elements. Be careful with performance though, running the function every 50 ms may sap quite a bit of browser performance so try to keep the processing to an absolute minimum.
That means caching whatever you can and keep the logic simple.
Edit
The run and stop methods below could be used to start the timer when particular elements get focus, then stop it when they lose focus. That way you aren't hogging resources when not required.
/Edit
var keyCountCheck = (function() {
var elementArray, timerRef;
return {
// Initialise once
init: function() {
var input, inputs;
// Initialise elementArray if hasn't been done already
// If adding and removing elements, create new aray
// instead each time.
if (!elementArray) {
elementArray = [];
inputs = document.getElementsByTagName('input');
for (var i=0, iLen=inputs.length; i<iLen; i++) {
input = inputs[i];
if (input.type == 'text') {
elementArray.push(input);
}
}
}
timerRef = window.setInterval(keyCountCheck.run, 50);
},
// Run timer
run: function() {
// If setInterval not running, start it
if (!timerRef) {
keyCountCheck.init();
}
var el;
for (var i=0, iLen=elementArray.length; i<iLen; i++) {
checkLength(elementArray[i]);
}
},
// In case there is a reason to stop this thing.
stop: function() {
if (timerRef) {
window.clearTimeout(timerRef);
timerRef = null;
}
}
};
}());
window.onload = keyCountCheck.init;
function checkLength(el) {
// Character limit can be set as a data- attribute or
// class or various other ways. This is the simple way
var limit = 10;
var msgEl = document.getElementById(el.id + '_limitMsg');
if (msgEl) {
msgEl.innerHTML = (limit - el.value.length) + ' characters left. ' + (new Date());
}
}
Some supporting HTML to play with:
<input id="i0" value="1"><span id="i0_limitMsg"></span>
<br>
<input id="i1" value="2"><span id="i1_limitMsg"></span>
<br>
<button onclick="keyCountCheck.stop()">stop</button>
<button onclick="keyCountCheck.run()">run</button>
setInterval(
function CountRemaining(string, targetcounter, limit){
var count = document.getElementById(string).value.length;
document.getElementById(targetcounter).innerHTML = ((limit-count) + " characters left");
},50
);
Tryout this way.
Basic concept of this solution,
var f = function() {function_name(arg1); };
setTimeout(f, msec);

Simple Javascript Image Rotation Not Properly Looping

This is a simple rotation script that is not working. It has four alerts: currentImage, 1, 2, and 3. In FF it goes through four alerts and stops. In Chrome, it goes through five. The image only changes once in both browsers.
function rotateImages(currentImage, id) {
var dir = "/images/";
var a = new Array("coolspider1.jpg", "coolspider2.jpg", "coolspider3.jpg");
var b = document.getElementById(id);
if(currentImage >= a.length){
currentImage=0;}
//loop stops here in ff
alert(a[currentImage]);
//loop stops here in chrome
b.src = dir + a[currentImage];
alert(1);
currentImage++;
alert(2);
rotator = window.setTimeout("rotateImages(" + currentImage + "," + id + ")",500);
alert(3);
}
There's a lot of issues here:
You are accessing the DOM too much with getElementById each time. Instead pass the element.
You are creating the same array each time. var a = new Array(...) can be created once.
You are never assigning to currentImage
You are using setTimeout weirdly, instead of a simple loop, for(i;etc..) setTimeout(func, 500 + i*500, params...)
You are using alert for something... not sure
Replace:
window.setTimeout("rotateImages(" + currentImage + "," + id + ")",500);
with:
window.setTimeout(rotateImages,500, currentImage, id);
setTimeout takes optional parameters of the... well, parameters.

Categories

Resources