Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dealing with Scope in Object methods containing 'this' keyword called by Event Listeners

I've generalized my lack of understanding of the situation to this small problem. Here's what I think I know so far:

I have an object myDog (a global variable). Dog has a member variable el that is an html element; because it's an element, I can add event listeners to it. So, when you click on myDog.el, it logs to the console the values of this.name and myDog.name. As expected because of scope, this.name is undefined and myDog.name is 'tye'. this inside of Dog.speak when invoked by the click event listener refers to the element that was clicked, the member variable el, not the object Dog. Since myDog is a global variable, it's able to pick back up regardless of the function's scope and get to myDog.name just fine.

See code below:

function Dog(name,id) {
    this.name = name ? name : "spot";
    this.id = id ? id : "dog";
    this.el = document.getElementById(this.id); // given there is a div with a matching    
    this.el.addEventListener("click",this.speak); // ignore IE for simplicity (attachEvent has its own 'this' scope issues)
}

Dog.prototype = {
    speak: function() {
        console.log("this.name: "+this.name+"\nmyDog.name: "+myDog.name);
    }
};

var myDog = new Dog("tye","dog1");

So... my questions are

1) What are some strategies for attaching objects to html elements, so that I can go from this.el back to myDog (this.el's owner) without myDog being a global variable?

2) Are global variables in this case a necessary evil? And if so, what are so good strategies in this case to gracefully use them? For example, what if I wanted 100 dogs instantiated? How would I handle all those global variables in Dog.speak?

Here's a jsfiddle version if you want to play with it: http://jsfiddle.net/chadhutchins/Ewgw5/

like image 452
Chad Hutchins Avatar asked Dec 01 '11 05:12

Chad Hutchins


People also ask

What does this refer to in event listener?

Simply put, it just means you should have a property in the object called "handleEvent" , which points to the event handler function. The main difference here is, inside the function, this will refer to the object passed to the addEventListener .

How do you know if an element has an event listener?

Right-click on the search icon button and choose “inspect” to open the Chrome developer tools. Once the dev tools are open, switch to the “Event Listeners” tab and you will see all the event listeners bound to the element. You can expand any event listener by clicking the right-pointing arrowhead.

How does event listener work in JavaScript?

The addEventListener() is an inbuilt function in JavaScript which takes the event to listen for, and a second argument to be called whenever the described event gets fired. Any number of event handlers can be added to a single element without overwriting existing event handlers. Syntax: element.


1 Answers

"1) What are some strategies for attaching objects to html elements..."

Since you're using .addEventListener(), I'd suggest taking advantage of a feature of it that few people seem to know about... making your Dog object implement the EventListener interface.

This establishes a very clean relationship between your Dog data and its associated element.

Only minor changes are required. Code first... explanation below.


DEMO: http://jsfiddle.net/Ewgw5/1/

function Dog(name,id) {
    this.name = name ? name : "spot";
    this.id = id ? id : "dog";
    this.el = document.getElementById(this.id);

    // ---------------------------------v----no function!
    this.el.addEventListener("click", this);
}

Dog.prototype = {
    // Implement the `EventListener` interface   
    handleEvent: function(event) {
        switch (event.type) {
            case "click": return this.speak();
        }
    },
    speak: function() {
        console.log("this.name: "+this.name+"\nmyDog.name: "+myDog.name);
    }
};

var myDog = new Dog("tye","dog1");

So all I did was pass this in the constructor to addEventListener() instead of passing a function, and then I added a handleEvent() method to Dog.prototype.

Now when a "click" event occurs, it will invoke the handleEvent() method. The value of this in that method will be your Dog instance. So from there you can call whatever method(s) you need.

Because you made the element a property of this, you can access the element via this.el. But that's technically not even necessary, since the element is also available via the event object as event.currentTarget.


"2) Are global variables in this case a necessary evil..."

Thankfully no!


This behavior should be part of your shim for .addEventListener().

like image 114
user1106925 Avatar answered Oct 08 '22 03:10

user1106925