jQuery .offset() returns undefined value

Marian picture Marian · Aug 19, 2014 · Viewed 10.6k times · Source

I built this very basic .map() function with jQuery to store the top offsets of my HTML sections in an array (inside script tag at the very end of the body):

jQuery

$(window).load(function() {
    var obj = $(this),
        sec = $('section'),
        arr = sec.map(function() {
                  return obj.offset().top
              }).get();
})

It's almost as simple as in the jQuery documentation. And it already worked! ... Until like a week ago.
My HTML (jade [compiled with CodeKit]) has a very simple structure either:

HTML

body.preload
    section#id1
        whateverContent
    section#id2
        moreContent
    section#id3
        evenMoreContent
    ...

Surprisingly today the console decided to tell me that top is undefined:

Console

Uncaught TypeError: Cannot read property 'top' of undefined

Supposedly in my function .map() takes all section tags and returns the offset of each iterated element just as stated in the documentation:
Within the callback function, this refers to the current DOM element for each iteration.
Not even to mention again that this has already worked rock solid for me about a week ago.

So what's the issue here?

Is there a typo? Is something in between messing around? I got more variables defined there, but it shouldn't make any difference, should it? Could it have to do something with compiling jade? With CodeKit? Any clues are highly appreciated!

This is my uncensored, complete JS on the page (worked a week ago):

// Let DOM finish loading
$(window).load(function() {
    // CSS animation hack
    $('body').removeClass('preload')
    // Sticky nav and active class
    var win = $(window),
        doc = $(document),
        obj = $(this),
        sec = $('section'),
        nav = $('nav'),
        anc = $('nav a'),
        pos = nav.offset().top,
        // Fill array with top offsets from sections
        arr = sec.map(function() {
                    return obj.offset().top
              }).get(),
        act = function() {
                    win.scrollTop() > pos ? nav.addClass('sticky')
                    : nav.removeClass('sticky'),
                    $.each(arr, function(i, val) {
                            (win.scrollTop() > val && win.scrollTop() < (val + sec.eq(i).outerHeight(true) + 1) ) ? anc.eq(i).addClass('active')
                            : anc.eq(i).removeClass('active')
                    })
                };
    // Execute sticky function
    win.scroll(act)

Answer

undefined picture undefined · Aug 21, 2014

You are returning the same value in each iteration. the cached this object doesn't refer to your section collection's elements, it refers to the window object. Use $(this) instead of the obj in the map's callback.