I have a small program, when you click on an "entry", the editing mode is opened, and the entry is to edit locked for others. There is every 10 seconds sends an ajax request to update the timestamp in the table.
$(".entry-edit").click(function() {
// code
loopLockingVar = setInterval(function() { loopLockingFunction(id) }, 10000);
// code
});
Then I have a cancel button to updating the timestamp in the table to 0 and to clear the interval.
$(".entry-cancel").click(function() {
// code
clearInterval(loopLockingVar);
// code
});
It all works when editing only one entry, but if two or more processed simultaneously, and then click cancel, the interval for the first entry still further...
I have this tried:
var loopLockingVar;
$(".entry-edit").click(function() {
// code
if( ! loopLockingVar) {
loopLockingVar = setInterval(function() { loopLockingFunction(id) }, 10000);
}
// code
});
However, this does not work more if you cancel and again clicks on edit...
You're assigning multiple interval IDs to the same variable which will only hold the interval ID that was assigned to it last. When you clear the interval, only the interval corresponding to that ID will be cleared.
A straightforward solution would be to maintain an array of interval IDs, and then clear all intervals represented in the array. The code could look something like this:
var intervalIds = [];
$(".entry-edit").click(function() {
intervalIds.push(setInterval(function() { loopLockingFunction(id) }, 10000));
});
$(".entry-cancel").click(function() {
for (var i=0; i < intervalIds.length; i++) {
clearInterval(intervalIds[i]);
}
});
maybe you can try like this.
var loopLockingVar;
$(".entry-edit").click(loopLockingVar,function() {
// code
loopLockingVar = setInterval(function() { loopLockingFunction(id) }, 10000);
// code
});
Related
My chrome extension is an automation tool that clicks through several buttons on several pages.
I use this function to wait for a pre-determined time before clicking on the button on the second page.
function sleep(ms) {
return new Promise((accept) => {
setTimeout(() => {
accept();
}, ms);
});
}
This function gets all the items on my profile page
function getAllItems() {
return document.getElementsByClassName('Item');
I then click on each item and click the save button on the item page
async function main() {
// Gets all items on my page.
let allItems = getAllItems();
for (let i = 0; i < allItems.length; i++ ) {
let currentItem = allItems[i];
// click on first item on page
currentItem.click();
// wait for 3 seconds in order to get save button to load
await sleep(3000);
function getSaveButton() {
return getButtonByClassName('Save');
let saveButton = getSaveButton();
saveButton.click();
}
}
My problem is that sometimes the 3000 seconds is not enough time for the save button to load and the loop ends.
I don't want to put the pause up to anything significantly higher, as obviously that slows the whole process down.
Since you mentioned in your comments that you want to wait for the button to appear, as long as necessary, you should use a setInterval to check the webpage once in a given interval if the button has appeared - so in your case:
function main() {
// Gets all items on my page.
let allItems = getAllItems();
for (let i = 0; i < allItems.length; i++ ) {
let currentItem = allItems[i];
// click on first item on page
currentItem.click();
// check every 3 seconds if the save button is loaded. When loaded, click it.
const intervalId = setInterval(() {
const saveButton = getButtonByClassName('Save');
if (saveButton) {
saveButton.click();
clearInterval(intervalId);
}
}, 3000);
}
You can probably use a smaller interval obviously :). Also note that in this case, the function is no longer "async".
I have a jQuery datatable that immediately loads ON READY. After that, the datatable is reloaded every 30 seconds. This feature is functioning properly.
I have added a search feature that automatically reloads the datatable with new search results. This part is also functioning properly.
The problem I am experiencing is when I am using the search feature, and the new search results are returned. After 30 seconds, the new results are cleared and the datatable reloads with all of the original records.
Here is what I am currently attempting:
$(document).ready(function()
{
var searchCriteria = "";
displayBookings(searchCriteria);
var idle = 0;
var idleInterval = setInterval(timer, 30000);
$(this).mousemove(function(e){idle = 0;});
$(this).keypress(function(e){idle = 0;});
function timer()
{
idle = idle + 1;
if(idle > 2)
{
displayBookings(searchCriteria);
console.log('table reloaded');
}
}
$('#searchPending').on('click', function()
{
var isPending = 'Y';
var searchCriteria = {
isPending: isPending
};
displayBookings(searchCriteria);
});
});
The function displayBookings() takes searchCriteria. If searchCriteria is blank, then a basic query is fired. Obviously is searchCriteria contains parameters, then the same query is fired with a WHERE clause attached. I did not disclose the code for displayBookings().
All I need to do is stop the 30 second interval if the #searchPending button is clicked.
Clear the interval so it will stop loading.
clearInterval(idleInterval)
specifically in your code:
$('#searchPending').on('click', function()
{
clearInterval(idleInterval)
var isPending = 'Y';
var searchCriteria = {
isPending: isPending
};
displayBookings(searchCriteria);
});
Rather than start and stop the timer interval, since you'll run into a bit of a race condition, you can just have the "refresh" (your "timer" function) refresh using the latest search criteria. To do this, just pass the same object into your displayBookings function. E.g.
const search = { criteria: "" };
$(...).click(() => {
search.criteria = 'change it...';
displayBookings(search.criteria);
});
setInterval(() => displayBookings(search.criteria), 30000);
This way, if a refresh happens, it will use the latest search.criteria. You can achieve the same result with minimal change in your code by simply removing the var from the second searchCriteria. Currently, without removing the var, your outer criteria is being "shadowed" by your inner.
I alluded to debouncing1 in one of my comments. I misread the code and debouncing is not what you want. Instead, you want to only "refresh" if there hasn't been any user activity within some threshold. Here's an alternative from the approach you used:
let lastInteraction = 0;
function interact() {
lastInteraction = Date.now();
}
$(this).mousemove(interact);
$(this).keypress(interact);
Then in your refresh function:
if (Date.now() - lastInteraction > threshold) { ...
Implementing both the central criteria and revised idle check:
$(document).ready(function() {
const idle = {
threshold: 1000,
lastInteraction: 0,
interact() {
idle.lastInteraction = Date.now();
},
isIdle() {
return Date.now() - idle.lastInteraction > idle.threshold;
}
};
const search = { criteria: "" };
$(this).mousemove(idle.interact);
$(this).keypress(idle.interact);
setInterval(() => {
if (idle.isIdle()) {
displayBookings(search.criteria);
}
}, 30000);
$('#searchPending').on('click', () => {
search.criteria = { isPending: 'Y' };
displayBookings(search.criteria);
});
displayBookings(search.criteria);
});
1 The Wikipedia article linked to discusses debouncing with a keyboard. It's the same concept. You'd use debouncing on your displayBookings function if you plan on having it execute live as the user is typing. This would prevent too many HTTP requests from happening in a short duration of time.
I'm trying to loop through a date range and delay going to the next iteration by 1 second. Each time I run the following in either jsFiddle or Plunker the browser crashes.
var current_date = new Date("01/13/2013");
var end_date = new Date("01/20/2013");
var end_date_time = end_date.getTime();
while (current_date.getTime() < end_date_time) {
setTimeout(function () {
console.log(current_date);
current_date.setDate(current_date.getDate()+1);
}, 1000);
}
Could anyone point me in the right direction as to why this isn't working and how to fix it?
You have a blocking loop here. It blocks the whole browser (possibly forever!).
while (current_date.getTime() < end_date_time) {
// do something (it doesn't matter what)
}
What you need is setInterval:
var current_date = new Date("01/13/2013");
var end_date = new Date("01/20/2013");
var end_date_time = end_date.getTime();
var interval = setInterval(function () {
if (current_date.getTime() >= end_date_time) {
clearInterval(interval);
}
console.log(current_date);
current_date.setDate(current_date.getDate()+1);
}, 1000);
(I didn't check the code for correctness)
Why does it block the UI?
While javascript code is running, the user can't interact with the website and often with the whole browser.
When you look at what the browser was doing over time the while() approach looks like
[while][while][while][while][while][while][while][while][while][while][while]
the interval approach looks like
[interval] [interval] [interval]
Only in the free time the browser is able to do other things, for example handling user interaction.
You can simply call a timeout if a condition fails:
function incrementDate(currentDate, endDateTime) {
currentDate.setDate(currentDate.getDate()+1);
if (currentDate.getTime() < endDateTime) {
setTimeout(function() {
incrementDate(currentDate, endTime);
}, 1000);
}
}
incrementDate(current_date, end_date_time);
I'm trying to store my script that counts numbers starting from 23,000 to always continue to appear it's "live" and always counting using Web Storage. I've tried implementing this and so far, I can't seem to get it to work. What would be the best solution to get this to work and function to always count even when refreshing, etc? I've included my JS Fiddle and code below. Any help is kindly appreciated!!
EDIT: To clarify.. I'm trying to have a "live" counter always going no matter what when you go to the page, refresh it, whatever. It's just always going and getting bigger no matter what just like my script does.. However, everytime I refresh it starts back at 23,000.
HTML
<span id="liveNumbers">23,000</span>
JS
if(typeof(Storage)!=="undefined")
{
setInterval(function(){
random = (Math.floor((Math.random()*2)+1));
var plus = Math.random() < 0.5 ? 1 : 1;
random = random * plus;
currentnumber = document.getElementById('liveNumbers');
var curnum = parseInt(currentnumber.innerHTML.replace(",",""));
document.getElementById('liveNumbers').innerHTML =
commaSeparateNumber(curnum + random);
}, 3000);
function commaSeparateNumber(val){
while (/(\d+)(\d{3})/.test(val.toString())){
val = val.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
}
return val;
}
}
else
{
// Sorry! No Web Storage support..
}
Here's my attempt: fiddle
The logic:
On first visit (no localStorage data) the counter is reset to 23000.
Counter runs while page is open.
When closing the page, the current counter value is stored together with the current timestamp (lastSessionEnd).
When user loads the page again, the time that has passed since he closed the page is translated into interval cycles which are passed to the randomRange function and added to the stored counter from the last session.
Here's the code:
if(window.localStorage) {
//configs
var updateInterval = 3000; //ms
function randomRange() {
return Math.floor(Math.random()*3)+1; // [1..3] range
}
var counter = +localStorage.getItem('counter');
if (!counter) { //first load
counter = 23000;
} else { //simulate randomness that would have happened while the user was away from the page
var lastSessionEnd = +localStorage.getItem('lastSessionEnd');
for(var l = Math.floor((getUnixTimeStamp() - lastSessionEnd)*1000/updateInterval); l--;) {
counter += randomRange();
}
}
var liveNumbers = document.getElementById('liveNumbers'); //cache DOM query
function refreshDisplay() {
liveNumbers.innerHTML = commaSeparateNumber(counter);
}
refreshDisplay();
setInterval(function() {
counter += randomRange();
refreshDisplay();
}, updateInterval);
function commaSeparateNumber(val) {
while (/(\d+)(\d{3})/.test(val.toString())){
val = val.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
}
return val;
}
function getUnixTimeStamp() {
return Math.floor(Date.now()/1000);
}
window.addEventListener('beforeunload', function() {
localStorage.setItem('counter', counter);
localStorage.setItem('lastSessionEnd', getUnixTimeStamp());
});
} else {
// Sorry! No Web Storage support..
}
NOTE: this is not perfect, here are the caveats:
As it is done purely in the front-end, it is easily hackable by manipulating the localStorage. Don't use this for important stuff.
As it uses the localStorage API, if the user opens the page in more than one browser (or more than one computer/device), each one will have a different counter. Also, cleaning all personal data will reset the counter.
Finally, there's an interval cycle rounding error, it doesn't account for interrupted interval cycles. E.g. the user closes the page midway through an interval cycle, the next time he opens the page that half-cycle will be discarded and a new one starts. I believe this is a small detail which would take more effort to fix than it's worth, but I'll leave that decision and effort to you.
Hi I am making one site in RubyOnRails. I am having problem in showing some content at client side. What I want to do is like news where after every 10 sec., news would change. What I have done is I have make an ajax which fetch the news from my server, server returns array in json response. Now I have all the news array at client side I want to show one by one in 10 sec interval.
I have tried with this code but its not showing anything except last news.
function get_news(){
$.ajax({
url : "my.json",
success:function(data){
// data is array of json like [{"html" : "dfsf"},{"html":"ddd"}]
news_change(data);
}
});
}
get_news();
function news_change(feed){
$.each(feed,function(index,f){
var feed_html = f.html;
$('#news_div').fadeOut('slow', function(){
$('#news_div').html(feed_html);
$('#news_div').fadeIn('slow');
});
sleep(10000);
});
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
when I execute this code it only shows news which is last. And also it hang my browser. Please suggest me why is this because ?
Use setTimeout or setInterval, which execute code asynchronously after a certain number of milliseconds. Looping is synchronous and locks the browser while it executes.
// this will execute get_news every 10,000 ms
var newsInterval = setInterval(get_news, 10000);
// if you ever want to stop the interval, use clearInterval
clearInterval(newsInterval);
Note that get_news performs an ajax call, which could take some time, meaning that your news will not update exactly every 10 seconds.
EDIT: to iterate through the news array every 10 seconds, you'd pass the news_change function to setInterval:
var newsInterval;
function get_news(){
$.ajax({
url : "my.json",
success:function(data) {
newsInterval = setInterval(function () {
news_change(data);
}, 10000);
}
});
}
get_news();
// if you ever want to stop the interval, use clearInterval
clearInterval(newsInterval);