0

I'm trying to do some jQuery to navigate through sections with an increment class. But my jQuery code looks wrong. In the console there is this message

TypeError: nextSection.offset(...) is undefined

This is my codde :

for(var i = 0; i < 11; i++) {
    $('.project-' + i + '> .arrows > .arrow-bottom').click(function() {
        $('html, body').animate({
            scrollTop: $('.project-' + i+1).offset().top()
        }, 750);
    });

Someone knows why it doesn't work?

Thank you!

ibrahim mahrir
  • 31,174
  • 5
  • 48
  • 73
Thibault
  • 137
  • 8

2 Answers2

3

The problem is probably with closure, try changing your code to this:

for (var i = 0; i < 11; i++) {
  (function(j) {
    $('.project-' + j + '> .arrows > .arrow-bottom').click(function() {
      $('html, body').animate({
        scrollTop: $('.project-' + (j + 1)).offset().top()
      }, 750);
    });
  }(i));
}

You can refer to this post for more about closure and how it works, How do JavaScript closures work?

Community
  • 1
  • 1
Ayman El Temsahi
  • 2,600
  • 2
  • 17
  • 27
  • Minor stylistic note, on the 2nd to last line, passing the argument to the anonymous function happens /inside/ the parentheses used for invoked function expression, `}(i));`, but I have usually seen it placed outside, `})(i);`, because putting the unnamed function statement into parentheses forces it to evaluate as a function expression, which we then invoke with the argument `i`. The other way still works because the function expression is invocable right away, but clashes with the fact that function declarations are never invocable, so they are never followed by a list of parameters. – Peter Behr Mar 12 '17 at 21:02
  • Turns out that `}());` is Crockford style...should've read Wikipedia first. Oh well...a matter of taste. – Peter Behr Mar 12 '17 at 21:06
  • In this context, you should be extremely careful with your parentheses. What precedes the IIFE pattern can sometimes make a big problem for you, try the following two lines separately and try to spot why the error happens in the first one. var fun = function (x) { console.log(x.toLowerCase()); } (function(j){ fun(j); }("TEXT")); ///// var fun = function (x) { console.log(x.toLowerCase()); }; (function(j){ fun(j); }("TEXT")); – Ayman El Temsahi Mar 12 '17 at 22:09
1

All the event listeners will have a reference to the same i which will be incremented each time (untill the point where i becomes the boundary 11). So when the event listeners are called they will acess the value of that reference of i they got which will be 11 for all of them. So scrollTop: $('.project-' + i+1).offset().top() will be scrollTop: $('.project-' + 12).offset().top() for all of the items (which I assume not an element) and thus the offset will be undefined.

You can use an IIFE (Imediately Invoked Function Expression) to create a separate closure for each iteration, thus the event listeners will have unique values like this:

for(var i = 0; i < 11; i++) {
    (function(index) { // index will be created for each iteration (new one every time)
        // instead of i use index
        $('.project-' + index + '> .arrows > .arrow-bottom').click(function() {
            $('html, body').animate({
                scrollTop: $('.project-' + index + 1).offset().top()
            }, 750);
        });
    })(i); // pass i to the call to initialize index of the IIFE
}
ibrahim mahrir
  • 31,174
  • 5
  • 48
  • 73
  • This is a more thorough explanation, though it was not the first correct answer. – Peter Behr Mar 12 '17 at 20:26
  • 1
    @PeterBehr It took some time to write it as I was looking for duplicates question on SO to vote to close it as this question is populare but didn't find something strong to redirect to. – ibrahim mahrir Mar 12 '17 at 20:28