Function infinite loop and ignoring parts of other functions - javascript

Okay, so basically all I wrote this script to do is clear and click a button if the textbox is full and refresh the page if its not.
I can successfully clear the text box when its full and refresh the page when its not, but as soon as I try to use my clickButton function it kicks into an infinite loop and skips the if() in clrLog
function addFunction(func, exec) {
var script = document.createElement('script');
script.textContent = '-' + func + (exec ? '()' : '');
document.body.appendChild(script);
document.body.removeChild(script);
}
function clickButton(val) {
buttons = document.getElementsByTagName('INPUT');
for (var i = 0; i < buttons.length; i++)
{
if (buttons[i].type == 'submit' && buttons[i].value == val)
{
buttons[i].click();
}
}
}
function clrLog() {
var elements = [
];
elements = document.getElementsByClassName('logarea');
if (elements.log.value === '')
setTimeout(function () {
location.reload();
}, 5000);
for (var i = 0; i < elements.length; i++) {
elements[i].value = '';
}
clickButton('Edit log file');
}
function main() {
addFunction(clrLog(), true);
}
main();

I found out that I could avoid using a for loop by using document.querySelector(); instead - so much easier :)

Related

Delay inside for loop not working

I want to make a delay inside my for loop, but it won't really work.
I've already tried my ways that are on stackoverflow, but just none of them work for what I want.
This is what I've got right now:
var iframeTimeout;
var _length = $scope.iframes.src.length;
for (var i = 0; i < _length; i++) {
// create a closure to preserve the value of "i"
(function (i) {
$scope.iframeVideo = false;
$scope.iframes.current = $scope.iframes.src[i];
$timeout(function () {
if ((i + 1) == $scope.iframes.src.length) {
$interval.cancel(iframeInterval);
/*Change to the right animation class*/
$rootScope.classess = {
pageClass: 'nextSlide'
}
currentId++;
/*More information about resetLoop at the function itself*/
resetLoop();
} else {
i++;
$scope.iframes.current = $scope.iframes.src[i];
}
}, $scope.iframes.durationValue[i]);
}(i));
}
alert("done");
This is what I want:
First of all I got an object that holds src, duration and durationValue.
I want to play both video's that I have in my object.
I check how many video's I've got
I make iframeVideo visible (ngHide)
I insert the right <iframe> tag into my div container
It starts the $timeout with the right duration value
If that's done, do the same if there is another video. When it was the last video it should fire some code.
I hope it's all clear.
I've also tried this:
var iframeInterval;
var i = 0;
$scope.iframeVideo = false;
$scope.iframes.current = $scope.iframes.src[i];
iframeInterval = $interval(function () {
if ((i + 1) == $scope.iframes.src.length) {
$interval.cancel(iframeInterval);
/*Change to the right animation class*/
$rootScope.classess = {
pageClass: 'nextSlide'
}
currentId++;
/*More information about resetLoop at the function itself*/
resetLoop();
} else {
i++;
$scope.iframes.current = $scope.iframes.src[i];
}
}, $scope.iframes.durationValue[i])
Each $timeout returns a different promise. To properly cancel them, you need to save everyone of them.
This example schedules several subsequent actions starting at time zero.
var vm = $scope;
vm.playList = []
vm.playList.push({name:"video1", duration:1200});
vm.playList.push({name:"video2", duration:1300});
vm.playList.push({name:"video3", duration:1400});
vm.playList.push({name:"video4", duration:1500});
vm.watchingList=[];
var timeoutPromiseList = [];
vm.isPlaying = false;
vm.start = function() {
console.log("start");
//ignore if already playing
if (vm.isPlaying) return;
//otherwise
vm.isPlaying = true;
var time = 0;
for (var i = 0; i < vm.playList.length; i++) {
//IIFE closure
(function (i,time) {
console.log(time);
var item = vm.playList[i];
var p = $timeout(function(){playItem(item)}, time);
//push each promise to list
timeoutPromiseList.push(p);
})(i,time);
time += vm.playList[i].duration;
}
console.log(time);
var lastPromise = $timeout(function(){vm.stop()}, time);
//push last promise
timeoutPromiseList.push(lastPromise);
};
Then to stop, cancel all of the $timeout promises.
vm.stop = function() {
console.log("stop");
for (i=0; i<timeoutPromiseList.length; i++) {
$timeout.cancel(timeoutPromiseList[i]);
}
timeoutPromiseList = [];
vm.isPlaying = false;
};
The DEMO on PLNKR.
$timeout returns promise. You can built a recursive chain of promises like this, so every next video will play after a small amount of time.

