javascript/jquery clearInterval works but with delay - javascript

I have a flask-environment running server-sided, which calculates the distance between it's current position (GPS-Module attached to the server) and given coordinates. I want a text to update when a certain event occurs. It should keep updating until a different event occurs.
This is my function to update the distance:
function getDistToCurPos(lat, lon) {
$.getJSON('/getDistFromCurPosToPoint', {
pLat: lat,
pLon: lon,
},
function(data){
dist = data;
let distStr = String(dist);
let outStr = '<div class="distanceOutput"> Distance: '.concat(distStr).concat("</div>");
$( "div.distanceOutput" ).replaceWith( outStr );
});
};
When the start-event occurs, I call the function as follows (with example-values 38 and 9):
distRefreshID = setInterval(function() { getDistToCurPos(38,9); }, distUpdateTime);
distUpdateTime is set to 500. The server response takes 15 ms in average.
Initial state of the text:
<div class="distanceOutput">Inactive !</div>
To terminate updating the text, I call the following, when a different event occurs:
if ( distRefreshID !== 0) {
clearInterval(distRefreshID);
$( "div.distanceOutput" ).replaceWith('<div class="distanceOutput">Inactive again!</div>');
distRefreshID = 0;
}
The code works in general. But when the stop-event occurs, I see "Inactive again!" only for a short moment. After that, the text keeps getting updated with the current values for a few seconds.
Any tips, where my bug is ?

setInterval does not initially call the function, it calls it after x milliseconds have passed so if you want to instantly update the content call the function right before setting the interval
getDistToCurPos(38,9);
distRefreshID = setInterval(function() { getDistToCurPos(38,9); }, distUpdateTime);

Related

setTimeout executes itself right away/on clear

I'm making a webpage where user events are logged in.
To test the feature I made a small, independant webpage with a teaxtarea and a text input. The events logged are those performed on the input element.
I want to prevent the same event text to be shown multiple times in a row, but I can't seem to prevent them from showing up!
I also want to add a line to separate event groups 0.5 seconds after no other event happened, but the line seems to appear on every event trigger, evenif I use clearTimeout with the timeout ID.
Basically: I don't want any line to be repeated. If the last line is a separator line, then it must not add another one. Yet it doesn't see to work.
JSFiddle Demo
Here is my code:
JavaScript
var timerID = 0;
function addSeparateLine()
{
document.getElementById('listeEvenements').value += "--------------------\n";
}
function show(newEventText)
{
var eventListField = document.getElementById('listeEvenements');
var eventList = [];
if (eventListField.value.length > 0)
{
eventList = eventListField.value.split("\n");
}
var eventCounter = eventList.length;
if (eventList[eventCounter - 2] == newEventText)
{
clearTimeout(timerID);
newEventText = "";
}
timerID = setTimeout(addSeparateLine, 500);
if (newEventText !== "")
{
eventListField.value += newEventText + "\n";
}
return true;
}
HTML
<fieldset id="conteneurLogEvenements">
<legend>Events called from HTML attribute</legend>
<textarea id="listeEvenements" rows="25"></textarea>
<input id="controleEcoute" type="text" onBlur="show('Blur');" onchange="show('Change');" onclick="show('Click');" onfocus="show('Focus');" onMousedown="show('MouseDown');" onMousemove="show('MouseMove');" onMouseover="show('MouseOver');" onkeydown="show('KeyDown');"
onkeypress="show('KeyPress');" onkeyup="show('KeyUp');" />
</fieldset>
http://jsfiddle.net/z6kb4/2/
It sounds like what you want is a line that prints after 500 milliseconds of inactivity, but what your code currently says to do is "print a line 500 milliseconds after any action, unless it gets canceled". You can get better results by structuring the code more closely to your intended goal.
Specifically, instead of scheduling a new timeout every time an event occurs, simply start a loop when the first event occurs that checks the time that has elapsed since the most recent event received and then prints a line when the elapsed time exceeds the desired threshold (500 milliseconds). Something like:
function addSeparateLine() {
var elapsed = new Date().getTime() - lastEventTime;
if (elapsed >= 500) {
document.getElementById('listeEvenements').value += "--------------------\n";
clearInterval(timerID);
timerID = -1;
}
}
...and then you schedule it like:
if(newEventText !== "") {
lastEventTime = new Date().getTime();
eventListField.value += newEventText+"\n";
if (timerID == -1) {
timerID = setInterval(addSeparateLine,100);
}
}
Working example here: http://jsfiddle.net/z6kb4/4/
Because you are not actually stopping the show function in any way. The clearTimeout only applies to the separator add. I have updated your fiddle. You need to wrap your function with
if (+new Date() - lastfire < 500) return;
and
lastfire = +new Date();
(before the last return--see the updated fiddle). Also, make sure to stick the global definition var lastfire = -1; somewhere up top.

