0

I have multiple <div class='drop'> with jQuery .slideUp() and .slideDown() attached to them. I would like to use some kind of loop to determine which one of the trigger <span class='more'> was clicked and .slideDown() the corresponding <div>. Here's what I've got so far:

$(document).ready(function(){
    var i=0;
    $('.more').eq(i).click(function(){
        $('.drop').eq(i).slideDown(800) && $('.more').eq(i).hide(300);
    });
    $(".less").eq(i).click(function(){
        $(".drop").eq(i).slideUp(800) && $(".more").eq(i).show(500);
    });
});

It works as long as I define i and don't put it in a loop. As soon as it's looped like so:

$(document).ready(function(){
    for(var i=0; i<$('.drop').length; i++){
        $('.more').eq(i).click(function(){
            $('.drop').eq(i).slideDown(800) && $('.more').eq(i).hide(300);
        });
        $(".less").eq(i).click(function(){
            $(".drop").eq(i).slideUp(800) && $(".more").eq(i).show(500);};
        });
    };
});

It stops working. What am I missing?

cookie monster
  • 10,671
  • 4
  • 31
  • 45
  • Not sure if it is THE problem, but you have a semicolon after the closing bracket of the for loop. Get rid of that. – DerStrom8 May 04 '14 at 03:35
  • 2
    @derstrom8 That doesn't cause an error. It's just an empty statement. – Paul May 04 '14 at 03:46
  • 1
    Your code is a mess. Fix your indentation before posting. And why are you using `&&` before executing the `$(".more").eq(...` part? It serves absolutely no purpose. – cookie monster May 04 '14 at 03:51
  • Dup of a problem posted about multiple times per day. See [that answer](http://stackoverflow.com/questions/11488014/asynchronous-process-inside-a-javascript-for-loop/11488129#11488129) for a solution. – jfriend00 May 04 '14 at 03:58

2 Answers2

0

It is because of the wrong use of a closure variable in a loop, in this case there is no need to use a loop.

jQuery(function ($) {
    var $drops = $('.drop');
    var $mores = $('.more').click(function () {
        $drops.eq($mores.index(this)).slideDown(800) && $(this).hide(300);
    })
});
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
0

Consider the following segment of code:

for(var i=0; i<$('.drop').length; i++){
  $('.more').eq(i).click(function(){
    $('.drop').eq(i).slideDown(800) && $('.more').eq(i).hide(300);
  });
}

When does that anonymous function get called? When one of the .more elements is clicked.
What is the value of i at that point? The loop has finished by the time the element is clicked, so i === $('.drop').length.

So when the anonymous function is called, it's like executing the function:

function(){
    var i = $('.drop').length;
    $('.drop').eq(i).slideDown(800) && $('.more').eq(i).hide(300);
}

and it's pretty clear that $('.drop').eq($('.drop').length) isn't very useful.

You could fix it by creating a new function with a locally scoped copy of i at each iteration:

for(var i=0; i<$('.drop').length; i++){
  $('.more').eq(i).click(function(i) {
    return function(){
      $('.drop').eq(i).slideDown(800) && $('.more').eq(i).hide(300);
    };
  }(i));
}

However if all your elements with that class belong to some subtree of the DOM, there is probably a better way using event delegation. Bind to the click handler of the parent of that subtree and then handle it there and use e.target to determine which element was clicked. There is an example of that here.

Paul
  • 139,544
  • 27
  • 275
  • 264