📜 ⬆️ ⬇️

Custom events in action

In this post I will tell you how I use jQuery custom events (custom events) in my work.

Imitation of events


A simple task is given, the implementation of which is observed by all habrovans: when the user has scrolled down enough, the “Best for 24 hours” box-block is animated, and hidden when the user scrolls up the page. This task is solved by hanging the event handler on the scroll and resize events of the window (window), which is occupied by two things: it calculates whether or not the insert box should be displayed / hidden and, depending on the result, makes animation of the display or hide it.

If this task faced us, how would we proceed to its implementation? Well, for example, wrote such a piece of code:

$(window).on('scroll resize', function(){ //   , ,   / //    ( ) }); 

')
Immediately, I .delegate() note that since version 1.7, the time of .bind() , .delegate() , .live() over: from now on we use the universal method for connecting event handlers .on() (except for the cases when we need to use a single "eavesdropping " .one() , as well as catch dom-ready with $(function(){ }) ).

In the example above, we catch window scrolling and resizing events using an anonymous function as a handler.

Do not forget anything? Probably not. We are checking. When a window scrolling event occurs, the body of the handler function is processed: take the dimensions of the visible rectangle, count the coordinates and move the div-ku to them. Stand still. And if the user does not “twist” anything, we will not position our element at the very beginning of work? Well yes. It seems, still forgotten. Well, we will write this:

 $(window).on('scroll resize', function(){ // get new coordinates // animate repositioning }); // get new coordinates // animate repositioning 


Trouble: it turned out the code "with a smell." Keep calm and get rid of copy-paste:
 $(window).on('scroll resize', repositionAnchor); function repositionAnchor(){ // get new coordinates // animate repositioning } repositionAnchor(); 


I do not know about you, but I like it even less.

Someone will say: "Go back to the original example and tie up even for the event 'load'." No question, tie up. But what if, for some reason, this code is executed after the passage of the load event?

The solution is on the surface. Thanks to jQuery, we can independently imitate the passage of events that we expect using the .trigger() method. Our initial example becomes:

 $(window).on('load scroll resize', function(){ // get new coordinates // animate repositioning }).trigger('scroll'); 


Attached to the load, scroll and resize, and without waiting for any to happen, we imitate the onset of one of them. Profit. Both the declaration, and initialization.

Custom events


Well, what if the document itself is shortened or somehow changed the shape due to the drawing of new data received asynchronously? Need to display the block will appear in us, but the 'scroll' event will not happen? Then ... when it becomes necessary, we “call” $(window).trigger('scroll') - it's much easier.

Easier then - yes, but the fact that “scroll” means “scrolling”, and not “changing the internals of the document”, is it nothing? Personally to me - what . Oh, the window object would have a 'change' or 'redraw' event, say. Then it would be possible to hang on him, and later - “shoot” them when the time comes.

What if I tell you that nothing prevents us from doing this? What does it matter if the object has such an event or not? That's it:

 $(window).on('load scroll resize redraw', function(){ // get new coordinates // animate repositioning }).trigger('scroll'); // some time later, just when we need it: $(window).trigger('redraw'); // << custom event 


There is no standard event called redraw . We ourselves have come up with it now. Therefore, it is called "user".

In jQuery, we can “hang” on listening to such an event on all elements except text nodes and nodes with comments.

So, look, it turns out that we are not limited to a rigid list of user interaction events with the DOM: we can invent our own names for events, “shoot” them, and, of course, hang their handlers. This freedom of action allows us to rise to a higher level in the definition of application blocks (modules) and ensure their interaction on the principles of loose coupling.

Of course, we already knew about this from the jQuery API documentation itself: an event can have any name or type , for example, 'onDataAvailable' , 'elvisHasLeftTheBuilding' and 'the_answer_to_life_the_universe_and_everything_is_ready' . Many knew this, but I am sure that not everyone used it.

Two words about event namespaces. Everything that goes in the name of the event through a dot is the so-called namespace of this event ( 'onDataAvailable.widgetOne' - “widgetOne” is the namespace for this event). There may be several of them ( 'onDataAvailable.widgetOne.dataEvents' - two namespaces are involved here: widgetOne and dataEvents). This is a convenient way to group events. But namespaces deserve a separate article, so there is not a word about them anymore. One conclusion from the above: we avoid the names of events with dots.

What to cling to?


We have just learned anew that we can “shoot” and “listen” to events with absolutely any names. However, another question comes to the fore. If to listen for a click ( 'click' ), we hang on a DOM element .. (or on one of his parents, who will receive the desired event when it ascends), then what should we hang on when we want to listen to the onset of a user event? For example, the 'onDataAvailable' events, which should occur, in our opinion, when the data important for the application was loaded and processed?

I first answered this question for myself:

 $(window).on('onDataAvailable', function(){ //  }); 


But immediately abandoned this working version. It seemed to me wrong to use the most important object of the client environment as a gateway for “walking” user events. One of the reasons: is it not enough, because of the lack of it, or how, one of the events will acquire the name of known events ( 'load' , 'resize' , etc.), and not those “listeners” will work.

Then I created, without binding to the document, an empty element ( var eventNode = $('') ) and connected listeners to it - eventNode.on('customEvent', function(){ /*...*/ }) or “ fired an event from it: eventNode.trigger('customEvent') .

But I still thought it was wrong that we can create custom events, but send or listen to them from non-DOM elements - no. And now, after re-reading the documentation and digging into the jQuery source, I came to the conclusion that the eventNode from our example may well be a simple object ( {} ) wrapped in $() , like this: var eventNode = $({}) . Such an object not only has .on() , .trigger() , .one() methods in its prototype, but also really works with them.

Here, simply what happened:

 var events = (function(){ var eventNode = $({}); return { on: on, trigger: trigger }; function on(){ eventNode.on.apply(eventNode, arguments); } function trigger(){ eventNode.trigger.apply(eventNode, arguments); } })(); // events.on('customEvent', function() {}); // events.trigger('customEvent'); 


What to say?


I am going to eliminate the omission present in all the above examples. Our events do not transmit any data.

This omission is eliminated easily: the second parameter to the .trigger() method, immediately after the name of the event, we can pass our own data, which the handler will receive the second parameter (the first, as you know, the object of the event itself). I don't even have to rewrite a recent example:

 events.on('onDataAvailable', function(evt, data) { var items = data.items, page = data.page, total = data.total; // render items based on data }); events.trigger('onDataAvailable', { items: [ /*... */ ], page: 3, total: 10 }); 


On this I interrupt. In the second part, I will talk about the attempt at typing events, filtering based on expected data patterns, and also about resolving the problem of ringing calls.

Thanks for attention!

Source: https://habr.com/ru/post/213295/


All Articles