jQuery getting tag's content dynamically

First of all, I know my question seems to be already asked many many times but I'm facing a weird issue.
Here's the situation :
I've got an integer (dynamically loaded) in this tag :
<i id="my_id">{{here's my integer}}</i>
What I want to do is to retrieve the integer inside my tag but this integer is set to 0 at first (When the page isn't fully loaded") and then 2 or 3 seconds later, this integer is set to its real value.
So I tried something like this :
var test = 0;
$('#my_id').change(function(){
test = $('#my_id').html();
});
console.log(test);
This always returns me 0. I tried many things to get the current value of my tag but I can't find a way to succeed. Can you please help me get this integer ?
Cordially, Rob.
The change event is only fired by input elements. You can try polling the value like so:
var intervalId = setInterval(function() {
var value = parseInt($('#my_id').text(), 10);
if(value > 0) {
clearInterval(intervalId);
//... do stuff
}
}, 250); //poll every 250ms
Another way is to fire a custom event when you change the value:
//Somewhere in your code where you set the value in the i tag:
$('#my_id').text(value);
$('#my_id').trigger("valueChanged");
//Elsewhere in your code
$('#my_id').on("valueChanged", function() {
var value = parseInt($(this).text(), 10);
if(value > 0) {
//... do stuff
}
});

Waiting until animation is over until replacing data inside the div [duplicate]

This question already has answers here:
How to wait for one jquery animation to finish before the next one begins?
(6 answers)
Closed 1 year ago.
I am having trouble changing the contents of a div. What I want to achieve is wait for the animation to end and then change the content to launch the animation to reveal the new info. This code works from time to time, meaning sometimes it hangs (isWorking never becomes false, because animation freezes or animation never has the time to finish from the constant looping inside the while.) Is there a way to wait for the animation to end and then change the content? (The way you see it below allows me to browse for the new content while the animation is ongoing which saves time for the end user.) Is there a way to catch when the animation ends?
function DesignResults(InnerHTML) {
while(isWorking){
}
$("#holder").html(InnerHTML);
ShowSearch(true);
}
var isWorking = false;
function ShowSearch(show) {
isWorking = true;
var outer = $("#outer");
var inner = $("#inner");
var height = 0;
if (show == true)
height = inner.outerHeight();
var loaderHeight
if (height > 0) {
loaderHeight = 0;
} else {
loaderHeight = 31;
}
outer.animate({
'height': height + "px"
}, 1600, function () {
$("#loading").animate({ 'height': loaderHeight + "px" }, 900, function () { });
isWorking = false;
});
}
I understand that $(elem).is(':animated') will give me if the animation is still in progress, but that still freezes everything due to the constant looping of the while. Can anyone point me to the right direction?
EDIT:
I guess I am misunderstood. Here is the plan I want to achieve:
Start hiding animation.
While the animation is hiding I am launching another function to get the content
If I get the content faster I wish to wait for the animation to end then change the content and show the layer again.
There isn't a issue here if the data takes more than a second to return
I agree that i can do it after the animation completes as you suggested, but I have put the animation to almost 1 second execute time and that time can be used for the data to be pulled from the database. I am looking for the most effective code.
The last parameter in this function:
$("#loading").animate({ 'height': loaderHeight + "px" }, 900, function () { });
Is the 'complete' function. This function is called when the animation is complete.
So, you can do something in this function when it's complete, like so:
$("#loading").animate({ 'height': loaderHeight + "px" }, 900, function () {
alert('animations complete!');
});
----Edit----
Based on your edits, you probably want to do something like this.
Have a variable that will let you know when both are finished:
var signal = 0;
Have a function to change your content with this in it:
function changeContent() {
signal++;
if (signal === 2) {
// change the content code here
signal = 0;
}
}
When the animation is finished, call:
changeContent();
When you've grabbed the data, call:
changeContent();
When changeContent() is called the first time by either function, it will increment signal to 1 and exit the function. Then on the second time, it will increment signal to 2. Since signal === 2, you know that both the animation and grabbing the data is complete, so you can now change your content.
The good part about using an integer to do this, is if you want to have 3, 4, 5+ functions finish working before changing your content. In this case you just have to change the condition by increasing the number in if (signal === 2).
Just add the additional task here
$("#loading").animate({ 'height': loaderHeight + "px" }, 900, function () { /* Change Content */ });
Than it would be execute after the animation

Send Geolocation in background service [Titanium]

I'm creating an app for Android and iOS with titanium which sends a new Geolocation to a server every 5 seconds for as long as the server runs. On iOS however the app stops sending those locations after a random interval. Although i myself are not completely convinced this is due to the fact that the app pauses in iOS(Since it stops randomly and not on a fixed time) i'm still eager to try and be certain.
However; i really have NO CLUE to do this whatsoever. I've created a background service in an eventListener to see what happens and it starts logging right away(I've put a console log in it for now). Nonetheless, my geolocation is still ticking normally aswell.
Now, could someone please give me some pointers on how to get through this? Do i want to stop my normal geolocation listener and let the BG service take over? Or does the BGservice keep the geolocation eventlistener in my normal code active now?
At this point i'm afraid to say that i'm pretty much desperate to get any help, haha!
Here's my geolocation handling now, along with the BGservice:
//Start button. listens to an eventHandler in location.js
btnStart.addEventListener('click', function (e) {
if( Titanium.Geolocation.locationServicesEnabled === false ) {
GPSSaved.text = 'Your device has GPS turned off. Please turn it on.';
} else {
if (Titanium.Network.online) {
GPSSaved.text = "GPS zoeken...";
//Empty interval and text to make a clean (re)start
clearInterval(interval);
//Set a half second timer on the stop button appearing(So people can't double tap the buttons)
stopTimeout = setTimeout(showStopButton, 1000);
//Switch the textlabels and buttons from startview to stopview
stopText.show();
startText.hide();
btnStart.hide();
//Locationhandler
location.start({
action: function (e) {
//Ti.API.info(e.coords.longitude);
if (e.coords) {
//If the newly acquired location is not the same as the last acquired it is allowed
if (e.coords.longitude != lastLon && e.coords.latitude != lastLat) {
//set the last acquired locations+other info to their variables so they can be checked(and used)
lastLat = e.coords.latitude;
lastLon = e.coords.longitude;
lastKnownAltitude = e.coords.altitude;
lastKnownHeading = e.coords.heading;
lastKnownSpeed = e.coords.speed;
if (lastLat != 0 && lastLon != 0) {
setGPSholder(lastLat, lastLon, lastKnownAltitude, lastKnownHeading, lastKnownSpeed);
} else {
GPSSaved.text = 'Geen coordinaten.';
}
}
}
}
});
/*
A second interval which shows a counter to the user and makes sure a location is sent
roughly every 5 seconds(setInterval isn't accurate though)
A lot of counters are tracked for several reasons:
minuteInterval: Counter which makes sure the last location is sent after a minute if no new one is found in the meantime
secondsLastSent: The visual counter showing the user how long its been for the last save(Is reset to 0 after a succesful save)
*/
interval = setInterval(function () {
minuteInterval++;
minuteIntervalTest++;
secondsLastSent++;
counterBlock.text = "De laatste locatie is " + secondsLastSent + " seconden geleden verstuurd";
//If the counter is higher than 5 send a new coordinate. If at the same time the minuteInterval is over a minute
//The last location is put in the array before calling the sendCoordinates
if (counter >= 5) {
if (minuteInterval > 60) {
if (lastLat != 0 && lastLon != 0) {
setGPSholder(lastLat, lastLon, lastKnownAltitude, lastKnownHeading, lastKnownSpeed);
}
}
counter = 0;
sendCoordinates();
Ti.API.info(1);
} else {
counter++;
}
if (minuteIntervalTest > 60) {
sendTestRequest();
}
}, 1000);
if (Titanium.Platform.osname == 'iphone' || Titanium.Platform.osname == 'ipad') {
//var service = Ti.App.iOS.registerBackgroundService({url:'send_geolocation_service.js'});
var service;
// Ti.App.iOS.addEventListener('notification',function(e){
// You can use this event to pick up the info of the noticiation.
// Also to collect the 'userInfo' property data if any was set
// Ti.API.info("local notification received: "+JSON.stringify(e));
// });
// fired when an app resumes from suspension
Ti.App.addEventListener('resume',function(e){
Ti.API.info("app is resuming from the background");
});
Ti.App.addEventListener('resumed',function(e){
Ti.API.info("app has resumed from the background");
// this will unregister the service if the user just opened the app
// is: not via the notification 'OK' button..
if(service!=null){
service.stop();
service.unregister();
}
Titanium.UI.iPhone.appBadge = null;
});
Ti.App.addEventListener('pause',function(e){
Ti.API.info("app was paused from the foreground");
service = Ti.App.iOS.registerBackgroundService({url:'send_geolocation_service.js'});
Ti.API.info("registered background service = "+service);
});
}
} else {
stopGPS();
GPSSaved.text = "Geen internetverbinding.";
}
}
});
As you can see there's some counters running in an interval to decide if a geolocation should be sent every 5 seconds or every minute(If no new location since the last is found)
tl;dr: I want geolocations to be sent every 5 seconds but somehow iOS(iPhone 4s and 5 tested) stop sending after a random timeperiod and restart sending the moment i get the phone out of standby.
actually background service has a limitation to stop after 10 mins so if you want to catch location when device is in background mode then you need to set mode tag in tiapp.xml file.
just refer this online doc for how it works.
http://docs.appcelerator.com/titanium/3.0/#!/guide/tiapp.xml_and_timodule.xml_Reference-section-29004921_tiapp.xmlandtimodule.xmlReference-LegacyiPhonesection

Same function repeatedly called resets the setTimeout inner function

All,
I have a 'credit module' (similar to credit system in games), which when a user performs an action, creates an inner div with the cost to be added or substracted so user can see what the cost of the last action was.
Problem: Everything works fine as long as the function is called once, if the user performs multiple actions quickly, the setTimeout functions (which are suppose to animate & then delete the cost div) donot get executed. It seems the second instance of the function resets the setTimeout function of the first.
(function()
{
$("#press").on("click", function(){creditCost(50)});
function creditCost(x)
{
var eParent = document.getElementById("creditModule");
// following code creates the div with the cost
eParent.innerHTML += '<div class="cCCost"><p class="cCostNo"></p></div>';
var aCostNo = document.getElementsByClassName("cCostNo");
var eLatestCost = aCostNo[aCostNo.length - 1];
// following line assigns variable to above created div '.cCCost'
var eCCost = eLatestCost.parentNode;
// cost being assigned
eLatestCost.innerHTML = x;
$(eCCost).animate ({"left":"-=50px", "opacity":"1"}, 250, "swing");
// following code needs review... not executing if action is performed multiple times quickly
setTimeout(function()
{
$(eCCost).animate ({"left":"+=50px", "opacity":"0"}, 250, "swing", function ()
{
$(eCCost).remove();
})
}, 1000);
}
})();
jsfiddle, excuse the CSS
eParent.innerHTML += '<div class="cCCost"><p class="cCostNo"></p></div>';
is the bad line. This resets the innerHTML of your element, recreating the whole DOM and destroying the elements which were referenced in the previous invocations - letting their timeouts fail. See "innerHTML += ..." vs "appendChild(txtNode)" for details. Why don't you use jQuery when you have it available?
function creditCost(x) {
var eParent = $("#creditModule");
// Create a DOM node on the fly - without any innerHTML
var eCCost = $('<div class="cCCost"><p class="cCostNo"></p></div>');
eCCost.find("p").text(x); // don't set the HTML if you only want text
eParent.append(eCCost); // don't throw over all the other children
eCCost.animate ({"left":"-=50px", "opacity":"1"}, 250, "swing")
.delay(1000) // of course the setTimeout would have worked as well
.animate ({"left":"+=50px", "opacity":"0"}, 250, "swing", function() {
eCCost.remove();
});
}
You are starting an animation and scheduling a timeout to work on DOM elements that will get modified in the middle of that operation if the user clicks quickly. You have two options for fixing this:
Make the adding of new items upon a second click to be safe so that it doesn't mess up the previous animations.
Stop the previous animations and clean them up before starting a new one.
You can implement either behavior with the following rewrite and simplification of your code. You control whether you get behavior #1 or #2 by whether you include the first line of code or not.
function creditCost(x) {
// This first line of code is optional depending upon what you want to happen when the
// user clicks rapid fire. With this line in place, any previous animations will
// be stopped and their objects will be removed immediately
// Without this line of code, previous objects will continue to animate and will then
// clean remove themselves when the animation is done
$("#creditModule .cCCost").stop(true, false).remove();
// create HTML objects for cCCost
var cCCost = $('<div class="cCCost"><p class="cCostNo">' + x + '</p></div>');
// add these objects onto end of creditModule
$("#creditModule").append(cCCost);
cCCost
.animate ({"left":"-=50px", "opacity":"1"}, 250, "swing")
.delay(750)
.animate({"left":"+=50px", "opacity":"0"}, 250, "swing", function () {
cCCost.remove();
});
}
})();
Note, I changed from setTimeout() to .delay() to make it easier to stop all future actions. If you stayed with setTimeout(), then you would need to save the timerID returned from that so that you could call clearTimeout(). Using .delay(), jQuery does this for us.
Updated code for anyone who might want to do with mostly javascript. Jsfiddle, excuse the CSS.
function creditCost(x)
{
var eParent = document.getElementById("creditModule");
var eCCost = document.createElement("div");
var eCostNo = document.createElement("p");
var sCostNoTxt = document.createTextNode(x);
eCCost.setAttribute("class","cCCost");
eCostNo.setAttribute("class","cCostNo");
eCostNo.appendChild(sCostNoTxt);
eCCost.appendChild(eCostNo);
eParent.insertBefore(eCCost, document.getElementById("creditSystem").nextSibling);
$(eCCost).animate ({"left":"-=50px", "opacity":"1"}, 250, "swing");
setTimeout(function()
{
$(eCCost).animate ({"left":"+=50px", "opacity":"0"}, 250, "swing", function ()
{
$(eCCost).remove();
})
}, 1000);
}

Categories