preface

In recent years, due to the high cost and low efficiency of native development on mobile terminals, a large number of excellent front-end frameworks have emerged, as well as front-end development frameworks specifically for mobile devices (such as RN/Weex), the concept of big front-end has been constantly mentioned. Against this backdrop, front-end technology will increasingly become a must-have skill for mobile developers. As a mobile developer, I found that although front-end development is very different from mobile, there are many things that mobile can learn from the front-end, and the two are similar in many aspects. In the big front end, there are common topics such as MVC and MVVM architecture, componentization, responsive programming, engineering (packaging tools, package management tools), and so on. The author intends to start from the event mechanism of front-end and mobile terminal (taking iOS platform as an example), compare the similarities and differences between the implementation methods of the two ends, and also make some summaries of the event mechanism of front-end and mobile terminal.

Event mechanism

Whether front-end or mobile, when users browse the web or APP, they usually have many interactive operations on the screen, such as clicking, selecting, scrolling, keyboard input and so on, and the web or APP will respond to changes according to different operations. This event-based approach is essentially a messaging mechanism, called the event mechanism.

In the event mechanic, there are three things that are most important:

  • Event producer
  • The event object
  • Event consumer

Event producers can generate a series of event objects, which then carry the necessary information to pass to event consumers.




The front piece

Event flow and event binding

The EMCAScript standard specifies that the event flow consists of three phases: the event capture phase, the target phase, and the event bubble phase.

<html>
    <body>
        <div>
            <button id="mybtn" onclick="buttonClickHandler(event)"</button> </div> </body> </ HTML > <script>function buttonClickHandler(event) {
        console.log('button clicked')
    }
</script>
Copy the code

In the code above, if the button button is clicked, the standard event trigger goes through the following three stages:


target.addEventListener(type, listener, useCapture); // Target: document node, document, window or XMLHttpRequest. // Function argument: register event typetype// For example, register the onclick event for button. If the event is registered during the capture phase, then button.addeventListener ("click".function() {},true);
target.removeEventListener(type, listener, useCapture); // Undo registered events on an element.Copy the code

Here’s an example from Chrome:

<html> <head> <style> ul{ background : gray; padding : 20px; } ul li{ background : green; } < / style > < / head > < body > < ul > < li > point I try < / li > < / ul > < script > var ul = document. The getElementsByTagName ('ul') [0]; var li = document.getElementsByTagName('li') [0]; document.addEventListener('click'.function(e){console.log('document clicked')},true); // The third argument istrueUse the capture ul.addeventListener ('click'.function(e){console.log('ul clicked')},true);
li.addEventListener('click'.function(e){console.log('li clicked')},true);
</script>
</body>
</html>
Copy the code

In the above code, we create a list item and click “Click me try” to see what happens:

document clicked
ul clicked
li clicked
Copy the code

In our developer tools console, we see three lines of results printed, which is what we would expect, because here event capture comes into play, and the click event triggers the Document, ul, and Li nodes in sequence.

In IE, the bubbling mechanism is only supported, so the event binding and event unbinding can only be performed during the bubbling phase:

target.attachEvent(type, listener); //target: document node, document, window, or XMLHttpRequest. // Function argument:type: Registers the event type; // listener: a callback function when an event is triggered. target.detachEvent(type,listener); // The parameter corresponds to the registration parameter.Copy the code

Here’s an example from Internet Explorer:

< HTML > < body > < ul > < li > point I try < / li > < / ul > < script > var ul = document. The getElementsByTagName ('ul') [0]; var li = document.getElementsByTagName('li') [0]; document.attachEvent('onclick'.function(event){console.log('document clicked')})
ul.attachEvent('onclick'.function(event){console.log('ul clicked')});
li.attachEvent('onclick'.function(event){console.log('li clicked')});
</script>
</body>
</html>
Copy the code

Similarly, we clicked on “Let me try” and the developer Tools console printed the following result:

li clicked
ul clicked
document clicked
Copy the code

