JavaScript Automated Clicking - javascript

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);

Related

How many times can you make count += 1 in one second with Javascript?

I am trying to measure how many times i can make "count = count + 1" in one second. But this function breaks browser. Anyone can say why is it happening or have a better method to calculate it?
function counter(){
var count = 0;
var trs = true;
while(trs){
count = count + 1;
setTimeout(() => {trs = false;}, 1000);
}
console.log(count);
}
The reason it crashes is because of infinite loop. JavaScript is event driven + single threaded and in JavaScript execution model, the function must finish before it yields control to another function. Thus, the code while (trs) {...} never yields control to the function defined in setTimeout because it never finishes. trs is always true.
Nice visualization on how the JavaScript execution model works: https://blog.avenuecode.com/understanding-the-javascript-concurrency-model
Here's a better way to do it:
function counter() {
var count = 0
var start = Date.now()
var end = start + 1000
while (Date.now() <= end) {
count = count + 1
}
console.log(count)
}
There is an infinite loop over there. Now, since javascript is single-threaded, the browser will get stuck performing that infinite loop, which may cause the cache filling up very quickly and cause a crash (or not re-render anything).
First, generally speaking, it is better to create the setTimeout only once, before you call while:
var count = 0;
var trs = true;
setTimeout(() => {trs = false;}, 1000);
while(trs){
count = count + 1;
}
console.log(count);
However, this will also cause a crash.
You can try to measure the operations differently, measure how much time it takes for a single count = count + 1 operation to run, then find how many of these can be executed in a second (Although it is not the same as running X consecutive operations, which another parameters could influence the result, but I am assuming you care about a single operation or single function you want to measure).
var count = 0;
var t0 = performance.now()
count = count + 1;
var t1 = performance.now()
console.log(1000/(t1 - t0));
If you want it to be inside a loop, you can also include the value check (with if), which the while does before executing its block.
var count = 0;
var trs=true;
var t0 = performance.now()
if(trs==true){
count = count + 1;
}
var t1 = performance.now()
console.log(1000/(t1 - t0));

Audio element volume not resetting after setInterval with jQuery

The code below randomly selects and plays audio elements (of different pitches, chosen by the user). I am working on a fade option; the user specifies a fade-time in seconds, which is passed in by ID (fade.value).
I am actually having two problems:
A) In the first iteration, the audio elements begins fading before it starts playing.
B) When the same element is called repeatedly, if the fade time is longer than the repeat time, the volume does not reset properly (which was the intended point of tone.volume=1), but "stays down."
function pickAndPlay(pitchSet, saturation){
var pitchCheck = rand(1,100);
if (pitchCheck <= saturation){
fyshuffle (pitchSet); // the Fischer-Yates shuffle
var tone = document.getElementById(pitchSet[0]);
tone.currentTime = 0;
tone.volume = 1;
tone.play();
if(fadeTime.value>0){
$(tone).animate({volume: 0}, fadeTime.value*1000);
};
document.getElementById("noteName").value=pitchSet[0];
console.log(iteration);
iteration++;
console.log("tone.volume:"+tone.volume);
};
};
function nextThing(millis,pitchSet,saturation){
if(iteration==0){pickAndPlay(pitchSet,saturation)};
return setInterval(pickAndPlay,millis,pitchSet,saturation); // this needs `return`: https://stackoverflow.com/questions/44609995/settimeout-recursion-javascript
};
Thanks for any suggestions or references you can offer about how to fix these problems.
For the second question, the animation is still running when the audio begins playing again. Just use the jQuery stop method:
function pickAndPlay(pitchSet, saturation){
var pitchCheck = rand(1,100);
if (pitchCheck <= saturation){
fyshuffle (pitchSet);
var tone = document.getElementById(pitchSet[0]);
tone.currentTime = 0;
$(tone).stop();
tone.volume = 1;
tone.play();
if(fadeTime.value>0){
$(tone).animate({volume: 0}, fadeTime.value*1000); console.log(fadeTime.value);
};
document.getElementById("noteName").value=pitchSet[0];
iteration++;
};
};

Delay logic for web automation chrome extension (Javascript)

