clearInterval does not work when called recursivly [duplicate] - javascript

This question already has an answer here:
recursive clearInterval does not work
(1 answer)
Closed 8 years ago.
I have the following function in javaScript. This function is called when i detect a need to re-load the stylesheet. for example, doe to user language change, so the text won't fit the buttons anymore. The problem is, it gets stuck in the setInterval part. looping into it endlessly. I can see in the chrome debugger that it does get to the clearInterval part - but it wont clear. This function - resetStyle - is only called once.
Can anyone please help?
Thank you!
p.resetStyle = function () {
var that = this;
var oldStylesheet_href = $('#mainStylesheet').attr("href");
var i = oldStylesheet_href.indexOf('lang');
var lastLanguege = oldStylesheet_href.substring(i + 5, i + 7);
var prefix, sufix;
if (lastLanguege === createjs.mainManager.LangString) {
return;
}
prefix = oldStylesheet_href.substring(0, i - 1);
sufix = '&' + oldStylesheet_href.substring(i + 7, oldStylesheet_href.length);
var head = document.getElementsByTagName('head')[0]; // reference to document.head for appending/ removing link nodes
var link = document.createElement('link'); // create the link node
link.setAttribute('id', 'newStylesheet');
link.setAttribute('href', prefix + '&lang=' + createjs.mainManager.LangString + sufix);
link.setAttribute('rel', 'stylesheet');
link.setAttribute('type', 'text/css');
var sheet, cssRules;
// get the correct properties to check for depending on the browser
if ('sheet' in link) {
sheet = 'sheet';
cssRules = 'cssRules';
} else {
sheet = 'styleSheet';
cssRules = 'rules';
}
var timeout_id = setInterval(function () { // start checking whether the style sheet has successfully loaded
try {
if (link[sheet] && link[sheet][cssRules].length) { // SUCCESS! our style sheet has loaded
clearInterval(timeout_id); // clear the counters
clearTimeout(timeout_id);
that.onStyleReset();
}
} catch (e) {} finally {}
}, 10), // how often to check if the stylesheet is loaded
timeout_id = setTimeout(function () { // start counting down till fail
clearInterval(timeout_id); // clear the counters
clearTimeout(timeout_id);
head.removeChild(link); // since the style sheet didn't load, remove the link node from the DOM
that.onStyleReset();
}, 15000);
$('head').append(link);
$('#mainStylesheet').remove();
link.setAttribute('id', 'mainStylesheet');
};