However, sometimes trapping events and bubbling can have side effects. For example, bubbling can trigger unwanted listeners on the parent node, so is there a way to make bubbling end prematurely? We simply call the stopPropagation function of the Event object (cancelBubble in IE) at the location where we want the event to stop bubating. For example, in the IE browser above, the sample code is modified as follows:

li.attachEvent('onclick'.function(event){
    console.log('li clicked');
    event.cancelBubble=true;
});
Copy the code

The ul node and document will no longer receive the bubbling click event, so their registered event handlers will not be triggered:

li clicked
Copy the code

2. Event delegation

What is event delegation? Event delegation uses the event bubbling mechanism to specify an event handler to manage all events of a certain type. The definition of event delegate is not straightforward enough, and some people may still have trouble understanding what event delegate is. I found that many big bull on the Internet used the example of taking express to explain the event delegation when explaining the event delegation. However, considering that this example is really quite appropriate and vivid, I will directly take this example to explain what the event delegation means: the employees of the company often receive express delivery. In order to facilitate the receipt of the express, there are two ways: one is the delivery after the recipient to take the express; The other is to entrust the front MM to sign for it, and the front MM will sign for it according to the requirements after receiving the express. Obviously, the second scheme is more convenient and efficient. At the same time, this scheme has an advantage, that is, even if there is a new employee, the MM at the front desk can sign for the delivery instead of the new employee. The reason why this example is very appropriate is that this example contains two meanings of delegation: first, the employees in the company can entrust the front MM to sign for the express delivery, that is, the existing DOM node in the program has events and can be entrusted with events; Secondly, the new employee can also ask the front MM to sign for the express delivery, that is, the newly added DOM node in the program also has events, and can also entrust to handle events.

Why use event delegates? We can add event handlers directly to the DOM when it needs to handle events, but what about when many DOM needs to handle events? Let’s say there are 100li in a ul, and each li needs to handle click events. We could iterate over all the li’s and add event handlers to them, but what would that do? We know that the number of event handlers added to the page has a direct impact on the overall performance of the page, because it requires constant interaction with the DOM node. The more times you access the DOM, the more the browser redraws and rearranges, which naturally increases the interaction ready time of the page. This is why you can reduce DOM operations to optimize page performance; If we use the delegate, we can put the operation of the event in the JS code, so that the operation with DOM can be reduced to one time, which greatly reduces the number of interactions with DOM nodes and improves performance. At the same time, the unified management of the event operation can also save memory, because every JS function is an object, it will naturally occupy memory, the more event handlers added to dom nodes, the more objects, the more memory will be occupied; With delegates, we can add event handlers only at the parent level of the DOM node, which naturally saves a lot of memory and improves performance. How is the event delegate implemented? Because of the bubble mechanism, clicking on the child element also triggers the click event of the parent element. Then we can give the event clicking on the child element what the outermost parent element does, and let the event bubble up to the outermost DOM node to trigger the event handler, which is called the event delegate. Before introducing the event delegate approach, let’s look at the general approach to handling events:

<ul id="list">
    <li id="item1" >item1</li>
    <li id="item2" >item2</li>
    <li id="item3" >item3</li>
</ul>
  
<script>
var item1 = document.getElementById("item1");
var item2 = document.getElementById("item2");
var item3 = document.getElementById("item3");
  
item1.onclick = function(event){
    alert(event.target.nodeName);
    console.log("hello item1");
}
item2.onclick = function(event){
    alert(event.target.nodeName);
    console.log("hello item2");
}
item3.onclick = function(event){
    alert(event.target.nodeName);
    console.log("hello item3");
}
</script>
Copy the code

If you click on li, you need to find the location of the target LI and execute the event handler. So what do we do with event delegates (see the example)?

<ul id="list">
    <li id="item1" >item1</li>
    <li id="item2" >item2</li>
    <li id="item3" >item3</li>
</ul>
  