I am making a chrome extension for web automation.The first step is to get a list of sites and instructions from a server in a delimiter-format.
Once the list is obtained it is divided into an array that i call "siteArray".
The site array is then divide into another array i call "instructionsArray"
and among those items in the array one of them is the duration spent on the site i.e instructionsArray[5] has the value of "10" seconds. {the duration is not the same for all the sites}
My question arises in how to delay(implementing duration)
One implementation I found is using a sleep function which turns out to be inefficient as it is just a long for loop
see code:
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
and then:
getlist();
for each(siteArray){
instructionsArray = siteArray[count].split(delimiter);
gotoUrl(instructionsArray[1]);
sleep(instructionsArray[5]);
count++;
}
Where
getlist() fetches a list of instructions and splits into the
siteArray .
gotoUrl() changes url.
sleep() runs the sleep function.
Is there a better way to implement a duration/wait/delay.
Rework the way you iterate over the array: process one entry, set a timer, repeat.
var count = 0;
processSite();
function processSite() {
if (count >= siteArray.length) {
console.log('all done!');
return;
}
var instructionsArray = siteArray[count].split(delimiter);
count++;
gotoUrl(instructionsArray[1]);
setTimeout(processSite, instructionsArray[5]);
}
After giving much thought into it I have solved the issue.
Using wOxxOm's idea I came up with this 'sub-idea' to create multiple timeouts i.e
for(index = 1;index<(siteArray.length);index++){
instructionsArray = siteArray[index].split(delimiter);
bigtime+=instructionsArray[5];
setTimeout(function(){processNext(count)},bigtime);
}
This code creates multiple timeout functions for each site.
similar to :
setTimeout(function(){processNext(count)},10 sec);
setTimeout(function(){processNext(count)},10+10 sec);
setTimeout(function(){processNext(count)},10+10+10 sec);
but in a dynamic way.
The count variable is change in the processNext function:
processNext(count){
instructionsArray = siteArray[count].split(delimiter);
visitUrl(instructionsArray[1]);
count++;
}
I give Thanks to wOxxOm for the insight.

How to get setTimeout to start counting as soon as the alert box is open

