Unable to create multiple droppable elements inside a loop using jquery - javascript

I am trying to create many droppable elements inside a loop. Here is the code:
for (var i = 0; i < 10; i++) {
for(var j = 0; j < 20; j++){
$("#main").append( '<a "href="javascript:void(0);" id="click'+i+'-'+j+'" onclick="change_to_blocked('+i+','+j+')"><img id="image'+i+'-'+j+'" src="http://localhost/free.png" />');
$("#main").append('');
tmp1 = i;
tmp2 = j;
$('#image'+i+'-'+j).droppable({
drop: function(e,ui) {
$('#image'+(i)+'-'+(j)).attr('src','/bot.png');
console.log(i);
}
});
}
$("#main").append('<br>'); }
However, it only applies to the last value of the loop.

You need to create a closure otherwise at the time the events occur the values of i and j will be the values of the last iteration of the loop.
One way is to wrap the code within loop in an IIFE - Immediately Invoked Function Expression
for (var i = 0; i < 10; i++) {
for (var j = 0; j < 20; j++) {
(function (i, j) {
$("#main").append('<a "href="javascript:void(0);" onclick="return showIndexes('+i +','+j+')">Item # '+i+'-'+j+'</a><br>');
})(i, j); //params used in the IIFE
}
}
By passing the values as arguments of the function they are closed in the function and won't be changed by subsequent iterations
Some of the html rendering was left out for clarity
When looping over arrays with jQuery, you can create a closure by using $.each which will provide you the index as first argument of the callback
DEMO

Related

How to create custom double for loop in JS

So at the moment I'm trying to create a javascript application that uses quite a few nested for loops eg.
for (var i = 0; i < a; i++) {
for (var j = 0; j < b; j++) {
//Code to run
}
}
So for the sake of neatness I thought I could make some sort of function for this purpose that could be called upon like this
doubleLoop(conditionA, conditionB) {
//Code to execute
}
Is this at all possible in Javascript or am I just gonna have to make do with loads of double for loops?
I suppose you could do something like make conditionA and conditionB functions or something, but a simpler alternative would be to just split your functionality up into functions.
Something like this:
function draw(){
doOuterTask();
}
function doOuterTask(){
for (var i = 0; i < a; i++) {
doInnerTask();
}
}
function doInnerTask(){
for (var j = 0; j < b; j++) {
//Code to run
}
}
Here's a more specific example:
function setup() {
createCanvas(400, 400);
}
function draw() {
background(0);
drawGrid(10, 10);
}
function drawGrid(rowCount, columnCount){
for(var rowIndex = 0; rowIndex < rowCount; rowIndex++){
drawRow(rowIndex, rowCount, columnCount);
}
}
function drawRow(rowIndex, rowCount, columnCount){
for(var colIndex = 0; colIndex < columnCount; colIndex++){
var circleWidth = width/columnCount;
var circleHeight = height/rowCount;
var x = colIndex * circleWidth;
var y = rowIndex * circleHeight;
fill(255);
ellipse(x, y, circleWidth, circleHeight);
}
}
This code draws a grid of circles, which you might do using a nested for loop. Instead, it creates a drawRow() function which draws a single row of circles, and then calls that from a drawGrid() function. The drawGrid() function loops over each row and calls the drawRow() function, which loops over each circle in that particular row.
The benefit of this is that you only ever have to worry about one loop at a time, and it also makes testing things much easier because you can directly call the "inner function" to test that it's working the way you expected.

For loop lasts infinitely javascript

I have a problem in my code:
var row = ["1","2","3","4","5"];
var column = ["1","2","3","4","5"];
var arrayLength = row.length;
var arrayLength2 = column.length;
for (var i = 0; i < arrayLength; i++) {
for (var e = 0; e < arrayLength2; e++) {
var samples = document.querySelectorAll('[data-row-id="'+row[i]+'"][data-column-id="'+column[e]+'"]');
for(var i = 0; i < samples.length; i++) {
var sample = samples[i];
sample.setAttribute('data-sample-id', row);
console.log("Colore cambiato");
}
}
}
When i run it, the cycle lasts infinitely and the console.log is called up a lots of times
Where is the error? Thanks!
The problem is that your inner-most loop uses the same i looping variable as your outer most loop and it's constantly changing i so that the outer loop never finishes.
Change the variable on your inner loop to a different identifier that you aren't already using in the same scope.
Youre using same loop i twice nested so it runs infinitely coz it always resets i in inner loop
use something else instead like k
for(var k = 0; k < samples.length; k++) {
var sample = samples[k];
sample.setAttribute('data-sample-id', row);
console.log("Colore cambiato");
}

Optimization - For statement and variable declarations

