Next lesson playing in 5 seconds

  • Overview
  • Transcript

5.1 A Custom Event System

Now it's time to apply what you've learned. We'll build a custom event system in this lesson.

5.1 A Custom Event System

We are to the point where we can finally start implementing some of the things that we've talked about throughout this course. And we're going to do so in two parts. In this lesson, we are going to write an event system. Something that we could use as a basis for other components. And then in the next lesson, we are going to write a simple UI component that will take advantage of the event system that we wrote in this lesson. So that just leaves the question as to how we implement these things. Do we use factory functions, do we use the old style data types, or do we use classes? Well I don't know about you, but I like to use new things. And even though there isn't a browser that officially supports classes. There are many transpilers that are being used in production by very large companies and corporations. So if it's good enough for them, it's good enough for us. So we are going to use classes which means that we need a transpiler. Once again I'm going to use Babel. And we're going to write everything as modules. Because that's basically how we need to write our stuff. So, let's start by creating a file that's going to contain our class. And we're going to call this class Event Target. So we can do event target.js or I wonder if we will eventually see files that is the name of the class followed by class, and then JS. That strikes me as very Java-ish. And hopefully we won't do that. So we have have our events target. Let's just go ahead and define that class. So, EventTarget. Let's go ahead and fill out the constructor. I don't know what exactly we will need here yet, but we can always come back and add in what we need. But now let's talk about the methods that this thing will need. We need a way of adding event listeners, we need another way of removing event listeners. So we can do addListener. And we could use you know addEventListener but that could cause some confusion. Some people might think that, well this has addEventListener. And then they tried to use that like the addEventListener method in the DOM. So if we are slightly different but recognizable enough then I think that that is going to be fine. Now for the addListener method we know that we need two things. We need to know what type of event that we want to listen for. And then we need the function that is going to execute. So we need the same thing for removing a listener, so we have type, and the function. And then we also need a way of firing the event. Because this is going to be the base class for other classes, and those other classes will need to fire an event. So let's have a method simply called fire, or we could say fireEvent. And I'm not sure what exactly we need here just yet. So we will come back and fill that method out. Well let's focus on adding listeners. Now, a listener is nothing more than a function. And we can have multiple functions for each individual type. So that means that we are going to store our functions in an array. And we will need to be able to reference that array by the type, a string. So the key is essentially a string. The value is an array that is an object. So, we are going to store all of our listeners within an object, so it would look like this. We would have lots listeners. That would be an object. Then we could have like click, which would be an array. And so on and so forth. So we can make this private because we don't want outside access to the actual listeners themselves. So let's use a WeakMap. That way we can use it in any browser. At least the browsers that can use the WeakMap polyfill so. New WeakMap and then inside of the constructor we will go ahead and set up our listeners. So map.set. This is the key, and then the value is going to be an object that has listeners, which is going to be an object. So inside of addListener, the first thing we could get is this listeners' properties. So we can say Listeners equals map.get, pass in this and then listeners. So that will give us our listeners object. The next thing we need to do is check to see if we have any listeners for the given type. Because if we don't then we need to create that key on our listener's object and set that to an array. So if not Listeners, and then type. Then we will say listeners of type equals a new array. And then we just want to push the new listener to that array. So we will say listeners of type.push. And then we will pass in our function. So we have now added an event listener for a given event. Now let's remove it. Let's go to the remove listener method. And the let's get the listeners of the given type. So we will have map gets this, and then listeners and type. Now there's no guarantee that we have any listeners for this type. So let's add a check. If we don't have any listeners, then we will simply return. Because there's nothing else that we need to do. However if we make it past this check, then we need to search our array for the given function. And we could do that very easily with the indexof method. So listeners.indexof, and we will pass in the function. Now it is possible that somebody added multiple listeners with the same function. I don't know why they would want to do that, but that is possible. So we could do this in a loop. So while index is greater than negative one, we are going to remove that function from our array. So we will use the splice method, we will pass an index, and we only want to remove one. But then we need to refresh index, so once again indexOf(fn) and that will remove all of those listeners from our array. So now we just need to fire an event. So when it comes to firing an event we need to know what event to fire. We also need an object that contains everything about that event. So let's have two arguments. The first will be type The second will be the event object. And let's go ahead and test for type. If we don't have anything here, then there's nothing to do because we don't know what event to fire. So, in this case we will throw a new error and let's just say we need to know the type. And more descriptive message would work better, but this is going to work for us. So now let's check the event object. And let's see if it has a type property. And if not, then we will set type equal to the type that was passed. Although this should be not event object type. And then we could also have an event target. So if not then we will set the target to this instance. That way there's always a target. And then after we do this we can finally get all of the listeners for a given event. And that's basically what we did inside of the Remove method. So let's just copy and paste. Let's also check if there are any listeners for this event. Because if there isn't then there isn't anything else to do. But if we make it this far, then we need to call all of those functions. So we can use the forEach method. And for our callback function we just want the item in the array, and then we just call that function and pass the event object. Now we have a choice when it comes to calling the event listener. We could say okay, we want this to execute within the context of this object. But we also want to respect the wishes of whoever set up the event listener to begin with. As somebody passed in a function that's already bound to a this. Well, we don't want to change that. So to be safe we are just going to call the function as it is. And let's do one other thing. Let's make this easy to fire an event, so that we could pass just a type. And if we didn't have anything else as far as event data, then we could just omit the event objects. So let's do if not eventObj, then we will create that eventObj. Because then the type and the target will be populated, and that's really the most important pieces of information needed. If we needed to supply anything else, then we would supply an event object to the fireEvents method. So this should be it. Let's export this class, so that we can then import it wherever we need to. Let's open up index.html, and we will import EventTarget from eventTarget.js. And then let's set up a couple of listeners. So let's do what let listener1 = a function. That's going to accept an event Object. And inside of this method let's just alert the type. Or e.type, rather. And then we will do the same thing inside of another listener. Except that instead of alerting the type, let's alert the target. Now, I know that that's not going to be very useful because we should just see object, object. But at least we would see that we have a target there. And then once we have our listeners ready, we need to actually set them up. So first we need an EventTarget object. Now normally we wouldn't be directly creating an event object. This is a base class that would then be inherited by child classes and we would create instances of those child classes. But since we don't have a child class, we'll just use EventTarget. So we will call the addListener let's just call this event load. And we will pass a listener one. We need to do the same thing for listener two. And then we want to fire that event. So evt fire event. And we want to fire the load event. But now we can omit that event object. And it will automatically build it for us. So at this point in time, we should see two alert boxes. One with the type, one with the target. Now let's test the removing of an event listener. So let's copy what we have for ADD listener. Let's change that to removeListener. And then let's fire the load event again. Now at this point we shouldn't have any listeners. So we shouldn't see anything in the browser. So let's view this in the browser. Let's see what we get, and we don't get anything at all. Well, let's fire up the developer tools. Let's go to Console. Let's put that on the right part of the screen and the browser on the left. And let's see. Okay. So we have exception thrown and not caught. And then we have undefined. Except that these aren't really helpful. So you know Edge is new. I would say that it's beta, but it's not, it's released. There are some things that just should be there and it's not. So Chrome, thankfully, ought to tell us what the problem is. And yes, so let's see what that is. Anonymous Module duplicate declared listener1. So let's go back to our code and that was the problem. We had two listener1s. So now this should work, if we go back to Edge, we can refresh and we have one that says load. The other that says object Objects. And then we should see nothing at all. Let's look at the console just make sure that there aren't any errors, and that is indeed the case. So everything should work in Chrome as well. Load Object object and then nothing. So we have the first piece in this two parts project. The next thing we need to write is the component itself that will inherit from our event target class.

Back to the top