I currently have a function that creates alerts using the alert() function inside of the setTimeout() function, and that is nested inside of a for loop.
So it looks something like this
for(i<10){
setTimeout(function(param){
alert(param);
3000, param);
i++
}
The issue i am having when i do this is, the timer only starts counting after i click ok on the alert box.
So the first alert pops up, i click ok, 3 seconds later the second box appears.
Instead, I want to be able to have the first alert appear, wait 3 seconds, press ok, and the second alert to appear immediately
How can i do this?
Edit: I tried setInterval isntead of setTimeout as suggested but it seems to cause me to get stuck in a set interval loop?
Here is my code:
for (var product in cart) {
alert(product);
setTimeout(function(item) {
alert("Name: " + item + ": Quantity: " + cart[item]);
}, i * 3000, product);
i++;
}
I just tried replacing setTimeout with setInterval, I also tried clearInterval right after and the box is not even appearing
You need to use setInterval() for executing code with certain interval of time. Use clearInterval() for canceling the setInterval() action.
var i = 0,
param = 'hi';
var intr = setInterval(function(param) {
if (++i === 10)
clearInterval(intr);
console.log(param);
}, 3000, param);
In your case you can get object keys into an array using Object.keys(), then inside setInterval() you can get it using index.
var i = 0,
cart = {
car: 1,
car1: 2,
car2: 3,
car3: 4,
car4: 5,
car5: 6,
car6: 7,
car7: 8,
car8: 9,
car9: 10
},
// getting object keys
arr = Object.keys(cart);
// initializing interval
var intr = setInterval(function(p) {
// getting value using key
alert('Name : ' + p[i] + ' Quantity : ' + cart[p[i]]);
// canceling interval
if (++i === 10)
clearInterval(intr);
}, 3000, arr);
The for loop will execute as fast as possible, so your timeouts are just going to be called at t~=0 + timeout which won't be noticeable.
What you need is to increment a variable and set your timeouts to that variable*timeout.
Also, the call to window.alert stops the execution of your scripts. You may need to use some other way to show your info-box (like a modal-window) :
var obj = {x:1, y:2, z:3}
var i = 0;
var now = performance.now();
for(var p in obj){
setTimeout(function(){snippet.log(this +' __ '+ (performance.now()-now));}.bind(obj[p]), ++i*3000);
}
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Updated to work while iterating through properties of the products:
I made it simple by keeping a setInterval and got the object properties with Object.keys(obj)
var products = {
sexdoll: '$69',
dog: '$0',
soul: '$minimum wage'
}, keyedProducts = Object.keys(products);
var i=0, intval = setInterval(function() {
alert(keyedProducts[i]);
if(++i > keyedProducts.length-1) { clearInterval(intval) }
}, 3000);
http://jsfiddle.net/xybt1g9x/

Instant updating UI elements using javascript

I know maybe this is not possible.
I have searched the web but without suceess
I have a while loop and I want to do two things:
Update a textarea with formatted information
Update the width of div (a progress bar).
The first function has 4-5 additional sub-functions.
Basically I have a 6 elements numerical array. I have a custom format function to create a formatted string elements for my numbers. etc.
if (reg_index/reg_total > last_refresh) {
window.setTimeout (
function() {
document.getElementById("progress_line").style.width = "" + 100 * last_refresh + "px";
document.getElementById("progress_value").innerHTML = my_format( 100*last_refreh, "###" ) + "%";
},
5
);
last_refresh+=0.01;
}
Ok, I'm unable to define a right timeout interval to get what I want.
Can anybody point me to a useful link?
Thanks.
The problem you're having here is that all of your functions will use the last value of last_refresh. When you define a function, it has an enduring reference to the variables in scope, not a copy of their values when it's created. More: Closures are not complicated
You could do this:
function update() {
if (reg_index/reg_total>last_refresh)
document.getElementById("progress_line").style.width =" "+100*last_refresh+"px";
document.getElementById("progress_value").innerHTML=my_format(100*last_refreh,"###")+"%";
last_refresh+=0.01;
setTimeout(update, 0); // Or 5 or whatever
}
}
update();
That uses a function that does a bit of the work, then schedules itself to do more of the work in a moment. It closes over the reg_index, reg_total, and last_refresh variables.
If you know it needs to run at least once, you can make it slightly more efficient:
function update() {
document.getElementById("progress_line").style.width =" "+100*last_refresh+"px";
document.getElementById("progress_value").innerHTML=my_format(100*last_refreh,"###")+"%";
last_refresh+=0.01;
if (reg_index/reg_total>last_refresh)
setTimeout(update, 0); // Or 5 or whatever
}
}
update();
To help with the concept of looping with setTimeout, compare this standard loop (the browser doesn't update until the very end): Live Copy | Live Source
var a = ["zero", "one", "two", "three", "four", "five"];
var index;
for (index = 0; index < a.length; ++index) {
display("Entry " + index + " is " + a[index]);
}
...with this equivalent using setTimeout, which yields to the browser on every iteration so it can update (and even if the interval is 0, it runs much more slowly — I've used 200 here so you can see it run): Live Copy | Live Source
var a = ["zero", "one", "two", "three", "four", "five"];
var index;
index = 0;
update();
function update() {
display("Entry " + index + " is " + a[index]);
++index;
if (index < a.length) {
setTimeout(update, 200);
}
}
Below you've said:
Unfortanetly I cant apply at my example.... I have a main loop with 2000 reads on local database.... I have done it works using timeouts but now my code spends a lot of time
I usually deal with that by breaking things up in to chunks (or see below for another alternative), example: Live Copy | Live Source
var index;
var total = 10000; // 10,000 to do in total
index = 0;
update();
function update() {
var limit = Math.min(index + 100, total); // Do them 100 at at time
while (index < limit) {
if (index % 10 == 0) {
display("Process entry #" + index);
}
++index;
}
if (limit < total) {
setTimeout(update, 200);
}
}
Choose the size of the chunks so that you're updating (yield) often enough, but not updating (yielding) so much that you lose too much time. The above does it based on the number of loops, but another way is to let yourself run for (say) one full second and then yield. You can get a lot of work done in a second.
The other alternative is to use web workers, at least on the browsers that support them. My other answer here on Stack Overflow has a discussion and example.
Even if you use web workers, though, you'll probably want to break the work into chunks, because if you have 2,000 records to get through, it doesn't make sense to update the progress bar 2,000 times &mdasdh; you'll just do updates a human can't readily perceive. 100 updates would be more than enough, probably even just 20 (so, 100 records/chunk) would be fine.

Categories

Resources