<script>
var item1 = document.getElementById("item1");
var item2 = document.getElementById("item2");
var item3 = document.getElementById("item3");
var list = document.getElementById("list");
list.addEventListener("click".function(event){
 var target = event.target;
 if(target == item1){
    alert(event.target.nodeName);
    console.log("hello item1");
 }else if(target == item2){
    alert(event.target.nodeName);
    console.log("hello item2");
 }else if(target == item3){
    alert(event.target.nodeName);
    console.log("hello item3"); }}); </script>Copy the code

We add a click event to the parent node that will bubble up from the child node when the child node is clicked. After the parent node captures the event, it determines whether it is the node to be processed by judging the event. Target, so as to obtain the corresponding information and process it. Obviously, using the event delegate approach can greatly reduce the complexity of your code while reducing the likelihood of errors. Let’s take a look at the advantages of using event delegates when we add dom dynamically. First let’s look at the normal writing:

<ul id="list">
    <li id="item1" >item1</li>
    <li id="item2" >item2</li>
    <li id="item3" >item3</li>
</ul>
  
<script>
var list = document.getElementById("list");
  
var item = list.getElementsByTagName("li");
for(var i=0; i<item.length; i++){ (function(i){
        item[i].onclick = function(){
            alert(item[i].innerHTML);
        }
    })(i);
}
  
var node=document.createElement("li");
var textnode=document.createTextNode("item4");
node.appendChild(textnode);
list.appendChild(node);
  
</script>
Copy the code

If I click on Item1, I get an event response through item3, but if I click on Item4, I get no event response. Note Traditional event binding cannot dynamically add events to dynamically added elements. What if you use the event delegate approach (see the example)?

<ul id="list">
    <li id="item1" >item1</li>
    <li id="item2" >item2</li>
    <li id="item3" >item3</li>
</ul>
  
<script>
var list = document.getElementById("list");
  
document.addEventListener("click".function(event){
    var target = event.target;
    if(target.nodeName == "LI"){ alert(target.innerHTML); }}); var node=document.createElement("li");
var textnode=document.createTextNode("item4");
node.appendChild(textnode);
list.appendChild(node);
  
</script>
Copy the code

When you click on Item4, item4 has an event response, which means that the event delegate can dynamically add events to newly added DOM elements. We can find that when using event delegate, there is no need to traverse the child node of the element, just need to add events to the parent element, the rest is executed in JS, this can greatly reduce dom manipulation, which is the essence of event delegate.

Mobile (iOS)

In the web when we talk about events, we will talk about the capture and transfer of events (bubbling), so on the mobile end, in fact, can not do without these problems, we will introduce the iOS event mechanism from these aspects: 1, how to find the most appropriate control to deal with events? 2. How does the event respond after finding the first responder of the event?

I. Occurrence and transmission of events

Events in iOS can be divided into three main types:

  • Touch events
  • Accelerometer event
  • Remote control event

Here we only discuss the most common touch events in iOS.

Responder object

Before we learn about touch events, we need to understand one of the more important concepts: UIResponder. Not every object in iOS can handle events, only objects that inherit from UIResponder can accept and handle events, which we call “responder objects.” Classes that inherit from UIResponder can receive and handle touch events because UIResponder provides the following properties and methods to handle touch events:

- (nullable UIResponder*)nextResponder;
- (BOOL)canBecomeFirstResponder;    // default is NO
- (BOOL)becomeFirstResponder;
- (BOOL)canResignFirstResponder;    // default is YES
- (BOOL)resignFirstResponder;
- (BOOL)isFirstResponder;
  
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
Copy the code

When a touch event is generated, the system calls the above four methods at different stages of the touch.

Occurrence of an event

  • When a touch event occurs, the system adds the event to an event queue managed by UIApplication.
  • UIApplication pulls the uppermost event from the event queue and dispatches the event down, sending the event first to the application’s main window (keyWindow).
  • The main window will find the most appropriate view in the view hierarchy to handle touch events.
  • When the appropriate view control is found, the view control’s Touches method is called for specific event handling.

