Those inexplicable bugs

I haven’t written anything for two weeks. I feel a bit confused recently. I would like to share the bug I met some time ago. $(‘#point’).on(‘click’,’.read-more’,function () {}); It almost ignores the fact that the nature of the problem is actually caused by event delegation. Without further ado, the code I see every day:

    $(document).on('click'.function (e) {
        consol.log('jquery Event Binding ')});Copy the code

The second:

   document.addEventListener('click'.function (e) {
        consol.log('Native event binding')});Copy the code

The third:

   var id = setInterval(function () {
        console.log('Timer loop event binding')}, 1000);Copy the code

The above code, which many of you write every day, seems to be simple event binding, but it can often give us unexpected results, especially in this era of SPA, AJAX page partial refresh is so popular. So what is event binding, which causes the program to repeat itself? This thing to say to clear, seems not so simple, or with a section of test code to illustrate it. You can copy it locally and try it yourself:

<! DOCTYPE html> <html> <head> <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button class="add_but"> </button> <div id="point">fdfsdf
</div>
<script src="https://cdn.bootcss.com/jquery/1.8.3/jquery.js"></script>    
<script>
    var count=1;
    var example = {
        getData:function () {
            var data ={
                content:'df'+count++,
                href:' '
            };
            this.renderData(data);
        },
        renderData:function (data) {
            document.getElementById('point').innerHTML='<div>this is a '+data.content+'';
           $('#point').on('click'.'.read-more'.function () {
            alert('Point of occurrence'); }) / *setInterval(function () {
                console.log('fdfdfg'); }, 2000); */ /* Document. QuerySelector ()'body').addEventListener('click'.function (e) {
                if(e.target.classList.contains('read-more')){
                    alert('Point of occurrence'); }})*/}}; document.querySelector('.add_but').addEventListener('click'.function (e) {
        example.getData();
        e.stopImmediatePropagation();
    });
</script>
</body>
</html>Copy the code

The above is a test code I wrote to clarify this matter, you can copy it and try. When we click the button on the page, the example.getData() function is invoked. When the data is retrieved successfully, the content of the element named Point in the page is refreshed locally, and the read-more A tag in the content is bound with an event. When the element is loaded for the first time, the page is fine, the ‘accident point’ pops up once, when the second refresh is triggered, you’ll see that it pops up twice, when the third time, you’ll see that it pops up three times, and so on… OMG, what is wrong with this program? I clearly delete the previous binding elements before each event binding. Why, the deleted body feels like it is still in action. Finally, I asked the great god around me, and suddenly realized that the original binding had been there all along, but the binding was stored in a place called the event queue. He was not in the main thread of the loop execution, so he drew a picture that needed tacit understanding to understand, and reluctantly took a look.

The event queue

Restore the truth

In fact, the above code is specially written for the test of the code, in addition to the timer, the other two click events in a normal way, repeated execution will not occur, normal code:

// jquery event binding; $('#point .read-more').on('click'.function () {
            alert('Point of occurrence'); }) // native JS events are directly bound; document.querySelector('.read-more').addEventListener('click'.function (e) {
            alert('Point of occurrence');
        })Copy the code

See the difference? Instead of bubbling event delegates, you bind events directly to the added elements. So Dom events make sense. A dynamically added element is bound to an event, and when the element is removed, the event bound to it is actually removed from the event binding queue, rather than the above test code, which gives the impression that the element is removed but its bound event is still in memory. But remember, this was a misunderstanding, test the code above is to give people the illusion, because we don’t have to dynamically add element binding events, and only in the form of the event delegation, actually event is binding on the # point element, it has always been, using event up to let the program know we click on the link element dynamic addition. In the test, we used native JS to reproduce the event delegate. Jquery’s ON binding event works in much the same way.

document.querySelector('body').addEventListener('click'.function (e) {
     if(e.target.classList.contains('read-more')){
          alert('Point of occurrence'); }})Copy the code

Ways to remove bugs

The timer

This is the easiest error to make, and certainly the easiest to solve, because when you set the timer, it returns a number, which should be a number in the event queue timer, something like 9527; The step is to set a global variable to hold the return value ID, and each time the timer is set, the timer is cleared by id

clearInterval(intervalId); IntervalId &&clearInterval(intervalId); // intervalId=setInterval(function () {
                console.log('fdfdfg'); }, 2000);Copy the code

Dom events

In fact, as we said above, the most straightforward way is not to use event delegation, but to use direct binding; If you do use event delegates to bind events, it is unbind. Jquery provides the unbind function to unbind events, but after jquery 1.8 this method is deprecated in favor of the off method. $(‘#point’).off(‘click’,’.read-more’).

For defective solutions, add flags

After the first binding, the flag is set. The next time the binding is performed, the program knows that there is already a binding on this node and no need to add it.

     var flag = false;
     var example = {
        getData: function () {
            var data = {
                content: 'df' + count++,
                href: ' '
            };
            this.renderData(data);
        },
        renderData: function (data) {
            document.getElementById('point').innerHTML = '<div>this is a ' + data.content + ''; ! flag && $('#point').on('click'.'.read-more'.function () {
                alert('Point of occurrence'+data.content);
            });
            flag = true; }};Copy the code

Logically, it doesn’t look like a problem, but on closer inspection, it does. Refresh when our second, third, the content of the pop-up box or and simulation for the first time to refresh after clicking on the content of the popup, or ‘accident point df1, rather than as content increasing, why, feel the inside of the event queue callback functions are stored separately, the data are deep copy, rather than a reference. It’s a little hard to understand, I don’t know exactly why, if anyone can explain, please let me know.

And a tail

In the end, in fact, when writing some programs, the event binding causes the program to repeat execution, which rarely happens. It usually occurs when we write plug-ins. Plug-ins need to adapt to a variety of call environments, so it is very important to prevent the event binding inside plug-ins. Closertb. Site all rights reserved