An HTML Ruler With Vanilla JavaScript
For one of our projects, we have to update a bunch of preexisting React components to make them match a redesign given in Figma. This implies having to adjust margins, paddings, and relative positions, making the “real” components a pixel-perfect replica of the “designed” ones.
The problem with pixel-perfect stuff is that you have to measure your components by the pixel. And pixels are tiny 😅. So, I find myself creating “ruler” divs many times a day. Whenever I need to check some distance or size, I have to open the developer tools, edit some component’s HTML, add a new div
and then set some styles on it:
position: absolute;
left: 0;
top: 0;
width: 4px;
height: 40px;
background: lime;
With that ruler div
in place, then I tweak its position and size, and use it to check for margins, paddings, and alignment issues.
But that feels really tedious, so I’ve decided to automate the process. And yes, I know there are some ruler extensions in the Chrome Web Store. In fact, I’ve used a ruler extension in the past, but I had to uninstall it. I don’t want to go the extension way again, so I will build the ruler myself.
Also, I see this as an opportunity to build something simple but useful — at least for me — and to do it using vanilla JavaScript: no transpilers, no loaders, no bundlers, no BS (Build Step, of course).
First step: enough with the repetition
As I’ve said, I’m inserting the ruler manually. So the first step is to automate creating and styling the div
, and then inserting it into the document:
In the snippet above, the ruler
function creates a new div
element. The ruler will appear as a semi-transparent red square centered on the page.
The function returns a function to remove the ruler from the page. The snippet ends with a trick so it can be evaluated many times, each time removing the old ruler and creating a new one (this way I can keep tweaking the implementation easily).
Second step: move the ruler around
OK, now inserting the ruler is straightforward. But positioning it next to the element to be measured still needs some manual tweaking of the ruler style.
My first instinct is to use the mouse to drag the ruler into position, so I will add some event handlers:
First things first: in order for the ruler to be draggable, its draggable
attribute must be set to true
. Then, the drag & drop API events are used to move the ruler:
- When the
dragstart
event is fired on the ruler, the distance between the mouse and the ruler top left corner is captured. We can use it once the drag ends to determine how much to move the upper left corner of the ruler element. We store it as a JSON string, using thesetData
method. - The
dragover
event handler is attached to thedocument
, because it’s mandatory for an element to act as a drop target (and we want to drop our ruler anywhere in the document). - Once the drag operation finishes, the
drop
event is fired and the event handler in thedocument
updates the ruler position. We get the mouse offset position from thedragstart
event and we parse it. The final ruler position (that is, its top left corner position) is calculated as the position of the mouse where the drop event fires minus the stored offset.
We also update the removeRuler
function to get rid of any event handler once the ruler is gone:
Third step: resize the ruler
Now we can add and remove a ruler, and also move it around using the mouse. The only thing that’s left to do is resizing the ruler, as we still have to do it manually through the developer tools.
For this we are going to use the keyboard, so we need to add a keydown
event handler to the ruler element. But for the element to be able to process keyboard events, it has to be focused. So we start by setting a tabIndex
and giving the ruler the focus just after it is inserted in the page, and also when clicked:
Now we can define the event handler. And, because adjusting the position of the ruler using the mouse is hard when you need precision, I decided to let the user move the ruler using the keyboard too:
When the user presses one of the cursors or the Vim movement keys (h
, j
, k
, and l
), we check whether the control
key is also pressed. If it is pressed, we update the ruler size. Otherwise, we update its position.
There is a goodie in there: if the user is holding the shift
key, we use 10px increments to resize or move the ruler.
The changeSize
and changePosition
functions look like this:
We also update the removeRuler
function to get rid of the event handler (I know, there’s no need for the handler to be removed, as we are also removing the ruler element, but I prefer being consistent 🤷♂):
Fourth step: getting feedback
We can insert a ruler element, move it around with the mouse and move it and resize it using the keyboard. The only thing that’s left is knowing what is the size of the ruler. That’s an important thing, because a ruler without marks is mostly useless 😅.
To do this, we will set the ruler title
attribute, so if the mouse stops over the ruler, its size will be displayed in a tooltip. But this is a slow and sometimes frustrating UI, so we will also add a keyboard shortcut to print the size to the console.
First, we define some functions to display the ruler information:
Then we update changeSize
so that every time the ruler is resized the title is updated and the information logged to the console:
We update the ruler keydown
handler, so we can force the current size to be logged to the console pressing the i
key:
Final touches: minor improvements and launching the snippet
Minor improvements
The final code includes some minor improvements:
- The ruler has a “tracking mode” that can be toggled pressing the
t
key. When it is enabled, the ruler will follow your mouse, so you don’t need to find it and drag it. - The ruler can be “rotated” by pressing the
r
key (this will switch the width and height dimensions). - The ruler can be removed by pressing the
Esc
key.
Launching the snippet
If you are using Chrome, it turns out that you can save and run snippets of JavaScript from the Command Menu. So I just created a new snippet called “ruler”, I pasted the snippet code, and now I can insert a ruler inside the page just typing cmd-o
and then !ruler<enter>
😃.
That’s it: using a pinch of CSS and some event handlers I’ve built my own HTML ruler. I hope you find it as useful as I do.
And now, back to the Build Step 😅.