Here is a quotation from MDN about 'for' statement :
An expression (including assignment expressions) or variable
declaration. Typically used to initialize a counter variable. This
expression may optionally declare new variables with the var keyword.
These variables are not local to the loop, i.e. they are in the same
scope the for loop is in. The result of this expression is discarded.
So when I write this kind of code :
for(var i = 0; i < 10; i++) {
for(var j = 0; j < 10; j++) {
// ...
}
}
At each iteration of the outer loop I declare the variable j, which was already declared right ?
So is it better to write something like this :
for(var i = 0, j = 0; i < 10; i++) {
for(j = 0; j < 10; j++) {
// ...
}
}
... or we do not care?
The behavior this quote refers to is called hoisting and is important to know in JavaScript.
Here's how the MDN explains it:
Because variable declarations (and declarations in general) are
processed before any code is executed, declaring a variable anywhere
in the code is equivalent to declaring it at the top. This also means
that a variable can appear to be used before it's declared. This
behavior is called "hoisting", as it appears that the variable
declaration is moved to the top of the function or global code.
The reason you don't declare all your variables at the top of the function, is because their localization makes the code clearer.
There's absolutely no gain in having both vars being declared in the first loop. It's only confusing. It's the same for the JS engine, but the other developers reading this code will wonder why j is declared at an unexpected location.
Now, if you're disturbed by the fact your variable exists before (with an undefined value) and after the loop where you use it, rejoice: there's a new declaration type coming with ES6: let, which scopes the variable to the block.
for(let i = 0; i < 10; i++) {
for(let j = 0; j < 10; j++) {
// ...
}
}
Beware: compatibility of let
For now, please use the standard form that everybody expects:
for(var i = 0; i < 10; i++) {
for(var j = 0; j < 10; j++) {
// ...
}
}
The convention, in such a case, is that i and j won't be used outside of
their loop. When (and only in this case) you want to use i or j after (for example there's a break in the loop), use this:
var i, j;
for(i = 0; i < 10; i++) {
for(j = 0; j < 10; j++) {
// ...
}
}
// use i and j here

Value gets overwritten because of access by reference

a = [];
for (var i = 0; i < 3; i++) {
a.push(function() {
console.log(i);
})
}
a[0]() // I want 0, but I get 3
I am trying to write a simple piece of code where I have an array of functions such that when I execute a function at a particular index, the index value should get printed.
However, the piece of code above shows the same result (3 in this case) for all index values. I understand that the value is pointing by reference and therefore points to the last value of i. Could someone point out how to do this in the right manner?
Wrap it around a function. Now each time the loop executes, the wrapper function has its own value of i.
a = [];
for (var i = 0; i < 3; i++) {
(function(i){
a.push(function() {
console.log(i);
})
})(i);
}
a[0]()
You can add a self executing function to act like a module. Doing this, the scope of the variable i is in that function.
a = [];
for (var i = 0; i < 3; i++) {
(function(i){
a.push(function() {
alert(i);
})
})(i);
}
a[0]()
Note: In this block (function(i){ ... })(i), i can have any name, there is no connection between i from loop and i from function, i.e. (function(r){ ... })(r).
Here is an alternate version to an anonymous function that gets created and executed all at once.
The issue that you are having is that when the function gets called, the loop has already been evaluated and the value of i has already reached the max value of 3. You need trap the current value of i while the loop is being evaluated.
var a = [];
for (var i = 0; i < 3; i++) {
var fn = function() {
console.log(arguments.callee.i);
}
fn.i = i; // Pass as 'i' parameter to 'fn'.
a.push(fn);
}
a[0](); // The value 0 will be printed, rather than 3.
There is more than one way to skin a cat. The code above is very similar to:
var a = [];
function fn(i) {
return function() {
console.log(i);
}
}
for (var i = 0; i < 3; i++) {
a.push(fn(i));
}
a[0](); // The value 0 will be printed, rather than 3.

Remove element in nested loop

I am writting a simple metaballs implementation in JS. I have an array of the metaballs and i iterate through all of them every frame, and for each one I check the distance to every other metaball and if they are close enough I need to merge them.
This is how I guess it could look, but I don't know how to properly remove the element from array and not break the loops.
for(var i = 0; i < points.length; i++) {
for(var j = 0; j < points.length ; j++) {
if(i != j) {
if(distance < 10) {
//remove one of the points using splice
}
}
}
}
Thanks for help.
First, start your inner loop at i + 1. You've already compared elements up to i, so there's no need to repeat, right? That let's you get rid of your if statement as well.
Then, when you splice, decrement j so as to not skip the next element.
for(var i = 0; i < points.length; i++) {
for(var j = i + 1; j < points.length ; j++) {
if (distance(i, j) < 10) {
points.splice(j--, 1);
}
}
}

Categories