Event passing

In our app, all views are organized in a certain structure, namely a tree hierarchy. Each view has its own superView, including the Topmost view of controller (self.view of Controller). When a view is added to the superView, its nextResponder property is referred to its superView. When the controller is initialized, The nextResponder of self.view(topmost View) will be pointed to the controller, and the Controller’s nextResponder will be pointed to the superView of self.view.

How does the application find the most appropriate control to handle events?

  • First, determine whether the current control itself can accept touch events;
  • Determine if the touch point is on your body;
  • In the current control of the child control array from back to front traversal child control, repeat the previous two steps (so-called from back to front traversal child control, is the first to find the last element in the child control array, and then perform 1, 2 steps);
  • In the above process, the appropriate view is found, for example, called fitView, then the event is handed to the fitView, and the child control of the fitView is iterated until no more suitable view is found.
  • If there is no child control that matches the criteria, then it considers itself the most appropriate view to handle the event.

In the process of finding the most appropriate response control, all participating controls call the following two methods to determine if the control is a more appropriate response control:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
Copy the code

Specific principles can be referred to: iOS event passing hitTest method and PointInside method.

2. Response to events

The responder chain

In the iOS view, all controls are organized in a hierarchical structure, that is, controls are placed in sequence, and controls that can respond to events form a chain according to this sequence, which is called “responder chain”. In other words, a responder chain is a chain connected by multiple responder objects. As mentioned earlier, UIResponder is the base class for all responder objects. In UIResponder, the interface for handling various events is defined. And UIApplication, UIViewController, UIWindow, and all UIKit classes that inherit from UIView inherit directly or indirectly from UIResponder, so their instances are responder objects that can form a responder chain. The relationship of the responder chain in iOS can be expressed as follows:

When a view responds to a touch event, it automatically calls its Own Touches method to handle the event:

#import "DYView.h"// implementation will call touchBegin whenever the control is clicked. If we didn't override this method, we wouldn't be able to respond to handle touch events - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ ... [super touchesBegan: Touches withEvent:event] [touches created] [touches created] [touches created] // Call the parent touches method instead of calling the parent touches method, passing the event to nextResponder} @endCopy the code

