search

Home  >  Q&A  >  body text

javascript - jQuery event binding on and off problem

code show as below:

var $test = $(document);
  
function handler1() {
  console.log( "handler1" );
  $test.off( "click", handler2 );
}
  
function handler2() {
  console.log( "handler2" );
}
  
$test.on( "click", handler1 );
$test.on( "click", handler2 );

Why does this code output handler1 and handler2 when it is clicked for the first time? Off in handler1 works but handler2 is still executed once. What is the reason?
Refer to jQuery documentation

The explanation in the document is: Adding or removing event handlers on the current element won't take effect until the next time the event is handled
Maybe my English is too bad, I feel that this place is not very accurate. , it should be The addition or deletion of the same event is invalid during the current processing. For example, I changed the two clicks to the following

var $test = $(document);
  
function handler1() {
  console.log( "handler1" );
  $test.off( "mouseup", handler2 );
}
  
function handler2() {
  console.log( "handler2" );
}
  
$test.on( "mousedown", handler1 );
$test.on( "mouseup", handler2 );

Then only handler1 will be output. It can be seen that turning off mouseup in mousedown is successful

Back to the beginning, why is the addition or deletion of the same event invalid during the current processing? (I tried to find the source code of jQuery, but it was quite complicated, so I gave up)

迷茫迷茫2778 days ago554

reply all(3)I'll reply

  • ringa_lee

    ringa_lee2017-05-18 10:53:00

    You still have to start from the source code

    Look here first: https://github.com/jquery/jqu...

    handlerQueue = jQuery.event.handlers.call( this, event, handlers );

    This is the beginning of the core code for event distribution. It can be seen that jquery needs to call this.event.handler to find the list of event callbacks when distributing events. The handlerQueue here is just a copy of a part of the internal event list, and you use off to change it. It is an internal event list and has no effect on the copied handlerQueue.

    It’s like all event callback functions are stored in the pot. When an event occurs, jquery will copy a callback, put it into the bowl, and then execute the functions in the bowl in sequence. What you change by off is only the function in the pot, and the function in the bowl is not affected.

    reply
    0
  • ringa_lee

    ringa_lee2017-05-18 10:53:00

    You can think of on as adding a processing method to an event processing array of the component, and off as removing this method from the array. When an event is triggered, jq will execute the methods in the event processing array at once. So what will happen if off is called in the event processing method? The document has made it very clear that the processing method will not be removed from the array immediately in the current event triggering loop. It will only be reflected when the bound event is triggered again.

    reply
    0
  • 天蓬老师

    天蓬老师2017-05-18 10:53:00

    Thank you everyone
    The problem has been found. The reason is that when jq internally encapsulates events, events of the same type will be added to the same array, and when the event is triggered, the traversal is a copy of this array, and off does not change the copy. Array, so Hanler2 is output when clicked for the first time. As for mousedown and mouseup, because they are different types of events, they are encapsulated into two arrays, so it is effective to turn off mouseup in mousedown. Maybe the expression is not very good, please post a few lines of main jq source code

    //获取触发的事件的数组,在上面的代码中触发click时这个数组中是有两个值的,而触发mousedown时是只有一个值的,这个值是内部封装的一个handleObj对象
    handlers = ((jQuery._data(this, "events") || {})[event.type] || [])
    
    ...
    
    //之后会把这个handlers数组根据是否委托来slice(也是这个地方复制了handlers,导致click中的off第一次点击的时候无效),然后添加到数组handlerQueue中
    handlerQueue.push({ elem: this, matches: handlers.slice(delegateCount) })
    
    ...
    //最后触发的时候是遍历handlerQueue中的matches来执行
     for (i = 0; i < handlerQueue.length && !event.isPropagationStopped() ; i++) {
                    matched = handlerQueue[i];
                    event.currentTarget = matched.elem;
    
                    for (j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped() ; j++) {
                        ...
                        }
    }

    reply
    0
  • Cancelreply