You're reusing the variable timeout_id for two different things (your interval id and your timeout id), so you're overwriting your interval id when you call setTimeout. Change:
var timeout_id = setInterval(...
to:
var interval_id = setInterval(...
and update the variable where you call clearInterval as well: clearInterval(interval_id);

Related

I need help getting files from a folder on the server and using their path as the source for my video tag

Basically what I've got planned here is this:
let vid1 = "/videos/introduction.mp4";
let vid2 = "/videos/explanation.mp4";
let vid3 = "/videos/conclusion.mp4";
// Next video function
let video_count = 1;
if (nextBtn.addEventListener) {
nextBtn.addEventListener("click", nextVideo, false);
} else {
nextBtn.attachEvent("onclick", nextVideo);
}
function nextVideo() {
video_count++;
if (video_count == 16) video_count = 1;
let nextVid = "vid" + video_count;
video.src = nextVid;
}
// Previous video function
previousBtn.addEventListener("click", prevVideo);
function prevVideo() {
video_count--;
if (video_count == 0) video_count = 1;
// THIS LINE is the problem
let prevVid = "vid" + video_count;
video.src = prevVid;
}
In both let = nextVid and let = prevVid I want it so that the video source is set to the vid2 variable, but it displays it as .../videos/vid2. I want it to be .../videos/explanation.mp4. Removing the quotation marks makes vid undefined, but if I try to define it I won't know what to code from there as I'm not familiar with arrays.
My current code has been modified from here.
I also tried using Node.js but I don't know how to display the directory listing onto the server. I'm just starting out JS and I understand it pretty well and still learning so corrections and criticism are greatly appreciated.

Javascript array shows in console, but i cant access any properties in loops

I really try my damndest not to ask, but i have to at this point before I tear my hair out.
By the time the js interpreter gets to this particular method, I can print it to the console no problem, it is an array of "event" objects. From FireBug I can see it, but when I try to set a loop to do anything with this array its as if it doesn't exist. I am absolutely baffled......
A few things:
I am a newbie, I have tried a for(var index in list) loop, to no avail, I have also tried a regular old for(var i = 0; i < listIn.length; i++), and I also tried to get the size of the local variable by setting var size = listIn.length.
As soon as I try to loop through it I get nothing, but I can access all the objects inside it from the FireBug console no problem. Please help, even just giving me a little hint on where I should be looking would be great.
As for the array itself, I have no problems with getting an array back from PHP in the form of: [{"Event_Id":"9", "Title":"none"}, etc etc ]
Here is my code from my main launcher JavaScript file. I will also post a sample of the JSON data that is returned. I fear that I may be overextending myself by creating a massive object in the first place called content, which is meant to hold properties such as DOM strings, settings, and common methods, but so far everything else is working.
The init() function is called when the body onload is called on the corresponding html page, and during the call to setAllEvents and setEventNavigation I am lost.
And just to add, I am trying to learn JavaScript fundamentals before I ever touch jQuery.
Thanks
var dom, S, M, currentArray, buttonArray, typesArray, topicsArray;
content = {
domElements: {},
settings: {
allContent: {},
urlList: {
allURL: "../PHP/getEventsListView.php",
typesURL: "../PHP/getTypes.php",
topicsURL: "../PHP/getTopics.php"
},
eventObjArray: [],
buttonObjArray: [],
eventTypesArray: [],
eventTopicsArray: []
},
methods: {
allCallBack: function (j) {
S.allContent = JSON.parse(j);
var list = S.allContent;
for (var index in list) {
var event = new Event(list[index]);
S.eventObjArray.push(event);
}
},
topicsCallBack: function(j) {
S.eventTopicsArray = j;
var list = JSON.parse(S.eventTopicsArray);
topicsArray = list;
M.populateTopicsDropDown(list);
},
typesCallBack: function(j) {
S.eventTypesArray = j;
var list = JSON.parse(S.eventTypesArray);
typesArray = list;
M.populateTypesDropDown(list);
},
ajax: function (url, callback) {
getAjax(url, callback);
},
testList: function (listIn) {
// test method
},
setAllEvents: function (listIn) {
// HERE IS THE PROBLEM WITH THIS ARRAY
console.log("shall we?");
for(var index in listIn) {
console.log(listIn[index]);
}
},
getAllEvents: function () {
return currentArray;
},
setAllButtons: function (listIn) {
buttonArray = listIn;
},
getAllButtons: function () {
return buttonArray;
},
setEventNavigation: function(current) {
// SAME ISSUE AS ABOVE
var l = current.length;
//console.log("length " + l);
var counter = 0;
var endIndex = l - 1;
if (current.length < 4) {
switch (l) {
case 2:
var first = current[0];
var second = current[1];
first.setNextEvent(second);
second.setPreviousEvent(first);
break;
case 3:
var first = current[0];
var second = current[1];
var third = current[2];
first.setNextEvent(second);
second.setPreviousEvent(first);
second.setNextEvent(third);
third.setPreviousEvent(second);
break;
default:
break;
}
} else {
// do something
}
},
populateTopicsDropDown: function(listTopics) {
//console.log("inside topics drop");
//console.log(listTopics);
var topicsDropDown = document.getElementById("eventTopicListBox");
for(var index in listTopics) {
var op = document.createElement("option");
op.setAttribute("id", "dd" + index);
op.innerHTML = listTopics[index].Main_Topic;
topicsDropDown.appendChild(op);
}
},
populateTypesDropDown: function(listTypes) {
//console.log("inside types drodown");
//console.log(listTypes);
var typesDropDown = document.getElementById("eventTypeListBox");
for(var index2 in listTypes) {
var op2 = document.createElement("option");
op2.setAttribute("id", "dd2" + index2);
op2.innerHTML = listTypes[index2].Main_Type;
typesDropDown.appendChild(op2);
}
}
},
init: function() {
dom = this.domElements;
S = this.settings;
M = this.methods;
currentArray = S.eventObjArray;
buttonArray = S.buttonObjArray;
topicsArray = S.eventTopicsArray;
typesArray = S.eventTypesArray;
M.ajax(S.urlList.allURL, M.allCallBack);
//var tempList = currentArray;
//console.log("temp array length: " + tempList.length);
M.setAllEvents(currentArray);
M.testList(currentArray);
M.setEventNavigation(currentArray);
//M.setEventNavigation();
M.ajax(S.urlList.topicsURL, M.topicsCallBack);
M.ajax(S.urlList.typesURL, M.typesCallBack);
}
};
The problem you have is that currentArray gets its value asynchronously, which means you are calling setAllEvents too soon. At that moment the allCallBack function has not yet been executed. That happens only after the current running code has completed (until call stack becomes emtpy), and the ajax request triggers the callback.
So you should call setAllEvents and any other code that depends on currentArray only when the Ajax call has completed.
NB: The reason that it works in the console is that by the time you request the value from the console, the ajax call has already returned the response.
Without having looked at the rest of your code, and any other problems that it might have, this solves the issue you have:
init: function() {
dom = this.domElements;
S = this.settings;
M = this.methods;
currentArray = S.eventObjArray;
buttonArray = S.buttonObjArray;
topicsArray = S.eventTopicsArray;
typesArray = S.eventTypesArray;
M.ajax(S.urlList.allURL, function (j) {
// Note that all the rest of the code is moved in this call back
// function, so that it only executes when the Ajax response is
// available:
M.allCallBack(j);
//var tempList = currentArray;
//console.log("temp array length: " + tempList.length);
M.setAllEvents(currentArray);
M.testList(currentArray);
M.setEventNavigation(currentArray);
//M.setEventNavigation();
// Note that you will need to take care with the following asynchronous
// calls as well: their effect is only available when the Ajax
// callback is triggered:
M.ajax(S.urlList.topicsURL, M.topicsCallBack); //
M.ajax(S.urlList.typesURL, M.typesCallBack);
});
}

Watch a DOM property

I'm trying to monitor the DOM element on a third-party website. The element is non-existent until a countdown timer reaches, then it is created.
I've had some success playing around with: document.getElementsByClassName('countdown ng-hide').length
when it changes from 0 to 1 I want to effect a function.
How would I do this? I've tried using a Mutation Observer but it won't let me observe a null node.
Thanks!
EDIT: This is what I've got so far.
var timesDone = 0;
var songID = 0;
function clickit(xsongID) {
if(document.getElementsByClassName('lottery-countdown ng-hide').length == 1) {
document.getElementsByClassName('media submission ng-scope')[xsongID].click(); songName = document.getElementsByClassName('media-title submission-name ng-binding')[xsongID].outerHTML; timesDone++; }
}
setInterval(clickit, 29900, songID);
I did this recently by setting up an Interval function like this :
var timesTest = 0;
var checkExists = setInterval(function() {
if ($('.yourClassElement').length) {
// ok element found : do your stuff and clear the Interval...
// stuff...
clearInterval(checkExists);
timesTest = 0;
}
timesTest++;
// I won't let this run more than 5 seconds, so :
if (timesTest*100 > 5000)
clearInterval(checkExists);
}, 100);

How to run a setInterval that loops and tries a max of 10 times before breaking?

I have the following to try to reload on a connection drop:
setInterval(window.location.reload(), 1000);
My concern with this is that it could continue forever, ddos'ing my application.
How can I update the above to try at max 20 times before giving up and breaking?
Thank you
This makes me feel dirty, but you could update/extract the window hash with each refresh:
function hack () {
var last = parseInt(location.hash.slice(1));
if (last < 20) {
window.location.hash = last + 1;
window.location.reload();
}
}
window.location.hash = 0;
setTimeout(hack, 1000);
You need to persist some counter state from one page load to the next so you can know when 20 reloads have been done. Your options are:
A hash value
A query parameter
A cookie value
Something stored in local storage
If you don't need this value to persist beyond just the reloads of this page, then options 1) and 2) are better as they are only as persistent as you need. A hash value will not be sent to your server, but could interfere with other uses of the hash value. A query parameter would be sent to the server, but any reasonable server will ignore query values it doesn't know and it won't interfere with anything else. I'd probably pick a query parameter and have actually used one to avoid infinite redirection loops in some of my code. You could implement option 2) like this:
function checkAutoReload() {
var currentCnt = 0;
var re = /(\?|&)(reloadCnt=)(\d+)/;
var param = window.location.search.match(re), newURL;
if (param) {
currentCnt = parseInt(param[3], 10);
newURL = window.location.href.replace(re, "$1$2" + (currentCnt + 1))
} else {
newURL = window.location.href;
newURL += window.location.search ? "&" : "?";
newURL += "reloadCnt=1";
}
if (currentCnt < 20) {
window.location.replace(newURL);
}
}
setTimeout(checkAutoReload, 1000);
Notice, there's no need for a setInterval() because a given page's code only runs once before it either reloads or finds that it is done reloading.
Store the reloadCount in localStorage
MDN DOM Storage
var maxReload = 20;
var reloadPage = function() {
if (typeof a !== "undefined" && a !== null) {
console.log(localStorage.reloadCount);
localStorage.reloadCount = 0;
};
var reloadCount = parseInt(localStorage.reloadCount, 10);
console.log(reloadCount);
if (reloadCount < maxReload) {
reloadCount += 1;
localStorage.reloadCount = reloadCount;
// RELOAD CODE HERE
};
};
// call reloadPage from your code
reloadPage();

how do you add a custom callback javascript param to a bing api callback?

The bing V2 javascript api requires a callback to work. Using jQuery to add the script block dynamically (ignoring pollution of global namespace):
function translate(text) {
var txt = "text=" + text;
var lang = "&to=fr";
var appId = "&appid=apikey"; // Add your AppId here
var func = "&oncomplete=window.translated";
$("<script><\/script>")
.attr("src", "http://api.microsofttranslator.com/V2/ajax.svc/Translate?" + txt + lang + appId + func)
.appendTo("HEAD");
}
and then using a click event on multiple elements to trigger the translation:
$(document).ready(function () {
$('a').click(function () {
var tr = $(this).parent().parent();
var txtin = tr.find('.in').text();
var out = tr.find('.out'); // would like translation inserted here
translate(txtin);
return false;
});
});
and finally the callback required by the api:
function translated(text) {
$("#translation").text(text);
}
I want to specify different elements to received the translated text, depending on what element was clicked to kick the translation of - but using the above approach I can't pass any extra params to bing, to then be returned in the callback.
How should I rewrite this to allow a click on el in row1 to put the translation in row1 and a click on an el in row2 to put the translation in row2? i.e. using the element assigned to 'out' in my click event.
The callback method does not support a state object, so you need to keep track of your objects in some global place. I've implemented a queue model to help you make it
Add the queue definition in the global variables are
var queue = new Array();
Add your 'out' object to it just before calling the service
$('a').click(function () {
var tr = $(this).parent().parent();
var txtin = tr.find('.in').text();
var out = tr.find('.out'); // would like translation inserted here
//Here it goes
queue.push(out);
////////////////
translate(txtin);
return false;
});
Append the index of your object to the text and it will be returned back to you as the service does not translate numbers. You can skip adding the index if you are not making more than one translation at a time, this is only to grant that you get the correct object in case of having some service calls slower than others.
function translate(text) {
//Here it goes
var txt = "text=" + text + " ___" + (queue.length - 1);
////////////////
var lang = "&to=fr";
//...no more changes here
}
Finally extract your object in the callback method and remove the appended index and the splitter from the translated text.
function translated(text) {
if (queue.length > 0) {
var splts = text.split(' ___')
var indx = splts[splts.length - 1];
var out = queue[indx];
//remove the out object from the queue
queue.slice(indx, indx + 1);
//remove the index number from the end of the word
text = text.substr(0, text.lastIndexOf(indx) - 4);
out.text(text);
}
}

Categories

Resources