Description
Problem
In jupyterlab/jupyterlab#9757 (comment) @krassowski writes:
Analysis:
I am currently focusing on the delay visible when moving mouse out of the menu and the over again (this is universal problem when the test > notebook is open and affects all menus, including menu bar and context menu):
After lower than expected improvements in #273 and failure to eliminate this even when commenting > out handlers for
mousemove
,mouseenter
andmouseleave
I went a step further and removed event listeners from lumino's Menu for >mousemove
,mouseenter
andmouseleave
altogether. To my surprise the issue did not go away - it was still present when looking in the > profiler; we don't see any layout shifts but a lot of longRecalculate Style
tasks; those are preceded bySchedule Style Recalculation
> which helps to narrow down where it is coming from.The profiler does not show why those come to be - just that they follow browser-native
HitTest
(and precede the actual event emission). > The raw JSON profile file hints that the HitTest goes to the notebook cell (not unexpectedly, this is where my cursor lands after leaving > the menu) and also mentions intersection observer (IntersectionObserverController::computeIntersections) kicking in from time to time (due > to lazy notebook rendering I suppose):The following experiment suggests that this might be triggered by
HitTest
when mouse leaves the menu (and thenHitTest
for some weird > reason causes style recalculation on Chromium/Blink):
- on top of initial changes in lumino#273 add a
<div class="test">
with >z-index
=z-index of menu
-1
- make that div
position:absolute
and cover entire viewport (height:100%; widht: 100%
) so that it occludes the rest of the DOM (except > for the menu)- optionally add
background: grey; opacity: 0.5
just to see the div- see that moving mouse out of the menu and back again does not cause the delay anymore
And this time there is no style recalculation anymore nor scheduling of it:
Now the behaviour of
Hit test
is standardized by W3C: https://www.w3.org/wiki/Hit_Testing, but it is not well understood topic when it > comes to performance optimization. There is one [question on SO](https://stackoverflow.com/questions/41830529/> optimizing-native-hit-testing-of-dom-elements-chrome) which concerns the Hit Test taking a lot of time itself, but in our case the problem > is not the presence of Hit Test but that it sometimes schedules style recalculation.So why a Hit Test might schedule a style recalculation even though nothing changed in the DOM? Why does it not happen every time but just > some times? Well
:hover
pseudo selector might be the answer. We can test that by adding:<style>.test:hover{background:red!important}</style>This indeed restores the
Schedule Style Recalculation
andRecalculate Style
tasks (although this time style recalculation is super fast > because only our test div is affected):While I only tested on Chromium-based browsers, I would not be surprised if other engines are affected too.
Potential solution
In a further comment, @krassowski proposes
Proposed solution
Could we add a
<div>
occluding background DOM when menu is open? The div would be effectively transparent (this could be implemented e.g. > withopacity: 0.01
) and clicking on it would detach it from DOM and close the menu. This means that when user has a menu open and clicks > outside, e.g. on a link, it would not open (unless they click again after menu gets closed).The UX would be consistent with how native menus (both context menus and application-level menus) work in Chrome (on Ubuntu), in GNOME and I > suspect in many other applications/environments: neither hover nor click works on background elements when menu is open. Firefox does a bit > of both: clicks do not work on background elements when context menu is open, but hover styles do; clicks do work in Firefox though when the > topbar menu is open (as do hover styles).
And it be super fast regardless if the number of nodes in the DOM is 6000 or 600000.
Edit: just in case if someone was wondering, setting
pointer-events: none;
on thejp-LabShell
does not solve the issue.