Writing Your Own Custom Mouse UI Class In Vanilla JavaScript

By making my own custom Mouse class I was able to build on top of it to implement all of the following completely custom UI controls — including the re-sizable and draggable toolbar itself.

Image for post
Image for post
Manipulating CSS elements using a simple JavaScript Mouse class explained in this UI tutorial.

Information overwhelm is a real problem for new coders.

If you are learning how to code but doubting whether you are learning “the right subjects” (you know, the ones that buff up your experience and bring you one step closer to becoming employable) look no farther than the mouse class.

If you haven’t programmed your very own mouse class yet — you should.

Here are the top reasons to write your own mouse class:

  1. It’s Primal — Everything we do in UI space is based on mouse movement.
  2. It’s Re-usable — Write once, deploy in any project (even at work.)
  3. It’s Practical — I can’t count how many things you can build based on it by having the mouse class in your arsenal of re-usable modules.
  4. It’s Reputable — Publish it to GitHub. Next time someone asks: “Why not just download an NPM module for that?” You can say: “Thank you, thank you very much, but I am a real programmer and tons of people are already downloading the one I wrote. It’s how I got my current employer’s attention.
  5. It’s The Cure For Impostor Syndrome — Your boss asks you to create a custom slider or scroll-bar control. (It’s the job you wouldn’t take anyway because you thought you didn’t qualify for it.) So, what do you want to do? Download an existing NPM module, write one line of object instantiation code and tell your boss you did it? Or learn how to actually write code and be a practical programmer rather than hearsay one. Thought so.

What Are We Doing?

Mouse class sounds fine. But what function should it have? How should we decide which properties and methods our class should have?

First we need to identify a set of problems we want our class to solve.

We need to know mouse x and y position globally at any given time.

Image for post
Image for post

Dragging things around with the mouse is what it’s all about. This is the core function of the class we’re going to write.

We’ll simply define it as this.isDragging = false; in our class constructor.

A mouse can be openly floating across the screen space or it can be dragging an object (or a slider knob, for example) around if its pressed down on it.

The drag state of the mouse should be global. It should be tracked independently of whether it is clicking on any particular object or not. At any time in our application.

This way we when writing our own custom UI controls later we should never worry about calculating mouse or deal with custom mouse events ever again.

We must know the distance the mouse traveled since last time mouse button was clicked. At this point we will memorize its click location. We’ll use a simple object literal for that: this.memory = {x: 60, y: 50}:

We’ll also track and store mouse distance in mouse.difference.x and .y

Image for post
Image for post

The distance is calculated while the mouse is moving and the mouse button is still pressed. That’s the only span of time during which we need to know it. Once button is released again all coordinates are reset to 0.

Again, the distance is calculated globally, independent of any UI controls. When we code our own UI controls, we will have drag distance in pixels readily available to us via this mouse class, not the UI control class.

3 Main Mouse Events

If we can only capture the following events we can write the code around it:

  1. Mouse Button Pressed Down (mousedown)
  2. Mouse Button Unpressed (mouseup)
  3. Detect Mouse Movement (mousemove)

We will use addEventListener to listen to each event separately:

/* replace event-name with mousedown, mouseup or mousemove */
document.body.addEventListener("event-name", (e) => { ... }

We must use arrow function (e) = > { … } in our class as callback for events.

If you use standard function() { … } syntax then the this object in it will not refer to our class object instance. Something we need to avoid because we want to write to our class properties (this.current.x or this.memory.x for example) and not some other execution context.

Knowing all this — it’s easy to implement the Mouse class.

It is really just 3 mouse event listeners executed in Mouse class constructor.

Here’s What We Got So Far

The complete source code for our newly created Mouse class:

export class Mouse {    constructor() {        this.current    = {x: 0, y: 0}; // Current mouse position on
// the screen, at any time
// regardless of state
this.memory = {x: 0, y: 0}; // Memorized mouse position
// (for measuring dragging
// distance)
this.difference = {x: 0, y: 0}; // Difference this.inverse = {x: 0, y: 0}; // Handle negative distance this.dragging = false; // Not dragging by default // Mouse Button Was Clicked!
document.body.addEventListener("mousedown", (e) => {
// We are not currently dragging
if (this.dragging == false) {
// Start tracking dragging coordinates
this.dragging = true;
// Memorize mouse click location
this.memory.x = this.current.x;
this.memory.y = this.current.y;
// Reset inverse coordinates
this.inverse.x = this.memory.x;
this.inverse.y = this.memory.y;
// Mouse Button Was Released!
document.body.addEventListener("mouseup", (e) => {
// Mouse button has been released, disable drag state
this.dragging = false;
// Reset everything to 0
this.current.x = 0;
this.current.y = 0;
this.memory.x = 0;
this.memory.y = 0;
this.difference.x = 0;
this.difference.y = 0;
this.inverse.x = 0;
this.inverse.y = 0;
// Capture Mouse Move Event
document.body.addEventListener("mousemove", (e) => {
// Get the mouse coordinates
this.current.x = e.pageX;
this.current.y = e.pageY;
// If mouse is currently being dragged
if (this.dragging) {
this.difference.x = this.current.x - this.memory.x;
this.difference.y = this.current.y - this.memory.y;
if (this.current.x < this.memory.x)
this.inverse.x = this.current.x;
if (this.current.y < this.memory.y)
this.inverse.y = this.current.y;
} // end constructor
}; // end class

Save this code in mouse.js

Note: mouse.inverse.x and mouse.inverse.y refer to the upper left corner of the selection box in dragging state, regardless which way user drags the mouse. Moving up and left will create a box with “negative” values. Moving down and right will create a box with “positive” (normal) values.

When you click the mouse button and drag upward and to the left, then the top left corner of the selection will produce negative values. But no matter what direction you drag, mouse.inverse.x / y ensures to always refer to the upper left corner’s x and y position.

Learn It, Write It, Save It, Reuse It.

Every time you need automatic mouse movement and drag distance info in your app, simply add it to your project as follows.

Place both index.html (below) and mouse.js into the same folder.

<script type = "module">    import { Mouse } from "./mouse.js";    let mouse = null;

window.onload = () => { // Wait until all media is loaded
mouse = new Mouse(); // Instantiate mouse object
// ===== Automatically available throughout the app ===== mouse.x; // Current x position
mouse.y; // Current y position
mouse.memory.x; // Last clicked location on x
mouse.memory.y; // Last clicked location on y
mouse.isDragging; // Was mouse clicked down and dragging now?
mouse.distance.x; // Distance of the drag on x
mouse.distance.y; // Distance of the drag on y
mouse.inverse.x; // Always upper left corner of drag box
mouse.inverse.y; // Always upper left corner of drag box

You can reuse this class without having to deal with mouse coordinates and drag distance ever again anywhere in your future applications — from now on they are openly exposed globally in your app.

You can start building custom UI controls by including this class in your app.

Thanks For Reading

Now go build your own UIs!

Image for post
Image for post

Check out my CSS Book on Amazon — a pictorial approach to CSS.

Written by

Issues. Every webdev has them. Published author of CSS Visual Dictionary https://amzn.to/2JMWQP3 few others…

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store