Handling JavaScript mouseup event outside element
I’ve been working on a personal “Smart Home” / “IoT” project recently and came across a problem that I believe a lot of other people could be facing as well. In different shape or form.
I’ve got a frontend application that remotely controls a water pump. That water pump is being turned on and off through a button.
The issue occurs when I want to handle a case of releasing the button, that is the mouseup event, when not hovering over the button. Handling this event is crucial because the pump has to stop flowing water.
Side note: I’ve got a mechanism in place to handle this on the backend as well, but let’s consider that is not the case and frontend manages all the control logic.
I’ll be using React.js to demonstrate the problem and the solution. Though the code is applicable to any JavaScript library or Vanilla JavaScript with little to no changes.
Problematic example
Let’s consider the following code:
Have a try yourself — press and hold the left mouse button when over the Pump water button, then while still keeping the mouse button pressed, leave the Pump water button and release the mouse button:
The mouseup event is not handled. The water is still flowing. It’s because the event has not originated from the Pump water button but rather some other element your pointer was hovering over at the moment.
Solution
There are many ways you can take to tackle this, but let’s have a look at one that is the first to come to mind.
What we need to do is bind a mouseup event to the document when we’re inside handleMouseDown callback. We bind the event to document because the event handler runs when you release the mouse button no matter where you are in the document. And what’s more, the event handler runs even when you release the mouse button outside the browser.
Have a play yourself:
Note that:
is the same as:
The former uses a `{ once: true }` option indicating the event should be invoked at most once after being added. When invoked the browser automatically removes it. Note that `{ once: true }` is not supported in Microsoft Internet Explorer (Microsoft Edge is OK).
See the MDN web docs for more info - EventTarget.addEventListener().
Alternative solutions
Like I mentioned before there’re many ways to solve this issue.
Rather than binding and removing mouseup event handler every time you click a button, you could have a mouseup event bound to document through the lifetime of your app. On button mousedown you could set a boolean variable isMouseDown = true and check its value inside the mouseup event handler. If it is true, you handle it.
All of that depends on what app you’re building and what is your use case.