The event is thrown to the parent (the last responder) regardless of whether the current child control handles the event. Touches are handled by the parent control if it implements the Touches method, meaning that a touch event can be handled by a single control or by multiple controls in response. Therefore, the transmission and response process of the whole touch event can be summarized as follows:

  • When an event occurs, the event is distributed by UIApplication down the delivery chain, UIApplication -> UIWindow -> UIView -> Initial View, until the most appropriate view is found.
  • The most appropriate view then responds to the event to see if the Initial view can handle the event. If not, it passes the event to its parent view. If the superior view still cannot be processed, it will continue to be passed up; All the way to the view controller view Controller; If not, it then determines whether the view controller can handle the event. If not, it continues to pass up. (If the second graph view controller itself is still in another view controller, it continues to be handed over to the root view of the parent view controller, or if the root view cannot handle it, it is handed over to the parent view controller); All the way to The Window, if the Window still can’t handle the event, it continues to be passed to the application, and if the Application still can’t handle the event, it is discarded.
  • In the response to the event, if a control implements the Touches method, the event will be accepted by the control if [super Touches… It passes the event up the responder chain, to the last responder; The last responder’s Touches method is then called.

Event binding and event broker

event

In iOS application development, various controls are often used, such as buttons (UIButton), switches (UISwitch), sliders (UISlider), and various custom controls. These controls are used to interact with users and respond to user actions. One thing these controls have in common is that they all inherit from the UIControl class. UIControl is the base class of the control class. It is an abstract base class. We cannot use the UIControl class to instantiate the control directly. Event binding in iOS is a target-action mechanism that operates using the following two methods:

// Add binding - (void)addTarget (id)target action (SEL)actionforControlEvents:(UIControlEvents) ControlEvents // unbind - (void)removeTarget:(id)target action:(SEL)actionforControlEvents:(UIControlEvents)controlEvents
Copy the code

When we need to bind a click event to a control (such as a button), we can do the following:

[button addTarget:self action:@selector(clickButton:) forControlEvents:UIControlEventTouchUpInside];
Copy the code

When a button click event occurs, a message is sent to target (in this case, the self object), triggering the Target’s clickButton: method to handle the click-click event. This process can be described in the following figure:

[button addTarget:nil action:@selector(clickButton:) forControlEvents:UIControlEventTouchUpInside];

Copy the code

The code above, whose target object is nil, first checks to see if the button class itself implements clickButton:, which will be called if it does, otherwise it will find button.nextresponder based on the responder chain. Check again to see if the clickButton: method is implemented until the UIApplication(which is actually an AppDelegate) is implemented, and if not, do nothing.

The event agent

In IOS, delegates are implemented via @Protocol, so they are also called protocols. A protocol is a list of methods shared by multiple classes. The methods listed in a protocol have no corresponding implementation (equivalent to an interface). Classes that use the protocol need to implement the methods in the protocol. Delegation refers to giving an object the opportunity to react to changes in another object or to influence the behavior of another object. The basic idea is: two objects work together to solve the problem. An object is very common and intended for reuse in a wide range of situations. It stores a reference to another object (its delegate) and sends a message to the delegate at critical moments. The message may simply inform the delegate that something has happened, giving the delegate an opportunity to perform additional processing, or the message may ask the delegate to provide some key information to control what has happened. Here is an example to illustrate the specific application of agent in iOS development: Taking express delivery as an example, the employee can entrust the front MM to sign for and receive express delivery, so there is a protocol between the employee and the front MM:

@protocol signDelegate <NSObject>
- (void)signTheExpress;
@end
Copy the code

This agreement states a method of signing for the express. An employee can be represented by a class defined below:

##employer.h@protocol signDelegate <NSObject> - (void)signTheExpress; @end @interface employer : NSObject /** * delegate is an employer class property */ @property (nonatomic, weak) id<signDelegate> delegate; - (void)theExpressDidArrive; @endCopy the code
employer.m
#import "employer.h"
 
@implementation employer
- (void)theExpressDidArrive{
    if ([self.delegate respondsToSelector:@selector(signTheExpress)]) {
        [self.delegate signTheExpress];
    }
}
@end
Copy the code

Let’s look at the implementation of the foreground MM class:

#import "receptionMM.h"
#import "employer.h"<signDelegate> @interface receptionMM ()<signDelegate> @end @implementation receptionMM /** * implementation receptionMM */ - (void)theCourierCome{DaChu *employer1 = [[employer alloc]  init]; employer1.delegate = self; // Indicates that the foreground MM acts as a proxy. [employer1 theExpressDidArrive]; } - (void)signTheExpress{NSLog(@)"Courier signed for it.");
}
@end
Copy the code

In iOS development, the main purpose of using delegates is decoupling, because different modules have their own roles, and the processing of events needs to be done by specific modules to keep the data and UI separate, while also reducing the complexity of the application.

conclusion

While front-end and mobile development are very different, there are many similar concepts in terms of event mechanics: For example, the concept of front-end DOM number is very similar to the view tree in App page, the capture and bubble mechanism of front-end event is also similar to the transfer chain and response chain mechanism of iOS event, as well as the concept of event binding and event proxy at both ends. However, due to the highly objectified characteristics of mobile page elements, the processing mechanism for events is relatively more complex, and some design patterns are applied for different purposes. For example, event delegate is mainly used to reduce the complexity of code in front-end development, while it is mainly used to solve the decoupling problem between modules in iOS development. In addition, there are many excellent frameworks on both the front-end and mobile platforms, so there are a lot of content about the Event mechanism of the front-end and mobile terminals, such as the Event Bus of VUe. js and the unified message processing mechanism in ReactiveCocoa. I hope we can discuss it again when we have time.

reference

1. IOS event mechanism