how to use setTimeout or setInterval properly

I am using google maps and i am trying to put a pause in execution to prevent QUERY_LIMIT usage issue. My function that plots the addresses looks like this.
The code works, however i want to try setTimeout or setInterval to see if its going to look better on UI.
How do i call it, what should be the first argument?
Thanx alot.
vLocations = [];
for (var i = 0; i < vAddresses.length; i++) {
//pause to prevent OVER_QUERY_LIMIT issue
//geocode "free" usage limit is 5 requests per second
//setTimeout(PlotAddressesAsUnAssigned, 1000);
//sleep(500);
//this will resolve the address and store it in vLocations
AddWaypointAndUnassigned(vAddresses[i]);
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
}
Doing a pause (asynchronous execution) inside a loop (synchronous) will usually result in a lot of trouble.
You can use recursive calls that are done only when a timeout ends.
var vLocations = [];
// Manages the timeout and recursive calls
function AddWaypointAndUnassignedWithPause(index){
setTimeout(function(){
// When the timeout expires, we process the data, and start the next timeout
AddWaypointAndUnassigned(vAddresses[index]);
// Some other code you want to execute
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
if(index < vAddresses.length-1)
AddWaypointAndUnassignedWithPause(++index);
}, 1000);
}
// Start the loop
AddWaypointAndUnassignedWithPause(0);
JSFiddle example.
Try this, hope this will help
vLocations = [];
for (var i = 0; i < vAddresses.length; i++) {
//pause to prevent OVER_QUERY_LIMIT issue
setTimeout(function(){
//this will resolve the address and store it in vLocations
AddWaypointAndUnassigned(vAddresses[i]);
}, 500);
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
}
What about a waiting line, thats fired when an item is added and stopped when there are no items left.
With setTimeout:
var INTERVAL = 1000 / 5;
var to = null;
var vLocations = [];
function addAddress(vAddress) {
vLocations.push(vAddress);
startTimeout();
}
function startTimeout() {
if( to === null ) {
to = setTimout(processLocation, INTERVAL);
}
}
function processLocation() {
if( vLocations.length ) {
var vAddress = vLocations.shift();
AddWaypointAndUnassigned(vAddress);
to = setTimout(processLocation, INTERVAL);
} else {
to = null;
}
}
With setInterval:
var INTERVAL = 1000 / 5;
var to = null;
var vLocations = [];
function addAddress(vAddress) {
vLocations.push(vAddress);
startInterval();
}
function startInterval() {
if( to === null ) {
to = setInterval(processLocation, INTERVAL);
}
}
function processLocation(cb) {
if( vLocations.length ) {
var vAddress = vLocations.shift();
AddWaypointAndUnassigned(vAddress);
} else
clearInterval(to);
to = null;
}
}

How do I update the html displayed for each iteration of a for loop in javascript / jquery?

