How to make efficient web front-end programs is a problem that I will unconsciously consider every time I do front-end development. A few years ago, the awesome front-end engineers in Yahoo published a book about improving the performance of the web front-end, which caused a sensation in the entire web development technology industry, making the mysterious web front-end optimization question a cabbage on the street, and web front-end optimization has become a simple question that both novices and big giants can answer. When the entire industry knows the answer to the shocking secret, the existing optimization technology can no longer produce a qualitative leap for the website you develop. In order to make the performance of the website we develop better than other people's websites, we need to think more deeply and reserve better skills.
The event system in Javascript is the first breakthrough point I think of. Why is it a JavaScript event system? We all know that the web front-end contains three technologies: html, css and javascript. It is clear how html and css are combined: style, class, id and html tags. There is nothing to talk about, but how javascript enters the middle between html and css and lets the three integrate? Finally, I found that this entry point is the event system of JavaScript. No matter how long or complex JavaScript code we write, it is ultimately reflected in HTML and CSS through the event system. Therefore, I was thinking that since the event system is the entry point for the integration of the three, there will inevitably be a large number of event operations in a page, especially in today's increasingly complex web pages. Without these events, the javascript code we carefully wrote can only be used in the library, and heroes are useless. Since there are a large number of event functions on the page, will there be problems that affect efficiency when writing event functions as we are used to? The answer I have studied is that it is a real efficiency problem, and it is also a serious efficiency problem.
In order to clarify my answer, I want to explain the event system of javascript in detail.
The event system is the entry point for the fusion of JavaScript, HTML and CSS. This point is like the main function in Java. All magic starts here. So how does the browser complete this entry? I have studied three ways, they are:
Method 1: HTML event processing
html event processing is to write event functions directly into the html tag. Because this writing method is closely coupled with the html tag, it is called html event processing. For example, the following code:
The code copy is as follows:
<input type="button" id="btn" name="btn" onclick="alert('Click Me!')"/>
If the click event function is complicated, writing code like this will definitely cause inconvenience. Therefore, we often write the function outside and onclick directly call the function name, for example:
The code copy is as follows:
<input type="button" id="btn" name="btn" onclick="btnClk()"/>
function btnClk(){
alert("click me!");
}
The above writing method is a very beautiful writing method, so many people will unconsciously use it nowadays, but many people may not know that the latter writing method is actually not as strong as the former writing method. This is also a problem I encountered when studying non-blocking loading script technology not long ago, because according to the principle of front-end optimization, javascript code is often located at the bottom of the page. When the page is blocked by a script, the function referenced in the html tag may not have been executed yet. At this time, we click the page button, and the result will be reported "The XXX function is undefined error". In javascript, such errors will be caught by try and catch. Therefore, in order to make the code more robust, we will have the following rewrites:
The code copy is as follows:
<input type="button" id="btn" name="btn" onclick="try{btnClk();}catch(e){}"/>
Seeing the above code is not something that can be described by a disgusting person.
Method 2: DOM0 level event processing
DOM0 level event processing is an event processing supported by all browsers today. There is no compatibility problem. Seeing such a sentence will make everyone who does a web front-end excited. The rule for DOM0 event processing is: each DOM element has its own event processing attribute, which can be assigned a function, such as the following code:
The code copy is as follows:
var btnDOM = document.getElementById("btn");
btnDOM.onclick = function(){
alert("click me!");
}
The event attributes handled by DOM0 level events are defined in the form of "on+event name", and the entire attribute is in lowercase letters. We know that the DOM element is a javascript object in the javascript code, so it is very easy to understand the DOM0-level event processing from the perspective of the javascript object, for example, the following code:
The code copy is as follows:
btnDOM.onclick = null;
Then the button click event is cancelled.
Let's look at the following code:
The code copy is as follows:
btnDOM.onclick = function(){
alert("click me!");
}
btnDOM.onclick = function(){
alert("click me1111!");
}
The next function will overwrite the first function.
Method 3: DOM2 event processing and IE event processing
DOM2 event processing is a standardized event processing solution, but IE browser has developed a set of functions and functions are similar to DOM2 event processing, but the code is different from that.
Before explaining Method Three, I must add some concepts, otherwise I will not be able to explain the connotation of Method Three.
The first concept is: event flow
In page development, we often encounter this situation. The work interval of a page can be represented by a document in JavaScript. There is a div on the page. The div is equivalent to overlaying the document element. There is a button element in the div. The button element is overlaying the div, which is also equivalent to overlaying the document. So the problem is, when we click this button, this click behavior actually not only happens on the button. Both the div and the document are used for click operations. According to logic, these three elements can trigger click events. The event stream describes the concept of the above scenario. The event stream means: the order of events received from the page.
The second concept: event bubbles and event capture
Event bubbles are the solution proposed by Microsoft to solve the event flow problem, while event capture is the event flow solution proposed by Netscape. Their principles are as follows:
The bubbling event starts with a div, followed by a body, and finally a document, and the event capture is reversed, first a document, followed by a body, and finally a target element div. In contrast, Microsoft's solution is more humane and in line with people's operating habits, Netscape's solution is very awkward. This is the consequence of the browser war. Netscape is slower and solves the problem of event flow by sacrificing the code that users are used to.
Microsoft has designed a new event system in combination with bubble events, which is commonly known as ie event processing in the industry. The method of ie event processing is as shown in the following code:
The code copy is as follows:
var btnDOM = document.getElementById("btn");
btnDOM.attachEvent("onclick",function(){
alert("Click Me!");
});
Add events through the attachmentEvent method of the DOM element under ie. Compared with the DOM0 event processing, the method of adding events has changed from attributes to methods, so when we add events, we need to pass parameters into the method. The attachmentEvent method receives two parameters. The first parameter is the event type. The naming of the event type is the same as the event naming in the DOM0 event processing. The second parameter is the event function. The advantage of using the method is that if we are adding a click event to the same element, as shown below:
The code copy is as follows:
btnDOM.attachEvent("onclick",function(){
alert("Click Me!");
});
btnDOM.attachEvent("onclick",function(){
alert("Click Me,too!");
});
Run it, both dialog boxes pop up normally, allowing us to add multiple different click events to the DOM element. What if we don't want an event? What should we do? I provide a detachEvent method for deleting events. The parameter list is the same as the attachEvent. If we want to delete a certain click event, just pass the same parameters as the add event, as shown in the following code:
The code copy is as follows:
btnDOM.detachEvent("onclick",function(){
alert("Click Me,too!");
});
When running it, the consequences are very serious and we are confused. The second click was not deleted. What's going on? I mentioned earlier that deleting events requires passing parameters like adding events. However, in the anonymous function of javascript, even if the code of the two anonymous functions is exactly the same, javascript will use different variables to store them internally. As a result, the phenomenon we see cannot delete the click event, so our code must be written like this:
The code copy is as follows:
var ftn = function(){
alert("Click Me,too!");
};
btnDOM.attachEvent("onclick",ftn);
btnDOM.detachEvent("onclick",ftn);
The method added and the method deleted in this way point to the same object, so the event deleted successfully. The scenario here tells us that we need to have a good habit of writing events that we must define operations independently, and not use anonymous functions as a habit.
Next is DOM2 event processing, its principle is shown in the figure below:
DOM2 is a standardized event. Using DOM2 events, event delivery starts from the capture method, that is, from the document, and then to the body. The div is an intermediary point. When the event reaches the intermediary point, the event is in the target stage. After the event enters the target stage, the event begins to bubble up and handle, and finally the event ends on the document. (I'm pointing to document in this article, but the actual situation is that some browsers will start to capture the event and end the bubble in the window. However, I think that no matter how the browser itself is set during development, it is more development-oriented to pay attention to document, so I'm using document here). People are used to classify the target stage as part of the bubble, mainly because bubble events are more widely used in development.
DOM2 event processing is very difficult. When each event is triggered, all elements will be traversed twice. Compared with the ie event, the performance is much worse than the ie event. I only bubbling, so I only need to traverse once. However, less traversal does not mean that the ie event system is more efficient. Supporting two event systems at the same time will bring greater flexibility to our development from the perspective of development and design. From this perspective, DOM2 events are still very desirable. The code for the DOM2 event is as follows:
The code copy is as follows:
var btnDOM = document.getElementById("btn");
btnDOM.addEventListener("click",function(){
alert("Click Me!");
},false);
var ftn = function(){
alert("Click Me,too!");
};
btnDOM.addEventListener("click",ftn,false);
The addition of events in DOM2 event processing uses addEventListener, which receives one more parameter than ie event processing. The first two means the same as the two parameters of the ie event processing method. The only difference is that the prefix on is removed in the first parameter, and the third parameter is a boolean value. If its value is true, then the event will be processed in the capture mode, with the value false. The event is processed in bubbles. With the third parameter, we can understand why the event element should be run twice in DOM2 event processing. The purpose is to be compatible with the two event models. However, please note here that no matter whether we choose to capture or bubble, the two traversal will be carried out forever. If we choose one event processing method, then no event processing function will be triggered in the other event processing process. This is the same as the principle of idling the car in neutral gear. Through the design of the DOM2 event method, we know that DOM2 events can only execute one of two event processing methods at runtime. It is impossible for two event stream systems to provoke at the same time. Therefore, although the element is traversed twice, the event function cannot be provoked twice. Note that I mean that no two times is provoked, which refers to an event function. In fact, we can simulate the situation where two event stream models are executed at the same time, such as the following code:
The code copy is as follows:
btnDOM.addEventListener("click",ftn,true);
btnDOM.addEventListener("click",ftn,false);
But this way of writing is multi-event processing, which is equivalent to clicking the button twice.
DOM2 also provides a function to delete events. This function is removeEventListener, written as follows:
The code copy is as follows:
btnDOM.removeEventListener("click",ftn,false);
The same is true for the ie event, that is, the parameters must be consistent with the parameters that define the event. However, when using removeEventListener, the third parameter is not passed. The default is to delete the bubble event, because the default is false if the third parameter is not passed. For example:
The code copy is as follows:
btnDOM.addEventListener("click",ftn,true);
btnDOM.removeEventListener("click",ftn);
Run it and find that the event was not deleted successfully.
Finally, I want to say that DOM2 event handling is well supported in ie9, including ie9 or above, and ie8 below does not support DOM2 events.
Let’s compare the three event methods below, as follows:
Comparison 1: Comparison of one method for one side and the other two methods
The way to write method one is to combine html and javascript. You have me and I have you. Deepen this method to achieve a mixed development of html and javascript. In a software term, it is code coupling. The code coupling is not good, and it is very bad. This is the level of a rookie programmer, so once the method is completely defeated, the other two methods will win.
Comparison 2: Method 2 and Method 3
The two of them are written in the same way. Sometimes it is really hard to say who is better or worse. Looking at the above content, we found that the biggest difference between Mode 2 and Mode 3 is that there is only one event in a DOM element, and only once, while Mode 3 can allow an event in a DOM element to have multiple event handling functions. In DOM2 event processing, Mode 3 can also allow us to accurately control the event flow method. Therefore, the function of Mode 3 is more powerful than Mode 2, so compared with Mode 3, it is slightly better.
The following is the focus of this article: the performance problems of event systems. To solve the performance problems, one must find a focus. Here I will think about the performance problems of event systems from two focus points, namely: reducing the number of traversals and memory consumption.
First of all, the number of traversals. Whether it is capturing event streams or bubbling event streams, they will traverse elements, but they will all traversals starting from the top window or document. If the DOM element of the page has a deep parent-child relationship, then the more elements you traversal, the more harmful it will be, like DOM2 event processing, the greater the traversal. How to solve the problem of traversal of this event stream? My answer is no. Some friends here may have questions, why is there no more? There is an event object in the event system, which is an event. This object has a method to prevent bubbling or capture events. Why do I say that there is no one? This friend’s question makes sense, but if we want to use this method to reduce traversal, then our code will deal with the relationship between the father and son elements and the grandfather element relationship. If the page elements are nested a lot, this is a task that cannot be completed, so my answer is that the problem of traversal cannot be changed, and we can only adapt to it.
It seems that reducing traversal cannot solve the performance problem of event system, so now we can only consider memory consumption. I often hear people say that C# is very useful, but it is even better for web front-end development. We can directly drag a button to the page in the C# IDE. After the button reaches the page, the javascript code will automatically add an event to the button. Of course, the event function inside is an empty function. So I think we can place 100 buttons on the page in this way. If a single code doesn't work, there will be 100 button events processing, which is super convenient. Finally, we add a specific button event to one of the buttons to make the page run. Is this page efficient? In JavaScript, each function is an object, and each object consumes memory, so this useless 99 event function code must consume a lot of valuable browser memory. Of course, we will not do this in the real development environment, but in today's era of Ajax popular and single-page development is crazy and popular, there are so many events on a web page, which means that each event has an event function, but every operation we only trigger one event. At this time, other events are sleeping while lying down, which does not play any role and consumes computer memory.
We need a solution to change this situation, and there is indeed such solution in reality. In order to clearly describe this plan, I want to add some background knowledge first. In the discussion of DOM2 event processing, I mentioned the concept of target object. Putting aside the DOM2 event processing method, there is also the concept of target object in capture event processing and bubble event processing. The target object is the DOM element of the specific operation of the event. For example, the button in the click button operation is the target object. No matter which event processing method, the event function will contain an event object. The event object has an attribute target, the target always points to the target object, and the event object has another attribute, currentTarget, which points to the DOM element flowing to the capture or bubble event. From the above description, we know that whether it is a capture event or a bubble event, the event flow will flow to the document. If we add a click event on the document and the button on the page does not add a click event, then we click the button and we know that the click event on the document will trigger. There is a detail here that when the document click event is triggered, the target of the event target is button rather than document, so we can write the code like this:
The code copy is as follows:
<input type="button" id="btn" name="btn" value="BUTTON"/>
<a href="#" id="aa">aa</a>
document.addEventListener("click",function(evt){
var target = evt.target;
switch(target.id){
case "btn":
alert("button");
break;
case "aa":
alert("a");
break;
}
},false);
Running it, we found that the effect is the same as the event we wrote the button separately. But its benefits are self-evident. One function handles the event function of the entire page, and no event function is idle, which is perfect. This plan also has a professional name: event delegation. jQuery's delegate method is done according to this principle. In fact, the efficiency of event delegation is not only reflected in the reduction of event functions, but also reduces dom traversal operations. For example, in the above example, we add a function on the document. The document is the top-level object on the page, and the efficiency of reading it is very high. When it comes to specific object events, we do not use the dom operation but use the target attribute of the event object. All of this can only be summarized in one sentence: it is really fast, no reason to be fast.
Event delegation can also bring us a great by-product. Friends who have used jQuery should have used the live method. The feature of the live method is that you can add event operations to page elements. Even if this element does not exist on the page at present, you can add its events. After understanding the event delegation mechanism, the principle of live is easy to understand. In fact, jQuery's live is done through event delegation, and live is also an efficient way to add event.
After understanding event delegation, we will find that jQuery's bind method is an inefficient method. Because it uses the original event definition method, we should use bind with caution. In fact, jQuery developers have also noticed this problem. The new version of jQuery has an on method. The on method contains all functions of bind, live and delegate methods. So I suggest that friends who have read this article should abandon the previous method of adding events and use on functions to add events.
Event delegation has another advantage. In the example of event delegation in the above article, I added events to the document. Here I want to make a comparison. In jQuery, we are used to putting the definition of DOM element events in the ready method, as shown below:
The code copy is as follows:
$(document).ready(function(){
XXX.bind("click",function(){});
});
The ready function is executed after the page DOM document is loaded. It is executed first than the onload function. This has many benefits in advance. One of the benefits is also to improve performance. The definition of event like jQuery is also a standard practice. I believe some friends must bind certain events outside of ready, and finally find that the button will be invalid. This invalid scenario sometimes takes a moment, and it will be fine after a while. Therefore, we often ignore the principle of this problem and do not bind events in the ready function. This operation is actually binding events before the DOM is loaded, and under this time period, we actually bind events to them. , It is very likely that some elements have not been constructed on the page, so event binding will be invalid. Therefore, the reason for ready to define events is to ensure that all elements of the page are loaded to define events of the DOM element after they are loaded. However, problems can be avoided when using event delegates. For example, binding events to a document, the document represents the entire page, so it is the earliest time to load it. Therefore, it is difficult to achieve event delegates on the document, and it is also difficult to cause the browser to report "XXX function undefined". To summarize this feature: Event delegate code can be run at any stage of page loading, which will provide developers with greater freedom in improving web page performance or enhancing web page effects.
OK, this article has been written. Good night.