2

I am not a javascript object oriented pro and am stuck for a while now. I am trying to implement a call back api so that i can detect when an element is being scrolled. Unfortunately, am unable to fire the prototype method from another method. Please help

     //HTML
     //<div style="width:500px;height:500px;"id="myElement"></div>

     //JS
       mousewheelListen=function(){
            this.el.addEventListener('wheel',);//How do i call Scroller.prototype.scrolling() ?
        }
        Scroller=function(el){//Constructor
            this.el=el;
            mousewheelListen.call(this);
        }
        Scroller.prototype.scrolling=function(callback){//
            callback.call(this);
        }
        
        
        var el=document.getElementById('myElement');
        var myScroller=new Scroller(el);
        myScroller.scrolling(function(){//Listening when scrolling
            console.log('scrolling');
        });
7
  • 1
    Is this what you're looking for? this.el.addEventListener('wheel', event => this.scrolling(event)) Commented Apr 7, 2022 at 22:36
  • @Scotty. Still gives a message that "callback.call is not a function". Commented Apr 7, 2022 at 22:39
  • Well, I did pass into this.scrolling() an event object, not a callback function. If you want to pass in some sort of callback, you'll need to do that instead. I'm not exactly sure what you're trying to accomplish with this callback parameter. Commented Apr 7, 2022 at 22:43
  • 2
    Are you asking how to call the function you're currently passing into myScroller.scrolling()? This is where, I think, there's some logical issues in this code. What's this scrolling function supposed to do? Are you supposed to call it with a callback, with the expectation that the provided callback will be called every time this event fires? Or is this scrolling function itself supposed to be called everytime the event fires? Right now it seems your scrolling function is trying to do both of these things? Commented Apr 7, 2022 at 22:51
  • 1
    Sure, you are right. Am new to Javascript OOP but i figure out now. Thanks for your help Commented Apr 7, 2022 at 22:53

2 Answers 2

2

This seems to do the trick.

Moved all DOM element dependent code into a function that runs after all DOM Content has been loaded. Added the wheel event listener on the referenced element

var myScroller;

function Scroller(el) { //Constructor
    this.el=el;
    this.el.addEventListener('wheel', this.scrolling, { passive: true })
}

Scroller.prototype.scrolling = function(event){ //
    console.log(event);
}

window.addEventListener("DOMContentLoaded", function() {
  let el = document.getElementById('myElement');
  myScroller=new Scroller(el);
})
<div style="width:100px;height:100px;border:1px solid black" id="myElement"></div>

Below is a demonstration of the same thing using three different techniques, all of which end up with a different version of this in the actual scroll function. They all work (pretty much the same) and there's arguments that could be made for and against any of them, and people may have strong opinions about all of them. For me, it comes down to "what is going to be the most useful at the time?"

var myScroller;

function Scroller(el) { //Constructor
    this.el=el;
    this.el.addEventListener('wheel', this.scrolling, { passive: true })
    document.getElementById('element2').addEventListener('wheel', this.scrolling2.bind(this), { passive: true })
}

Scroller.prototype.scrolling = function(event){ //
    console.log(`this is myElement: ${this == document.getElementById('myElement')}`)
}

Scroller.prototype.scrolling2 = function(event){ //
    console.log(`this is myScroller: ${this == myScroller}`)
}

window.addEventListener("DOMContentLoaded", function() {
  document.getElementById('element3').addEventListener('wheel', (event) => { console.log(`this is window: ${this == window}`) }, { passive: true })
  let el = document.getElementById('myElement');
  myScroller=new Scroller(el);
})
<div style="width:100px;height:100px;border:1px solid black" id="myElement"></div>
<div style="width:100px;height:100px;border:1px solid black" id="element2"></div>
<div style="width:100px;height:100px;border:1px solid black" id="element3"></div>

I would call the below the "cleanest" version because it's binding instantiations of anonymous DOM Manipulation methods to DOM objects.

let attachScroller = (element) => element.addEventListener('wheel', function(event) { console.log(this.id) }.bind(element), { passive: true })

window.addEventListener("DOMContentLoaded", function() {
  attachScroller(document.getElementById('myElement'))
  attachScroller(document.getElementById('element2'))
})
<div style="width:100px;height:100px;border:1px solid black" id="myElement"></div>
<div style="width:100px;height:100px;border:1px solid black" id="element2"></div>

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

6 Comments

You'll want to either pass in this.scrolling.bind(this) or event => this.scrolling(this), otherwise the this value won't be correct in the Scroller.prototype.scrolling function.
@ScottyJamison It's not really clear what this should actually be when the scrolling function is called.
Thanks you guys saved my day. I just needed a way to call back scrolling from another method which you have helped me accomplish.
Generally, a method of a "class" should be called with a "this" value that corresponds to an instance of that class. Right now, with this.scrolling, you're actually plucking the method scrolling right off of an existing instance (this), and then letting that get called without preserving the fact that it came from that specific instance. If you really want to call the method with an undefined this value, I would pluck the method from the prototype instead of from an existing instance, as that looks less like a mistake. e.g. addEventListener(..., Scroller.prototype.scrolling, ...).
@philoczar Scotty has a really really good point about bind. In Javascript, this can be transferred around programmatically - which is a very powerful thing, but it can be hard to get used to. You will definitely want to bind the scrolling function as shown in his comments if you're going to rely on it for anything. (You don't actually need the Scroller object at all). (In this version it's being bound to e1, which may not be what you want, and isn't really very OO as Scotty explains)
|
1

The problem is that here:

myScroller.scrolling(function(){//Listening when scrolling
  console.log('scrolling');
});

You are invoking a callback. Your addEventListener not invoke your callback, invoke your scrolling function with an WheelEvent, not a function. The only point in which is invoked witha functión is in the previous piece of code.

You can simply do this:

mousewheelListen=function(){
  this.el.addEventListener('wheel', event => Scroller.prototype.scrolling.call(this, event));
}

Scroller=function(el){//Constructor
  this.el=el;
  mousewheelListen.call(this);
}
Scroller.prototype.scrolling=function(){//Listening when scrolling
  console.log('scrolling');
}

var el=document.getElementById('myElement');
var myScroller=new Scroller(el);

4 Comments

this.el.addEventListener('wheel', this.scrolling); - This doesn't work. It'll cause this.scrolling to be called without the correct this value. You either have to pass in this.scrolling.bind(this) or event => this.scrolling(event)
Tested this does not work
I think now, using call, is ok.
Yep, that works too

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.