In the browser DOM event, some events will be triggered continuously with the user's operations. For example: resize the browser window, scroll the browser page, and mousemove. That is to say, when the user triggers these browser operations, if the corresponding event handling method is bound to the script, this method will be triggered continuously.
This is not what we want, because sometimes if the event handling method is relatively large, DOM operations such as complex, and constantly triggering such events will cause performance losses, resulting in a decline in user experience (slow UI response, browser stuck, etc.). So usually we will add logic to the corresponding event to delay execution.
Generally speaking, we use the following code to implement this function:
var COUNT = 0;function testFn() { console.log(COUNT++); }// When the browser resizes // 1. Clear the previous timer // 2. Add a timer to delay the real function testFn by 100 milliseconds to trigger window.onresize = function () { var timer = null; clearTimeout(timer); timer = setTimeout(function() { testFn(); }, 100);};Careful students will find that the above code is actually wrong. This is a problem that novices will make: the return value of the setTimeout function should be saved in a relative global variable, otherwise a new timer will be generated every time the resize is resized, which will not achieve the effect we send.
So we modified the code:
var timer = null;window.onresize = function () { clearTimeout(timer); timer = setTimeout(function() { testFn(); }, 100);};At this time, the code is normal, but there is another new problem - a global variable timer is generated. This is something we don't want to see. If this page has other functions, it is also called timer. Different codes will cause conflicts before. To solve this problem, we need to use a language feature of JavaScript: closures closures. Readers can learn about related knowledge in MDN. The modified code is as follows:
/** * Function throttling method* @param Function fn Delay calling function* @param Number delay How long is the delay* @return Function Method for delaying execution*/var throttle = function (fn, delay) { var timer = null; return function () { clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); }};window.onresize = throttle(testFn, 200, 1000);We use a closure function (throttle throttling) to put the timer internally and return the delay processing function. This way, the timer variable is invisible to the outside, but the timer variable can also be accessed when the internal delay function is triggered.
Of course, this writing method is not easy for novices to understand. We can change the writing method to understand:
var throttle = function (fn, delay) { var timer = null; return function () { clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); }};var f = throttle(testFn, 200);window.onresize = function () { f();};Here is a point of view: the function returned by the throttle after being called is the real function that needs to be called when the onresize is triggered
Now it seems that this method is close to perfection, but it is not the case in actual use. For example:
If the user constantly resizes the browser window size, the delay processing function will not be executed once
So we need to add another function: when the user triggers resize, it should be triggered at least once within a certain period of time. Since it is within a certain period of time, this judgment condition can take the current time milliseconds, and each function call subtracts the current time from the last call time, and then judges that if the difference is greater than a certain period of time, it will be directly sent, otherwise it will still follow the timeout delay logic.
What needs to be pointed out in the following code is:
1. The function of previous variables is similar to that of timer. They are both recorded the last identifier and must be relative global variables.
2. If the logic process follows the logic "triggered at least once", then the function call needs to be completed to reset the previous to the current time. Simply put, it is: compared with the next last time, it is actually the current one.
/** * Function throttling method* @param Function fn Delay call function* @param Number delay How long is the delay* @param Number at least how long is it triggered* @return Function Method for delaying execution*/var throttle = function (fn, delay, at least) { var timer = null; var previous = null; return function () { var now = +new Date(); if ( !previous ) previous = now; if ( now - previous > at least ) { fn(); // Reset the last start time to the end time of this time previous = now; } else { clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); } }};practice:
We simulate a scene of throttling when a window scrolls, that is, when the user scrolls the page downward, we need to throttling some methods, such as: calculating the DOM position, etc., which requires continuous operation of DOM elements.
The complete code is as follows:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>throttle</title></head><body> <div style="height:5000px"> <div id="demo" style="position:fixed;"></div> </div> <script> var COUNT = 0, demo = document.getElementById('demo'); function testFn() {demo.innerHTML += 'testFN was called' + ++COUNT + 'time<br>';} var throttle = function (fn, delay, at least) { var timer = null; var previous = null; return function () { var now = +new Date(); if ( !previous ) previous = now; if ( at least && now - previous > at least ) { fn(); // Reset the last start time to the end time of this time previous = now; clearTimeout(timer); } else { clearTimeout(timer); timer = setTimeout(function() { fn(); previous = null; }, delay); } } } }; window.onscroll = throttle(testFn, 200); // window.onscroll = throttle(testFn, 500, 1000); </script></body></html>We use two cases to test the effect, namely adding at least triggering at least the at least and not adding:
// case 1window.onscroll = throttle(testFn, 200);// case 2window.onscroll = throttle(testFn, 200, 500);
Case 1 is manifested as: testFN will not be called during the page scrolling process (cannot be stopped), and it will be called once until it stops, which means that the last setTimeout in the throttle is executed, and the effect is as shown in the figure (see the original gif image):
Case 2 is manifested as: during the page scrolling process (cannot be stopped), testFN will be delayed by 500ms for the first time (from at least delay logic), and then execute at least every 500ms, the effect is as shown in the figure
So far, the results we want to achieve have been basically completed. Readers can think about some subsequent auxiliary optimizations by themselves, such as: function this pointing, return value saving, etc.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.