How would I have the h1 change for each iteration of the loop? This code now only displays the h1 text after everything is done.
for (i=0; i<array.length; i++) {
$("body > h1").text("Processing #" + i);
// things that take a while to do
}
Additional info: if I resize the window as it loops, the html updates.
var array = ['one', 'two', 'three']
var i = 0;
var refreshIntervalId = setInterval(function() {
length = array.length;
if (i < (array.length +1)) {
$("h1").text("Processing #" + i);
} else {
clearInterval(refreshIntervalId);
}
i++
}, 1000);
http://jsfiddle.net/3fj9E/
Use a setInterval with a one-millisecond delay:
var i=0, j=array.length;
var iv = setInterval(function() {
$("h1").text("Processing #" + i);
// things that take a while to do
if (++i>=j) clearInterval(iv);
}, 1);
http://jsfiddle.net/mblase75/sP9p7/
Sometimes you can force a render by forcing a recalculation of layout
for (i=0; i<array.length; i++) {
$("body > h1").text("Processing #" + i)
.width(); // force browser to recalculate layout
// things that take a while to do
}
It might not work in all browsers.
A better way, that does not block the browser so much:
function doThings(array) {
var queueWork,
i = -1,
work = function () {
// do work for array[i]
// ...
queueWork();
};
queueWork = function () {
if (++i < array.length) {
$("body > h1").text("Processing #" + i);
setTimeout(work, 0); // yield to browser
}
};
}
doThings(yourArray);
DEMO
I've spent a bit of time working out a jquery function that seems to solve this. Basically, it's a process handler that you can add any number of processes to and then call run to sequentially call these in a asynchronous way.
$.fn.LongProcess = function () {
var _this = this;
this.notifications = [];
this.actions = [];
this.add = function (_notification, _action) {
this.notifications.push(_notification);
this.actions.push(_action);
};
this.run = function () {
if (!_this.actions && !_this.notifications) {
return "Empty";
}
//******************************************************************
//This section makes the actions lag one step behind the notifications.
var notification = null;
if (_this.notifications.length > 0) notification = _this.notifications.shift();
var action = null;
if ((_this.actions.length >= _this.notifications.length + 2) || (_this.actions.length > 0 && _this.notifications.length == 0))
action = _this.actions.shift();
//****************************************************************
if (!action && !notification) {
return "Completed";
}
if (action) action();
if (notification) notification();
setTimeout(_this.run, 1000);
//setTimeout(_this.run,1); //set to 1 after you've entered your actual long running process. The 1000 is there to just show the delay.
}
return this;
};
How to use with <h1 class="processStatus"></h1>:
$(function () {
var process = $().LongProcess();
//process.add(notification function, action function);
process.add(function () {
$(".processStatus").text("process1");
}, function () {
//..long process stuff
alert("long process 1");
});
process.add(function () {
$(".processStatus").text("process2");
}, function () {
//..long process stuff
alert("long process 2");
});
process.add(function () {
$(".processStatus").text("process3");
}, function () {
//..long process stuff
alert("long process 3");
});
process.run();
});
if the process is very long you can use this script which shows every notification for a specific time interval.
here is the code..
html
<div id="ccNotificationBox"></div>
css
#ccNotificationBox{
-webkit-animation-name:;
-webkit-animation-duration:2s;/*Notification duration*/
box-sizing:border-box;
border-radius:16px;
padding:16px;
background-color:rgba(0,0,0,0.7);
top:-100%;
right:16px;
position:fixed;
color:#fff;
}
#ccNotificationBox.active{
-webkit-animation-name:note;
top:16px;
}
#-webkit-keyframes note{
0% {opacity:0;}
20% {opacity:1;}
80% {opacity:1;}
100% {opacity:0;}
}
javascript
var coccoNotification=(function(){
var
nA=[],
nB,
rdy=true;
function nP(a){
nA.push(a);
!rdy||(nR(),rdy=false);
}
function nR(){
nB.innerHTML=nA[0];console.log(nA[0]);
nB.offsetWidth=nB.offsetWidth;//reflow ios
nB.classList.add('active');
}
function nC(){
nB.classList.remove('active');
nB.innerHTML='';
nA.shift();
nA.length>0?nR():(rdy=true);
}
function init(){
nB=document.getElementById('ccNotificationBox');
nB.addEventListener('webkitAnimationEnd',nC,false);
window.removeEventListener('load',init,false);
}
window.addEventListener('load',init,false);
return nP
})();
usage
coccoNotification('notification 1');
example
http://jsfiddle.net/f6dkE/1/
info
the example above is perfect for external js as you use just one global variable which is the name of the function ... in my case coccoNotification
here is a different approach but it does the same
http://jsfiddle.net/ZXL4q/11/

Javascript variable doesn't update outside of loop, inside OK

