0

I have written a Javascript for-loop to auto-hide and show-on-click some content on my website, but for some reason, the variable is taking the highest value at the end of the loop...

So

for (var i = 1; i <= 5; i++) {
    $('.views-row-' + i + ' .faq_answer').hide();
    $('.views-row-' + i + ' .faq_more').click(function () {
        $('.views-row-' + i + ' .faq_answer').slideToggle();
    });
}

All .views-row .faq_answer elements hide as they should and all .views-row .faq_more have a clickhandler.
But for some reason the slideToggle line only applies on the line with class views-row-6 (which isn't there)...

So the i-variable in the function doesn't apply on all values of the loop...
How can I make it apply on all view-row-x's?

1
  • 2
    A search for JavaScript closures will help you better understand your issue. Commented Feb 7, 2013 at 14:31

4 Answers 4

3

You should reference your element with $(this) in the click callback. Here, i will be equal to the last value of i.

    $('.views-row-'+ i + ' .faq_more').click(function(){
        $(this).slideToggle();
    });
Sign up to request clarification or add additional context in comments.

2 Comments

Sorry, I didn't see, but you can get his parent or one of his siblings (I don't know the exact hierarchy) easyly with jQuery.
@Michiel Try my suggestion
1

I think you are approaching the problem incorrectly. It isn't a good idea to bind lots of event handlers in a loop. You are much better off doing something like this:

$('.faq_answer').hide();

$('.faq_more').click(function(){
    $(this).parent().find('.faq_answer').slideToggle();
});

I'm assuming that your html is something like:

<div class="views-row-1">
    <a href="#" class="faq_more"></a>
    <div class="faq_answer"></div>
</div>

Comments

1

In Javascript, variable has function-level scope. The interpreter will move every variable definition to the top of your function (hoisting) to make a reference to the variable available everywhere within such scope. Thus, inner function can access variable from outer function (closure). One gotcha here for everyone accustomed to block-level scope is that this mechanism includes binding loop counters to inner functions, which is an example of the case when inner functions has longer lifespan then outer ones.

In your case, therefore, when the callback get called, the selector will get a reference to your loop counter i with the value of 6, i.e. it is bound to i not the value of i at binding time.

So you just need to pass the value of outer function's variable to the inner function instead of passing a reference:

$('.views-row-'+ i + ' .faq_more').click(
    // pass value of i into the handler, not i itself
    function(i){
         // return the result of invoking the handler 
         // so that we can apply the current value of i to this result
         // and bind the whole closure as a handler to the click event
         return function(e) {
             $('.views-row-'+ i + ' .faq_answer').slideToggle();
         } 
    }(i)

);

Comments

0

Instead of the variable i it is probably best to find another way of matching elements

for ( var i = 1 ; i <= 5; i++ ) {

    // faq_more elements have id 'more' + i
    // faq_more elements contain class faq_more
    // faq_answer elements have id 'answer' + i
    // faq_answer elements contain class faq_answer

    $( ".faq_more" ).hide();

    $( ".faq_more" ).click( function(){

        var id = $( this ).attr( "id" ).replace( "more", "answer" );
        $( "#" + id ).slideToggle();

    });
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.