Why is there a difference in the task/microtask execution order when a button is programmatically clicked vs DOM clicked?

后端 未结 3 1711
走了就别回头了
走了就别回头了 2021-01-01 16:23

There\'s a difference in the execution order of the microtask/task queues when a button is clicked in the DOM, vs it being programatically clicked.

3条回答
  •  死守一世寂寞
    2021-01-01 17:00

    So, Chrome answer just because it's interesting (see T.J Crowder's excellent answer for the general DOM answer).

    btn.click();
    

    Calls into HTMLElement::click() in C++ which is the counterpart of the DOMElement:

    void HTMLElement::click() {
      DispatchSimulatedClick(nullptr, kSendNoEvents,
                             SimulatedClickCreationScope::kFromScript);
    }
    

    Which basically does some work around dispatchMouseEvent and deals with edge cases:

    void EventDispatcher::DispatchSimulatedClick(
        Node& node,
        Event* underlying_event,
        SimulatedClickMouseEventOptions mouse_event_options,
        SimulatedClickCreationScope creation_scope) {
      // This persistent vector doesn't cause leaks, because added Nodes are removed
      // before dispatchSimulatedClick() returns. This vector is here just to
      // prevent the code from running into an infinite recursion of
      // dispatchSimulatedClick().
      DEFINE_STATIC_LOCAL(Persistent>>,
                          nodes_dispatching_simulated_clicks,
                          (MakeGarbageCollected>>()));
    
      if (IsDisabledFormControl(&node))
        return;
    
      if (nodes_dispatching_simulated_clicks->Contains(&node))
        return;
    
      nodes_dispatching_simulated_clicks->insert(&node);
    
      if (mouse_event_options == kSendMouseOverUpDownEvents)
        EventDispatcher(node, *MouseEvent::Create(event_type_names::kMouseover,
                                                  node.GetDocument().domWindow(),
                                                  underlying_event, creation_scope))
            .Dispatch();
    
      if (mouse_event_options != kSendNoEvents) {
        EventDispatcher(node, *MouseEvent::Create(event_type_names::kMousedown,
                                                  node.GetDocument().domWindow(),
                                                  underlying_event, creation_scope))
            .Dispatch();
        node.SetActive(true);
        EventDispatcher(node, *MouseEvent::Create(event_type_names::kMouseup,
                                                  node.GetDocument().domWindow(),
                                                  underlying_event, creation_scope))
            .Dispatch();
      }
      // Some elements (e.g. the color picker) may set active state to true before
      // calling this method and expect the state to be reset during the call.
      node.SetActive(false);
    
      // always send click
      EventDispatcher(node, *MouseEvent::Create(event_type_names::kClick,
                                                node.GetDocument().domWindow(),
                                                underlying_event, creation_scope))
          .Dispatch();
    
      nodes_dispatching_simulated_clicks->erase(&node);
    }
    

    It is entirely synchronous by design to make testing simple as well as for legacy reasons (think DOMActivate weird things).

    This is just a direct call, there is no task scheduling involved. EventTarget in general is a synchronous interface that does not defer things and it predates microtick semantics and promises :]

提交回复
热议问题