Classes & OOP: Event Handlers

Roger PoonBy Roger Poon

JS++ Designer and Project Lead

We've made our 'Animal' class abstract. We've declared an abstract 'talk' method and implemented this method in all subclasses of 'Animal'. However, our animals don't talk yet. We need a way to make animals do something when an event occurs. In this case, the "do something" we want is for the animal to talk and the "event" that needs to occur will be a user mouse click. We can do this with event handlers.

jQuery provides event handling, but we're going to define event handlers in an object-oriented manner. With abstract classes, you don't need to hook events to each individual class. Instead, you can attach an event to the abstract class and have it applied to all subclasses.

Modify Animal.jspp as follows:

    external $;

    module Animals
    {
        abstract class Animal
        {
            protected var $element;
            private static unsigned int count = 0;

            protected Animal(string iconClassName) {
                string elementHTML = makeElementHTML(iconClassName);
                $element = $(elementHTML);

                attachEvents();

                Animal.count++;
            }

            public static unsigned int getCount() {
                return Animal.count;
            }

            public virtual void render() {
                $("#content").append($element);
            }

            public abstract void talk();
            private void attachEvents() {
                $element.click(talk);
            }

            private string makeElementHTML(string iconClassName) {
                string result = '<div class="animal">';
                result += '<i class="icofont ' + iconClassName + '"></i>';
                result += "</div>";
                return result;
            }
        }
    }
    

That's all you have to add. Compile the project and try clicking on the animals. You should see message boxes that pop up when you click on an animal (besides the rhino) and see the noise it makes:

Let's break down the code. We declared a private 'attachEvents' method. We made it 'private' because only the 'Animal' class needs it. In the 'Animal' constructor, we call the 'attachEvents' method. This should be straightforward at this point. Here's the code with new material; inside the 'attachEvents' method, we have this statement:

$element.click(talk);

As you'll recall, via inheritance, every subclass of 'Animal' will also have '$element' as data. This element is unique to each instance of a class. The '$element' field represents a jQuery object, as denoted by its $ prefix. We then use jQuery to attach an "event listener" to this '$element' that is unique to each instance.

Thus, when we instantiate 'Cat', 'Dog', 'Panda', and 'Rhino', we will execute the respective constructors for each, which start by executing a super() call to inherit from the 'Animal' class. 'Animal' in turn creates a unique '$element' for the class instance and attaches the 'click' event listener to the element. Thus, every one of our instances had an event attached to it even though only the abstract base class ('Animal') was modified.

Also, we passed the 'talk' method as a reference to 'click'. Both 'click' and 'talk' are functions. However, only 'click' uses the function call syntax (with parentheses). We didn't add parentheses after 'talk'. The reason is because we don't want 'talk' to immediately execute. We only want 'talk' to execute when a 'click' event occurs.

An event listener will wait until an event occurs. In this case, we attached a 'click' event so the event will be triggered when the user clicks the element with the mouse. Finally, we have to specify which method gets executed when the element is clicked:

$element.click(talk);

As you can see, we passed the 'talk' method as the "event handler." The event handler is the method that gets executed when the event gets triggered. In other words, we want the 'talk' method to execute when the element is clicked.

However, 'talk' is abstract. JS++ recognizes that we have an abstract class, and we're passing an abstract method. Since abstract methods cannot be executed, JS++ will resolve the correct method to execute. In this case, it will resolve to the respective subclass implementations of 'talk'.