Ensure only one setTimeout runs (is active) at a time? - javascript

The recursive setTimeout function getRandomProducts is called onload in the html body tag, and so is constantly iterating.The function setCategoryTree is being called onclick from the links in a nested ul of a navigation-bar. This function then passes the variable mainCategory to getRandomProducts, in which the variable is declared globally to maintain its' initialization....So, what I am trying to do is reset the getRandomProducts function when a link is clicked in the navigation-bar, and pass the category name from the link that was clicked. However, clearTimeout does not seem to be working and so the iterations occur a lot more frequently as there are then multiple recursive loops executing simultaneously.And not only that, but my global variables are not storing data from setCategoryTree as intended (basically I am trying to use the global variable similarly to a static-variable). I have discerned all of this behavior with the window.alert that is commented out.Here is the relevant Javascript code:
var mainCategory = ""; // initialized from setCategoryTree
var category = mainCategory;
function getRandomProducts(category)
{
//window.alert(category);
if(typeof category == "undefined")
category = "all";
else
clearTimeout(t);
var req = new XMLHttpRequest();
var products = document.getElementById("products");
req.onreadystatechange = function()
{
if( (req.readyState == 4) && (req.status == 200) )
{
var result = req.responseText;
products.innerHTML = result;
}
}
req.open("GET", "default.php?category=" + category, true);
req.send(null);
var t = setTimeout("getRandomProducts()", 1000);
}
function setCategoryTree(link)
{
var categoryTree = document.getElementById("categoryTree");
/* climbing the DOM-tree to get the category name (innerHTML of highest "a" tag) */
mainCategory = link.parentNode.parentNode.parentNode.getElementsByTagName("a")[0].innerHTML;
var subcategory = link.innerHTML;
categoryTree.innerHTML = "-- " + mainCategory + " -- " + subcategory;
getRandomProducts(mainCategory);
}

