Search code examples
javascripttimerscopes

Scopes, timer and loop in a javascript object fired on scroll


Here is the example.

I create an object that have 3 elements referenced by the first parameter (document.querySelectorAll...) the I loop through those elements and launch a specific scrolling function (simplified in the example. Inside this function I use this approach to detect the scroll and save perf. I pass the element every time I call this function (first commented console.log) and works well, then inside the timer also I still having the 3 elements (in this example) but then inside the if statement I only have the first one.

I think I understand the problem, but I'm not finding a solution.

function extend( a, b ) {
  for( var key in b ) { 
    if( b.hasOwnProperty( key ) ) {
      a[key] = b[key];
    }
  }
  return a;
}

function addEvent(element, evnt, funct){
  if (element.attachEvent)
    return element.attachEvent('on'+evnt, funct);
  else
    return element.addEventListener(evnt, funct, false);
}

function MyObject(el, opt){
  this.el = el;
  this.opt = extend( {}, this.opt );
  extend( this.opt, opt );
  this._init();
}

MyObject.prototype._init = function(){

  var self = this;
  //this.didScroll = false; //Remove this and put it per element

  this.totalObjs = document.querySelectorAll(this.el);
  this.objs = [].slice.call( this.totalObjs );

  this.objs.forEach( function( el, i ) {
    self._onScroll(el);
  });

};

MyObject.prototype._onScroll = function(e){

  var self = this;
  e.didScroll = false; // Add per element

  addEvent(window, 'scroll', function(){
    e.didScroll = true;
  });

  // Here I have my 3 elements
  // console.log(e.id);

  setInterval(function() {
    // Here I have my 3 elements
    // console.log(e.id);

    if( e.didScroll ){
      e.didScroll = false;

      // Here I have only the first element
      console.log(e.id);
    }
  }, 500);


};


var obj1 = new MyObject('.obj', {});

Solution

  • The problem is your scope. You are setting an interval for each element but checking inside that interval from the main object and setting the didScroll variable to false on the first one. Set this variable on the element and it works just fine.

    function extend( a, b ) {
      for( var key in b ) { 
        if( b.hasOwnProperty( key ) ) {
          a[key] = b[key];
        }
      }
      return a;
    }
    
    function addEvent(element, evnt, funct){
      if (element.attachEvent)
        return element.attachEvent('on'+evnt, funct);
      else
        return element.addEventListener(evnt, funct, false);
    }
    
    function MyObject(el, opt){
      this.el = el;
      this.opt = extend( {}, this.opt );
      extend( this.opt, opt );
      this._init();
    }
    
    MyObject.prototype._init = function(){
    
      var self = this;
      //this.didScroll = false; //Remove this and put it per element
    
      this.totalObjs = document.querySelectorAll(this.el);
      this.objs = [].slice.call( this.totalObjs );
    
      this.objs.forEach( function( el, i ) {
        self._onScroll(el);
      });
    
    };
    
    MyObject.prototype._onScroll = function(e){
    
      var self = this;
      e.didScroll = false; // Add per element
    
      addEvent(window, 'scroll', function(){
        e.didScroll = true;
      });
    
      // Here I have my 3 elements
      // console.log(e.id);
    
      setInterval(function() {
        // Here I have my 3 elements
        // console.log(e.id);
    
        if( e.didScroll ){
          e.didScroll = false;
    
          // Here I have only the first element
          console.log(e.id);
        }
      }, 500);
    
    
    };
    
    
    var obj1 = new MyObject('.obj', {});