Skip to content

[Bug]: After Chrome update 144.0.7559.59 mouseleave event is not fired. #3141

Description

@Debayan-Saha1406

Current versus expected behaviour

Expected Behavior- The hovered elements should be removed when i leave the activity
Actual Behavior- The hovered elements stay even if I leave the activity and stays there as long as I dont refresh.
The event listener bts doesnt fire and mousenter fires when i leave the activity too.

Image

Steps to reproduce

  1. Hover over the activity
  2. Move the mouse out of the activity the tooltip and the other icons stay as shown in image

Version

"jointjs": "^3.6.5",

What browsers are you seeing the problem on?

Chrome

What operating system are you seeing the problem on?

Windows

Activity

  1. Debayan-Saha1406 commented on Jan 20, 2026

    @Debayan-Saha1406
    Author

    Re: Mouseleave event fires for link but not for element listener.
    DblClick and Dragging is also not working on the paper designer.

    Anybody with a proper fix will be recommended

  2. kumilingus commented on Jan 20, 2026

    @kumilingus
    Contributor

    Could you please provide steps to reproduce?
    Is there any online JointJS example that also shows the issue?

  3. Debayan-Saha1406 commented on Jan 20, 2026

    @Debayan-Saha1406
    Author

    No I couldnt find any example.
    Steps are like this

    1. I hover over an activity -> mouseenter triggers and I add a delete button to that activity on runtime
    2. On hovering out from the activity-> Earlier mouseleave used to trigger that would used to delete those buttons so that activity retains the original state. But now mouseleave never fires and those icons stay attached.
    3. One more problem is it stops dragging and dropping. Earlier I could drag drop any activity individually but now it doesnt work the whole design drags.
    4. One thing that I have noticed is if i comment out the code for mouseenter then the activity becomes draggable and droppable individually but i lose the functionality of the hover
    5. It signifies something has changed in mouseneter mouseleave with the new chrome which has affected the functionality.
  4. kumilingus commented on Jan 20, 2026

    @kumilingus
    Contributor

    Could you please provide us with a code example that does these exact steps? Thank you.

  5. Debayan-Saha1406 commented on Jan 20, 2026

    @Debayan-Saha1406
    Author

    paper.on({
    'cell:pointerup': function (elementView, evt, x, y) {
    let coordinates = new joint.g.Point(x, y);
    let elementAbove = elementView.model;
    let elementBelow = this.model.findModelsFromPoint(coordinates).find(function (el) {
    return (el.id !== elementAbove.id && el.attributes.type !== DesignerElementType.StartActivity);
    });

        if (!startingSource) {     //link target drag from end ellipss to element
          startingSource = elementView.model;
        }
    
        const endNodeId = 'end-node';
    
        if (elementView.model.isLink() && startingSource.isLink()) { // not element found in target
          if (elementBelow && startingSource.getSourceElement() && elementBelow == startingSource.getSourceElement()) {
            elementView.model.remove();
          }
          if (!startingSource.attributes.target.port && !startingSource.attributes.target.id && !elementBelow) {
            elementView.model.remove();
          }
        }
        if (elementBelow) {
          let flag = portHelpers.validateCapacity(elementBelow, elementBelow.ports.id);
    
          if (flag && startingSource.isLink() && elementBelow.attr("label/id") === endNodeId) {
            startingSource.target(elementBelow);
            _thisRef._manageWorkflowDesignerService.addCell(graph.toJSON().cells.find(x => x.id === elementView.model.attributes.id));
          }
        }
    
        graph.attributes.cells.models.forEach((model, index) => {
          if (model.attributes.isCustomActivity) {
            let activityPort = model.attributes.label.portId;
            if (activityPort) {
              let activityPortId = '#' + activityPort;
              if (document.querySelectorAll(activityPortId)[1]) {
                let noPortId = document.querySelectorAll(activityPortId)[1];
                noPortId.classList.add(WorkflowActivityClass.NoPort);
              }
            }
          }
        });
    
      },
      'element:pointerdown': (elementView, evt, x, y) => {
        if (elementView.model.attributes.isCustomActivity ||
          elementView.model.attributes.type === DesignerElementType.StartActivity ||
          elementView.model.attributes.type === DesignerElementType.EndActivity) {
          this.updateActivitySelection(elementView.model.attributes.id);
        } else {
          this.selectedActivity = null;
        }
        this.closeSidebar();
      },
      'element:pointerup': function (elementView, evt, x, y) {
        delete this.dragStartPosition;
      },
      'element:mouseenter': (elementView) => {
        if (elementView.model.attributes.isCustomActivity) {
          this._activityName = elementView.model.attributes?.activityDisplayName;
    
          //Hiughlighting the activity
          handleActivitySelection(elementView?.model?.attributes, WorkflowActivityClass.RectHover, WorkflowActivityClass.PortActive, true);
    
          let activityPort = elementView.model?.attributes?.label?.portId;
    
          if (activityPort) {
            let activityPortId = '#' + activityPort;
            if (document.querySelectorAll(activityPortId)[1]) {
              let noPortId = document.querySelectorAll(activityPortId)[1];
              noPortId.classList.add(WorkflowActivityClass.NoPort);
            }
          }
    
          //Creating tooltip and remove button
          const [circle, binPath, linePaths] = createRemoveButton((evt) => { this.removeConfirmation() }, elementView.model.attributes.type);
    
          elementView.el.appendChild(circle);
          elementView.el.appendChild(binPath);
          elementView.el.appendChild(linePaths);
    
          if (elementView.model.attributes.type != DesignerElementType.YesNoActivity) {
            const [circleClone, pathClone, pathClone1] = createCloneButton((evt) => { this.cloneActivity(graph, this._businessControlService.selectedWorkflowActivity) }, elementView.model.attributes.type);
    
            elementView.el.appendChild(circleClone);
            elementView.el.appendChild(pathClone);
            elementView.el.appendChild(pathClone1);
          }
    
          this._createActivityTooltip(elementView, this._activityName);
        }
    
        if (elementView.model.attributes.type === DesignerElementType.StartActivity) {
          this.startActivityElement.rect.setAttribute('class', WorkflowActivityClass.StartEndHover);
          this.startActivityElement.port.setAttribute('class', WorkflowActivityClass.StartPortActive);
          this.startActivityElement.text.setAttribute('fill', '#3884FF');
    
          this._createActivityTooltip(elementView, this._translateService.instant('lbl_start_activity_info'));
        }
    
        if (elementView.model.attributes.type === DesignerElementType.EndActivity) {
          this.endActivityElement.circle.setAttribute('class', WorkflowActivityClass.StartEndHover);
          this.endActivityElement.text.setAttribute('fill', '#3884FF');
        }
        //Bringning element to the front to avoid overlap
        elementView.paper.cells.appendChild(elementView.el);
      },
    
      'element:mouseleave': (elementView) => {
        if (this.selectedActivity === null || this.selectedActivity?.id !== elementView?.model?.attributes?.id) {
          unselectWorkflowActivity(elementView.model.attributes, this.startActivityElement, this.endActivityElement);
        } else {
          selectWorkflowActivity(elementView.model.attributes, this.startActivityElement, this.endActivityElement);
        }
    
        //Removig tooltip and remove button
        Array.from(elementView.el.children).forEach((el: any) => {
          if (el.classList.contains("remove-button-tool") || el.classList.contains("remove-button-path") || el.classList.contains("activity-name-label"))
            el.remove();
        });
      },
      'blank:pointerdown': function (e, x, y) {
        _thisRef.selectedActivity = null;
        this.dragStartPosition = { x: x * _thisRef.zoomLevel, y: y * _thisRef.zoomLevel };
        _thisRef.closeSidebar();
      },
      'blank:pointermove': function (evt, x, y) {
        if (this.dragStartPosition)
          paper.translate(
            evt.offsetX - this.dragStartPosition.x,
            evt.offsetY - this.dragStartPosition.y)
      },
      'link:pointerdblclick': (linkView) => {
        this.closeSidebar();
      },
      'link:pointerup': (e, x, y) => {
        startingSource = e.model;
    
      },
      'link:mouseenter': (elementView) => {
        let removeButton = new joint.elementTools.Remove({
          markup: [{
            tagName: 'circle',
            selector: 'button',
            attributes: {
              'r': 12,
              'fill': '#16191D',
              'stroke': '#3884FF',
              'cursor': 'pointer',
              'class': 'remove-button-tool'
            }
          }, {
            tagName: 'path',
            selector: 'icon',
            attributes: {
              'd': 'M3 4.5V6H4.5V21C4.5 21.3978 4.65804 21.7794 4.93934 22.0607C5.22064 22.342 5.60218 22.5 6 22.5H18C18.3978 22.5 18.7794 22.342 19.0607 22.0607C19.342 21.7794 19.5 21.3978 19.5 21V6H21V4.5H3ZM6 21V6H18V21H6ZM15 1.5H9V3H15V1.5Z',
              'fill': '#E6EAF0',
              'stroke': 'none',
              'pointer-events': 'none',
              'transform': 'matrix(.8, 0, 0, .8, -9.5, -9.5)',
              'class': 'remove-button-path clone-path '
            }
          },
          {
            tagName: 'path',
            selector: 'icon1',
            attributes: {
              'd': 'M-2 -3v6h1.5V-3H-2ZM2 -3v6h1.5V-3H2Z',
              'fill': '#3884FF',
              'stroke': 'none',
              'pointer-events': 'none',
              'transform': 'matrix(.8, 0, 0, 1.2, -0.5, 1)',
              'class': 'remove-button-path clone-path-light'
            }
          }]
        });
        let verticesTool = new joint.linkTools.Vertices();
        let segmentsTool = new joint.linkTools.Segments();
        let targetArrowheadTool = new joint.linkTools.TargetArrowhead();
    
        let toolsView = new joint.dia.ToolsView({
          tools: [
            // verticesTool, segmentsTool,
            //targetArrowheadTool,
            removeButton
          ]
        });
    
        elementView.addTools(toolsView);
    
        let documentElement = document.getElementById(elementView?.id);
        if (documentElement) {
          let element = documentElement?.getElementsByClassName("connection");
          if (element.length > 0) {
            element[0].classList.add(WorkflowActivityClass.LinkSelected);
          }
          element = documentElement?.getElementsByClassName("marker-target");
          if (element.length > 0) {
            element[0].classList.add(WorkflowActivityClass.LinkEndMarkerSelected);
          }
        }
        elementView.showTools();
        //this.closeSidebar();
      },
      'link:mouseleave': function (elementView) {
    
        let documentElement = document.getElementById(elementView?.id);
        if (documentElement) {
          let element = documentElement?.getElementsByClassName("connection");
          if (element.length > 0) {
            element[0].classList.remove(WorkflowActivityClass.LinkSelected);
          }
          element = documentElement?.getElementsByClassName("marker-target");
          if (element.length > 0) {
            element[0].classList.remove(WorkflowActivityClass.LinkEndMarkerSelected);
          }
        }
        elementView.hideTools();
      }
    
    });
    

    These are the various listeners attached. Out of this element: mouseEnter does wat I had written above element:mouseleave never fires so those icons stay as it is.
    The surprising thing is that link's mouseenter and mouseleave works but not elements.
    Can u please try to give a fix or a workaround

  6. kumilingus commented on Jan 20, 2026

    @kumilingus
    Contributor

    Could you provide a code that can be run? You can fork this JSFiddle for example.

  7. Debayan-Saha1406 commented on Jan 20, 2026

    @Debayan-Saha1406
    Author

    https://github.com/user-attachments/assets/a2513ae7-2b34-41f0-8cc8-c7b9af1c23ce
    I have uploaded a video too. U can see. Its in Typescript.

    paper.on({

    'element:mouseenter': (elementView) => {
    //Code here works
    },

    'element:mouseleave': (elementView) => {
    // Code here doesnt get fired Why is this happening.
    },

    'link:mouseenter': (elementView) => {
    // Code here works
    },

    'link:mouseleave': (elementView) => {
    // COde here works
    }

    });

    My question is why doesnt mouseleave fire when i am going out of the activity. It used to fire till 5 days back when chrome updated to 144.

  8. Debayan-Saha1406 commented on Jan 20, 2026

    @Debayan-Saha1406
    Author

    Which JointJs versin is ur fiddle working on?
    In ur fiddle it works I can get mouseEnter and mouseLeave clearly but in my application it doesnt detect the mouseleave clearly. May be its not able to understand that it has left the actibity

  9. kumilingus commented on Jan 20, 2026

    @kumilingus
    Contributor

    I understand your question. We don't have an answer unless you provide us with an example that we can debug.

    This is JJ v3.6 JSFiddle.

    It could be related to this change in Chrome: https://developer.chrome.com/release-notes/144#interoperable_pointer_and_mouse_boundary_events_after_dom_changes

  10. Debayan-Saha1406 commented on Jan 20, 2026

    @Debayan-Saha1406
    Author

    I have found the RCA it was due to a tooltip layer which was later appended like this
    elementView.paper.cells.appendChild(elementView.el); This line was added to bring the tooltip at the top layer. Due to this mouseleave event doesnt fire. Can u tell a better alternative to bring this on top if possible

  11. kumilingus commented on Jan 20, 2026

    @kumilingus
    Contributor

    Please provide a code that can be run? You can fork this JSFiddle for example.
    If we see the code, we might be able to suggest an alternative.

  12. Debayan-Saha1406 commented on Jan 22, 2026

    @Debayan-Saha1406
    Author

    Ys this may be the issue. Do u have a way to fix this.

  13. kumilingus commented on Jan 22, 2026

    @kumilingus
    Contributor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions