3

I've long been using the excellent JS plugin called Waypoints.js to change CSS classes based on a user's viewport position. Now I'm trying to dynamically create these waypoints.

I've come up with two unsuccessful approaches.

Given this HTML:

<div class="emp-question-set fade-up" id="emp-question-set-1"></div>
<div class="emp-question-set fade-up" id="emp-question-set-2"></div>
<div class="emp-question-set fade-up" id="emp-question-set-3"></div>

Approach 1: Generate the waypoints using a for loop and getElementByID

// Number of question areas
var questionCount = $('.emp-question-set').length;

// Setup variables
var waypointName, selector, selectorjQuery;

// Start at 2, leave the first item to load normally
for (var i = 1; i <= questionCount; i++) {
  waypointName = 'questionsRollIn' + i;
  selector = 'emp-question-set-' + i;
  selectorjQuery = '#emp-question-set-' + i;

  waypointName = new Waypoint({
    element: document.getElementById(selector),
    handler: function(direction) {
      if (direction === 'down') {
        $(selectorjQuery).addClass('fade-up-active');
      }
      if (direction === 'up') {
        $(selectorjQuery).removeClass('fade-up-active');
      }
    },
    offset: '70%'
  });
}

Approach 2: Generate the waypoints using getElementsByClassName

  questionsRollIn = new Waypoint({
    element: document.getElementsByClassName('emp-question-set'),
    handler: function(direction) {
      if (direction === 'down') {
        $('emp-question-set').addClass('fade-up-active');
      }
      if (direction === 'up') {
        $('emp-question-set').removeClass('fade-up-active');
      }
    },
    offset: '70%'
  });

Hopefully one of you can help, thanks in advance.

2 Answers 2

5
+50

I think the problem with your first approach is about a wrong understanding how JavaScript closures working. This is sometimes confusing when coming from a language like Java or C#. Please note that your local variables are somehow global to the closure where the Event handler lives. Finally while executing the Event, your local variable 'selectorJquery' may contain something unexpected. You can verify this by logging the contents of the variable into the JavaScript console.

The second approach probably can't work because you seem to try to bind a set of elements where the js API just might expect only one element.

You can try like this (untested)

// just in case we need the waypoints later on
var waypoints = [];

$('.emp-question-set').each(function(index, value) {

    var that = $(value)

    var waypoint = new Waypoint({
        element: value,
        handler: function(direction) {
            if (direction === 'down') {
                that.addClass('fade-up-active');
            }
            if (direction === 'up') {
                that.removeClass('fade-up-active');
            }
            },
            offset: '70%'
    });
    waypoints.push(waypoint);
});

This is somewhat similar to your first Approach. The idea here is to iterate over all Elements with class .emp-question-set and to create a waypoint object for each. The important Thing is that we make sure that every handler live in his own closure. And so no variable collision can happen.

Sign up to request clarification or add additional context in comments.

1 Comment

I upvoted the answer for the closure concept, but the solution you gave still does not solve the closure problem. At the end of the loop that will have the last question object
0

I up voted the first answer for the closure concept, but it seems like it does not actually solve the problem. Doing the following should be solving your problem.

$('.emp-question-set').each(function(){
     questionsRollIn = new Waypoint({
     element: $(this), //this is now jquery loop context(this question)
     handler: function(direction) {
       if (direction === 'down') {
         $(this.element).addClass('fade-up-active'); //this is now the waypoint context
       }
       if (direction === 'up') {
         $(this.element).removeClass('fade-up-active');
       }
     },
     offset:"10%" //this can be modified based on how sensitive you want it
    });
});

$(this.element) should be your selector for changing the css class

6 Comments

Using this avoids the closure problem?
I edited the answer for you to give you the Idea what I wanted. Yeah by this I meant the current waypoint object
How do you get around making functions within loops in this model? Seems like it still wont work. Also - I need to keep jQuery out of this implementation. It needs to be straight JS.
jquery should not be your problem. You can say document.getElementsByClassName("emp-question-set").forEach(function(){ and the above code here. instead of $(this.element) use this.element}); I have tested the above solution and it works fine.
BTW in your initial post you did use jquery
|

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.