You are setting the variable t within the function. The next time this function gets called the var t will not be available for you.
Therefore, Set the variable above the function (not in it)
var randomProductsTimeout = false;
function getRandomProducts(category){
randomProductsTimeout = setTimeout()[..]

Related

Object in jquery not set on submit

I would like to get inputs values of a form and place in an object (for an offer).
So i tried to place this code on submit :
$(document).ready(function(){
$('#formOffre').on('submit', function(e) {
e.preventDefault();
console.log(Offre); // give undefined in console
if ( typeof Offre == 'undefined'){
// if undefined, create object
var Offre = {
BuyerID: 1, //I will handle this later
Total: 0,
OffreItem: [] //array with json objects
};
Offre.OffreItem.id = 0;
console.log("object created");
for (i=0; i > Offre.OffreItem.id ; i++) {
Offre.OffreItem.modele = formOffre.modele.value;
Offre.OffreItem.longueur = formOffre.longueur.value;
Offre.OffreItem.hauteur = formOffre.hauteur.value;
Offre.OffreItem.qte = formOffre.qte.value;
Offre.OffreItem.rix = formOffre.prix.value;
console.log("getting parameters of inputs to offer");
}
} else {
//if object exists ony get informations of inputs
Offre.OffreItem.id = 0;
for (i=0; Offre.OffreItem.id < i; i++){
Offre.OffreItem.modele = formOffre.modele.value;
Offre.OffreItem.longueur = formOffre.longueur.value;
Offre.OffreItem.hauteur = formOffre.hauteur.value;
Offre.OffreItem.qte = formOffre.qte.value;
Offre.OffreItem.rix = formOffre.prix.value;
}
}
this is my code. when i click on submit for the first time, it go to the if statement and create the object. But when i click again, I go through the if statement like the object is not set.
i put a console log and in every case the object is undefined.
Can you someone help me please?
Thanks
You are checking Offre out side of scope in which it is defined .
enter coconsole.log(Offre); // give undefined in console
if ( typeof Offre == 'undefined'){
// if undefined, create object
var Offre = { //here is issue this should be above submit function
BuyerID: 1, //I will handle this later
Total: 0,
OffreItem: [] //array with json objects
};
Also make sure your page is maintaining state.
For you I have created example give a look. here
Hoping this will solve your problem.
Fiddle
The problem here is that you're defining your variable inside the function.
To simplify your code:
$('#formOffre').on('submit', function(e) {
if ( typeof Offre == 'undefined'){
var Offre = { }; // This variable is only accessible inside this function
} else {
//
}
}
The var Offre will define a variable within the scope of the function, the next time you run the function, a new variable with that name will be created (Meaning it will always be undefined initially)
To get around this, you can define your variable outside of the function:
var Offre;
$('#formOffre').on('submit', function(e) {
if ( typeof Offre == 'undefined'){
Offre = { }; // Notice that we're not creating a new variable here, just accessing the one defined above
} else {
//
}
}

Check whether function has run fully before, based on variable

I have a function which "types" out a header title as though it is being typed on the screen.
The typer only starts typing once a particular section of my site is "active" or is seen on the screen.
At present, it takes the outputID aka the area where this text will be typed into. There are two instances of this function being run, each with different outputIDs - I only want the function to run once per outputID.
This is how the function is initially called.
<h2 id="typer-get-in-touch" class="typer" data-text="Get in Toche^^^^^ Touch"></h2>
if(anchorLink == 'contact'){
var outputID = $("#typer-get-in-touch");
textTyping(outputID);
}else if(anchorLink == 'expertise'){
var outputID = $("#typer-expertise");
textTyping(outputID);
}
This is the textTyping function
function textTyping(outputID){
$(outputID).show();
var textString = $(outputID).data("text");
var textArray = textString.split("");
var texttypeing = setInterval(
function() {
typeOutText(outputID,textArray);
}, 170);
function typeOutText(outputID,textArray) {
if (textArray[0] == "^"){
outputID.text(function(index, text){
return text.replace(/(\s+)?.$/, '');
});
textArray.shift();
}else {
if (textArray.length > 0) {
outputID.append(textArray.shift());
} else {
clearTimeout(texttypeing);
}
}
}
}
My issue at present is that the function runs multiple types, and continues to type each time the original anchorLink trigger is achieved. The result is that is writes the title many times e.g:
Get In TouchGet In TouchGet In Touch
Each time the section is navigated to, the typing starts again.
How can I run this function only ONCE per outputID? So once the outputID has been used, the function can no longer run for that data?
JSFiddle of non-working example: https://jsfiddle.net/qLez8zeq/
JSFiddle of mplungjan's solution: https://jsfiddle.net/qLez8zeq/1/
Change
function textTyping(outputID){
$(outputID).show();
var textString = $(outputID).data("text");
to
function textTyping(outputID){
var textString = $(outputID).data("text");
if (textString=="") return;
$(outputID).data("text","");
$(outputID).show();
FIDDLE
What you need to do is to bind the event handler for each ID and then unbind it after it's been triggered the first time. Since you're already using jQuery, you can use the "one" method to do exactly this for each outputID:
$( "#typer-get-in-touch" ).one( "click", function() {
textTyping(outputID);
});
I suppose you could store your processed outputIds into an array and then check if the given outputId is present in the array before starting?
Define your array, check for the existence, if not found, do code example:
var processedIds = [];
function textTyping(outputID) {
var foundItem = false;
for (var i = 0; i < processedIds.length; i++)
{
if (processedIds[i] == outputID) {
foundItem = true;
break;
}
}
if (!foundItem) {
//the rest of your code goes here
}
}
You can add some check at the beginning of your function:
var called = {};
function textTyping(outputID) {
if (called[outputID]) {
return;
}
called[outputID] = true;
// your code
}

How to access the values of local variables globally [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
I returned the rsltCallback function , when i call the googleSearchSuggestions function , i am getting undefined. When i console.log the input parameter inside the rsltCallback function it's printing the output to console.
var googleSearchSuggestions = function(search_Keyword , element) {
var parsed;
var uri = 'http://suggestqueries.google.com/complete/search?output=toolbar&hl=en&q=' + search_Keyword;
var xhr = (typeof XMLHttpRequest !== 'undefined') ? new XMLHttpRequest() : new ActiveXObject(Microsoft.XMLHTTP);
xhr.onreadystatechange = function(){
xhr.responseType = 'xml';
if(xhr.status == 200 && xhr.readyState == 4)
{
var response = xhr.responseXML;
var items = response.getElementsByTagName('toplevel')
for (var i = 0 ; i < items[0].childNodes.length;i++)
{
parsed = items[0].childNodes[i].firstElementChild.getAttribute('data');
rsltcallBack(parsed);
}
}
};
xhr.open('GET', decodeURI(uri), true);
xhr.send();
var rsltcallBack = function(input){
return input;
};
return rsltCallBack();
};
Because you have an asynchronous function call in your method you need to return the parsedValue at a later time. A way to do this are so called promises.
If you want that variable to be globally accessible then you can just add it to window (eg. window.parsedOutput = ...).
This incident as JavaScript Variable hosting issue. for ex:
var value = "a";
function test() {
var value;
alert(value)
}
test();
the global variable return undefined error. same code as works fine as below
var value = 10;
function test() {
var value ="b";
alert(value);
}
test();
online reference :
http://techslides.com/understanding-javascript-closures-and-scope
http://stackoverflow.com/questions/9085839/surprised-that-global-variable-has-undefined-value-in-javascript
An onreadystatechange function is only called after the state has been changed. This is not immediate; there is a split second in which the state has not yet changed. So you cannot access parsedOutput until the onreadystatechange function is called; its value has not yet been set. JavaScript doesn't just stop and wait for this to happen; it allows the request to be made in the background. It continues executing your code, even though the onreadystatechange method has not yet been called.
To solve your problem, simply wait for a response before using parsedOutput. You do this by using the onreadystatechange function to trigger the next step in your code.
BTW, performing a request in the background like this is call an asynchronous request. It is how you make remote requests with JavaScript; it prevents the script from hanging while waiting for a response.
Instead of accessing local variable, just initialize the variable outside the function and assign values locally,
Copy-Paste this,
var parsed ="";
var googleSearchSuggestions = function(search_Keyword , element) {
var uri = 'http://suggestqueries.google.com/complete/search?output=toolbar&hl=en&q=' + search_Keyword;
var xhr = (typeof XMLHttpRequest !== 'undefined') ? new XMLHttpRequest() : new ActiveXObject(Microsoft.XMLHTTP);
xhr.onreadystatechange = function(){
xhr.responseType = 'xml';
xhr.open('GET', decodeURI(uri), true);
xhr.send();
if(xhr.status == 200 && xhr.readyState == 4)
{
var response = xhr.responseXML;
var items = response.getElementsByTagName('toplevel')
for (var i = 0 ; i < items[0].childNodes.length;i++)
{
parsed = items[0].childNodes[i].firstElementChild.getAttribute('data');
rsltcallBack(parsed);
}
}
}
};
function rsltcallBack(input)
{
setTimeout(function()
{
alert(input);
},2000);
}
There are a few ways to declare global variables
var carName;
function myFunction() {
carName = 'Volvo';
}
This example declares the variable outside of the function and then assigns a value to it inside the function scope.
The other is
function myFunction() {
window.carName = 'Volvo';
}
This sets the variable on the window which can be deleted using delete window.carName.

Javascript - Passing function as string, then execute if it exists

I have a javascript library which does returns a list of results to a div triggered on keyup events. I want to use Jquery to apply a standard keyup event to all fields on all pages which have a certain class.
That part I can do and it works OK.
My issue is that the parameters which I use are dynamic and the last 2 of these are optional functions.
<input type="text"
class="font8_input" name="wsFromPart"
value="<%=wsFromPart%>" id="wsFromPart"
size="20" maxlength="20"
onfocus="javascript:fncAjaxClear()"
onkeyup="javascript:fncAjaxSearch('wsDatabase','..\\AjaxBrowses\\PartBrowse.asp','wsFromPart','wsFromPartList','fncPrePartAjax',null);"/>
At present, to control this I pass null if I don't have a function.
I'm trying to get the to a position where I can define all of the fields like this.
<input type="text" class="PartClass" name="wsFromPart" value="<%=wsFromPart%>" id="wsFromPart" />
Everything else will be added by setting classes/events by Jquery.
I'm trying to work out how I can test if a function exists on my page, and only execute if it does. I've tried passing the function name as a string but can't seem to make that work. My last attempt is to have a generic function, and pass the name of the function which may exist to this function to evaluate and, if a function, execute it.
<input type="text" class="font8_input" name="wsFrPt" id="wsFrPt" size="20" maxlength="20" value="<%=wsFrPt%>" onfocus="javascript:fncAjaxClear()" onkeyup="javascript:fncAjaxSearch('wsDatabase','..\\AjaxBrowses\\PartBrowse.asp','wsFrPt','wsFrPtList',fncCheckFunction('fncPreAjaxPart1'),'fncPostAjaxPart');"/>
function fncAjaxSearch(wsDb,wsAsp,wsId,wsTarget,wsPreFunction,wsReturnFunction) {
var myDate = new Date();
var myDate1 = myDate.getTime();
if (objXHR.readyState == 4 || objXHR.readyState == 0) {
var wsDatabase = escape(document.getElementById(wsDb).value);
var wsStr = escape(document.getElementById(wsId).value);
var wsParam = "";
if (wsPreFunction !== null) {
wsParam = wsPreFunction();
}
//Only do ajax call if the 'source' field is not empty, otherwise empty target and clear border.
if (document.getElementById(wsId).value > '') {
objXHR.open("GET", wsAsp + '?qryDatabase=' + wsDatabase + '&qryDummy=' + myDate1 + '&qrySearch=' + wsStr + wsParam, true);
objXHR.onreadystatechange = function(){if(objXHR.readyState==4){fncAjaxSearchReturn(objXHR,wsId,wsTarget,wsReturnFunction)}};
objXHR.send(null);
}
else {
document.getElementById(wsTarget).innerHTML = '';
document.getElementById(wsTarget).style.borderWidth = '0px';
}
}
}
If it is a global, you can do this
var fnc = window["yourFunctionName"]; //Use bracket notation to get a reference
if( fnc && typeof fnc === "function" ) { //make sure it exists and it is a function
fnc(); //execute it
}
If it is namespaced, you can do the same type of thing, just involves some looping.
var myFnc = "foo.bar.fun";
var nameParts = myFnc.split("."); //split up the string into the different levels
var fnc = window; //set it to window
for(var i=0;i<nameParts.length;i++){ //loop through each level of the namespace
fnc = fnc[nameParts[i]];
if(!fnc){ //make sure it exists, if not exit loop
fnc = null;
break;
}
}
if( fnc && typeof fnc === "function" ) { //make sure it exists and it is a function
fnc(); //execute it
}
You can check if function exists by:
if(typeof(yourFunctionName) == "function")
//...do your code
else
//...function not exists
if you passing the function name as a string, then change typeof(yourFunctionName) to typeof(eval(yourFunctionName))

Jquery .each() Scope Problem (bug?)

For some reason my call to nested jQuery.each() functions are losing scope for some variables, but not others. In the code below, the Client.KNE reference works, but ClientDiv does not, even though prior to that each, both are defined, populated variables...
By switching Client and ClientDiv to global variables, it works, but I feel like I should not have to create global variables here...
Doesn't Work:
jQuery.each(Messages.Additions, function (clientIndex) {
var Client = Messages.Additions[clientIndex];
var ClientDiv = $("#clientTitle_" + Client.ClientID);
if (ClientDiv.length == 0) {
$("#ClientTemplate").tmpl(Client).appendTo("#ClientContainer");
} else {
jQuery.each(Client.KNE, function (kneIndex) {
var KNE = Client.KNE[kneIndex]; // Works
var KNEDiv = ClientDiv.find("#kneTitle_" + KNE.KNE); // DOES NOT WORK
Does Work:
jQuery.each(Messages.Additions, function (clientIndex) {
Client = Messages.Additions[clientIndex];
ClientDiv = $("#clientTitle_" + Client.ClientID);
if (ClientDiv.length == 0) {
$("#ClientTemplate").tmpl(Client).appendTo("#ClientContainer");
} else {
jQuery.each(Client.KNE, function (kneIndex) {
KNE = Client.KNE[kneIndex]; // Works
KNEDiv = ClientDiv.find("#kneTitle_" + KNE.KNE); // Works
Anyone know what I'm doing wrong in the first version? Or is this a bug? Why does the one variable work but the other doesn't...
From here: Jquery $().each method obscures 'this' keyword it looks like I could pass the variables into the function call, but should I have to?
Tried the above link, and it is not working:
jQuery.each(Messages.Additions, function (clientIndex) {
var Client = Messages.Additions[clientIndex];
var ClientDiv = $("#clientTitle_" + Client.ClientID);
if (ClientDiv.length == 0) {
$("#ClientTemplate").tmpl(Client).appendTo("#ClientContainer");
} else {
jQuery.each(Client.KNE, function (kneIndex, Client, ClientDiv) {
var KNE = Client.KNE[kneIndex];
var KNEDiv = ClientDiv.find("#kneTitle_" + KNE.KNE); //Does not work - ClientDiv undefined
Similar questions without satisfactory answer:
Scope of jQuery each() function?
SOLUTION
$.each(Messages.Additions, function () {
var $Client = this;
var $ClientDiv = $("#clientTitle_" + $Client.ClientID);
if (!$ClientDiv.length) {
$("#ClientTemplate").tmpl($Client).appendTo("#ClientContainer");
} else {
$.each($Client.KNE, function () {
var $KNE = this;
var $KNEDiv = $ClientDiv.find("#kneTitle_" + jq($KNE.KNE));
// SWITCHED TO $ PREFIX
You can try this using this keyword which points to the current item in the loop. Instead of checking for if (ClientDiv == null) you should check for if (ClientDiv.length > 0) because jQuery returns am empty object if it do not finds the element so that check will fail.
var additions;
jQuery.each(Messages.Additions, function () {
var $clientDiv = $("#clientTitle_" + this.ClientID);
if ($clientDiv.length == 0) {
$("#ClientTemplate").tmpl(Client).appendTo("#ClientContainer");
} else {
jQuery.each(Client.KNE, function () {
$clientDiv.find("#kneTitle_" + this.KNE);
});
}
});

Categories

Resources