I hope I'm not missing something obvious here.
function renderViews(containerId) {
var root = '../Views/';
var viewsDomStr = '';
for (var i = 0; i < bundles.views.length; i++) {
$.get(root + bundles.views[i], function (data) {
viewsDomStr = viewsDomStr.concat(data);
});
}
console.log(viewsDomStr);
$('#' + containerId).append(viewsDomStr);
}
The problem is that the viewsDomStr is updated according to data from server only inside the for loop. For console.log(viewsDomStr); all I get is a reset to ''.
The function you are calling is asynchron.
Try with
function renderViews(containerId) {
var root = '../Views/';
var viewsDomStr = '';
function cb(){
console.log(viewsDomStr);
$('#' + containerId).append(viewsDomStr);
}
for (var i = 0; i < bundles.views.length; i++) {
$.get(root + bundles.views[i], function (data) {
viewsDomStr = viewsDomStr.concat(data);
cb();
});
}
}
The problem is the $.get request is asynchronous so the program continues on and doesn't wait for it. You want to use viewsDomStr inside the $.get function.
function renderViews(containerId) {
var root = '../Views/';
for (var i = 0; i < bundles.views.length; i++) {
$.get(root + bundles.views[i], function (data) {
console.log(data);
$('#' + containerId).append(data);
});
}
// This section runs before $.get is finished
}
EDIT: I've found that viewsDomStr is actually redundant. You are just adding text to the element so you can just add it to the $.get.
Since get method sends asynchronous request, you can check response every 1 sec using setInterval:
function renderViews(containerId) {
var root = '../Views/';
var viewsDomStr = '';
var success = false;
for (var i = 0; i < bundles.views.length; i++) {
$.get(root + bundles.views[i], function (data) {
viewsDomStr = viewsDomStr.concat(data);
success = true;
});
}
var t = setInterval(function(){
if(success) {
console.log(viewsDomStr);
$('#' + containerId).append(viewsDomStr);
clearInterval(t);
}
},1000);
}
The anonymous function of the get method will be asynchronous (as per the execution of get itself).
In short, it all happens too fast.

Loading jQuery plugins and multiple scripts best practices

I'm currently looking for a way to load in multiple scripts/plugins without having a laundry list listed out in the header.
To simply have a load.js have everything load in would be very elegant to me.
$(function() {
var scripts = ['scripts/jquery1.5.js','scripts/easing.js','scripts/scroll.js','scripts/main.js'];
for(var i = 0; i < scripts.length; i++) {
$.getScript(scripts[i]);
}
})
I currently have something like this but can't get it to work for some reason. Any ideas?
Have you looked at head.js?
Here is my conclusion for head.js, I have done some benchmarks myself:
http://blog.feronovak.com/2011/03/headjs-script-is-it-really-necessary.html
It is subjective opinion and benchmarks are not by any means scientific.
This is my solution : check if file is added (stored in array) and then load one file after another. Works perfectly!
var filesadded = "" //list of files already added
function loadJSQueue(array, success) {
if (array.length != 0) {
if (filesadded.indexOf("[" + array[0] + "]") == -1) {
filesadded += "[" + array[0] + "]" //List of files added in the form "[filename1],[filename2],etc"
oHead = document.getElementsByTagName('head')[0];
var oScript = document.createElement('script');
oScript.type = 'text/javascript';
oScript.src = array[0];
array.shift();
oScript.onreadystatechange = function () {
if (this.readyState == 'complete') {
loadJSQueue(array, success);
}
}
oHead.appendChild(oScript);
}
else {
array.shift();
loadJSQueue(array, success);
}
}
else {
success();
}
}
call it with
loadJSQueue(["../../JavaScript/plupload/js/jquery.plupload.queue/jquery.plupload.queue.js",
"../../JavaScript/plupload/js/plupload.js",
"../../JavaScript/plupload/js/plupload.html4.js"
], function(){alert("success");})
loadScripts(['script1.js','script2.js'], function(){ alert('scripts loaded'); }
function loadScripts(scripts, callback){
var scripts = scripts || new Array();
var callback = callback || function(){};
for(var i = 0; i < scripts.length; i++){
(function(i) {
$.getScript(scripts[i], function() {
if(i + 1 == scripts.length){
callback();
}
});
})(i);
}
}

Categories