Scroll and Fall

Scroll and Fall
Photo by Pawel Czerwinski / Unsplash

Working on browser unit test with Laravel Dusk I've been hunted for many days on a specific error: "element click intercepted". This is generated in two situations: when "Element FOOBAR is not clickable at point (X, Y). Other element would receive the click" it means that the desired element is overlapped by something else, and "Element is not clickable at point" (with no mentions to "other" elements) it means that the selected node is just outside of the viewport. Those both happened to me ways too often.

The documentation mentions a method scrollIntoView which should scroll the viewport until the given element, to assure it is clickable and procede with operations, but many times - apparently due random but very frequent conditions - the "element click intercepted" error was raised crashing the whole execution. So I had to investigate on the reason why scrollIntoView failed so miserably most of the times, I found it just wraps an homonym JS native function, and began to play with it within the browser's console. To find that:

  • Chrome ignores the behavior parameter, always doing a smooth scrolling. Which introduces a latency between the invokation of the function and the actual conclusion (when the target element is expected to really be within the viewpoint)
  • by default (which is the Laravel's implementation), scrollIntoView scrolls the viewport until the target element is on the top of it (or the page is already scrolled to the bottom)

Here come the problem: in my own setup, "until the target element is on the top of it" meant "until the target element in below the Bootstrap navbar, which is in position: fixed and always overlaps contents on the very top of the page". And what I experienced was a devastating, time-driven combination of

  • trying to click the element before it was scrolled into the viewport, due the latency of smooth scrolling, generating a "Element is not clickable at point" error
  • trying to click the element after scrollIntoView completed the action to place the element on the top, and below the navbar, generating a "Other element would receive the click" error
  • trying to click the element in the short interval between the start and the end of the scroll, which resulted in correct behaviour and the passage to the next step

I didn't find any way to suppress the smooth scroll, so I had to add a short pause() after each invokation to wait for the complete scrolling operation. About the position of the scrolled element, I implemented my own Dusk Browser Macro to scroll it on the bottom, not the top, of the page, to completely skip the navbar problem.

\Laravel\Dusk\Browser::macro('scrollView', function ($selector) {
  $selector = addslashes($this->resolver->format($selector));
  $this->driver->executeScript("document.querySelector(\"$selector\").scrollIntoView({block: 'end'});");
  return $this;

Please note: the same navbar problem exists also on the opposite side, at the bottom, if you have Laravel Debugbar activated within the Dusk environment. Or, of course, any other position: fixed item on top or bottom of the page.