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:

It’s Primal — Everything we do in UI space is based on mouse movement. It’s Re-usable — Write once, deploy in any project (even at work.) 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. 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.” 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.

1. Track Movement

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

2. Dragging State

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.

3. Mouse Travel Distance

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

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:

Mouse Button Pressed Down (mousedown) Mouse Button Unpressed (mouseup) 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 </script>

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!

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