Making a resizable div in JS is not easy as you think

It was a quiet night with a perfect temperature for the brain to give 100%. “Perfect time for programming” — Hung said. As he was scrolling aimlessly through a long list of JavaScript functions, he stumbled on an empty function named makeResizableDiv(div) . His arrogant when up, “can’t be that hard” — He said with a smile on his face without knowing that the problem he’s about to encounter is going to take a great deal of his knowledge and experience to solve.

The HTML

Like every other start, he first created a html structure for the “resizable div”. “It should be a square with 4 resizers at its 4 corners” — His thought went through his naive brain and passed down to his hand then finally command all 8 of his 10 fingers to type the correct letter for the html structure.

<div class='resizable'>
<div class='resizers'>
<div class='resizer top-left'></div>
<div class='resizer top-right'></div>
<div class='resizer bottom-left'></div>
<div class='resizer bottom-right'></div>
</div>
</div>

“That should do it” — He laid back to his pillow before moving on to the css code. Closed his eyes, everything was clear in his mind, a white rectangle on a black background with 4 white circle resizer at 4 of its corners. One hand on the keyboard, his other hand quickly controls the mouse to switch to the css file. He’s actually using Sass for his project, but to facilitate major readers, I’ll write it in plain css.

It’s CSS time!

“First, the white square on the black background” — He typed in those first lines of css code into the file

body, html {
background: black;
}
.resizable {
background: white;
width: 100px;
height: 100px;
position: absolute;
top: 100px;
left: 100px;
}

The browser refreshed, he raises his hands, celebrates his small victory even when the browser window hasn’t fully loaded as if he has already done it thousands of times and know exactly how his code going to display on the browser.

The result of his css

Just like how he plans it, the whole page turned black, leaving just a lonely white square inside. His code was simple, his moves were perfect, no redundant code, no compile error, just like a skillful mysterious magician under the moonlight with his keyboard as his main tools.

His next move is clear, 4 “resizers” at 4 corners are his next targets.

Without hesitation, he types in more CSS code for all the “resizers”. His voice echoes in his head: “We should create lines to connect the resizers and those resizers should be white circles with a blue border to be more…stylish”

.resizable .resizers{
width: 100%;
height: 100%;
border: 3px solid #4286f4;
}

“We should see 4 blue lines connect 4 corners now” — He’s assuming this code will go well like the previous code but very unfortunate, his code messed up.

“What the heck is that?” — He becomes angry at the browser. Just like every other time, he never accepts the fact that he and his code are the reasons why those ugly results exist, but he always blames the browser for misunderstanding his intention.

The problem here is a classic problem, the border increased the size of the element. It’s clear that the browser is using the default value for CSS box-sizing property which is content-box .

According to the magic book “MDN Web docs” on the page about box-sizing:

Clearly, he didn’t want the content-box value but actually the border-box one. Usually, this kind of work will be handled by the reset.css file, but he forgot to add it to his project this time so he has to manually add it to his code

.resizable .resizers{
width: 100%;
height: 100%;
border: 3px solid #4286f4;
box-sizing: border-box;
}

“Everything is still under control” — He calms himself down with a typical pleasant lie.

He then quickly added style for 4 resizers

.resizable .resizers .resizer{
width: 10px;
height: 10px;
border-radius: 50%; /*magic to turn square into circle*/
background: white;
border: 3px solid #4286f4;
}

“That should do it” — He refreshed the browser

As he expected, 4 nice white circle with a blue border. The only thing left to do is to move each resizer into its rightful place.

“I can just add the absolute position to the resizers and then set their top, left, bottom and right position” — He thought.

.resizable .resizers .resizer{
width: 10px;
height: 10px;
border-radius: 50%;
background: white;
border: 3px solid #4286f4;
position: absolute;
}
.resizable .resizers .resizer.top-left {
left: -5px;
top: -5px;
cursor: nwse-resize; /*resizer cursor*/
}
.resizable .resizers .resizer.top-right {
right: -5px;
top: -5px;
cursor: nesw-resize;
}
.resizable .resizers .resizer.bottom-left {
left: -5px;
bottom: -5px;
cursor: nesw-resize;
}
.resizable .resizers .resizer.bottom-right {
right: -5px;
bottom: -5px;
cursor: nwse-resize;
}
The final result

“Just as planned!” — He smiled and switches to the JS code.

Here comes the JS

His journey continues when he pulls out an empty sheet of paper. He began drawing his plan on the sheet, with great confidence that his splendid plan will work at first try. This is what he came up:

  • The mouse trigger the mousedown event on the resizer
  • Then catch the mousemove event on the resizer to handle begin to resize
  • With the bottom-right resizer, when the user drags that resizer, the element’s width will equal to the x position of the mouse minus the x position of the element.
plan 1

“This is unacceptable, the element stopped resizing when the mouse accidentally moved out of the resizer!” — His PDD (Perfection Desiring Disorder) reemerged, his life is so boring that his made-up illness also sounds tedious.

He realized that the fix for that bug is extremely simple, instead of applying the mousedown event on the resizer, he switched it to the whole window therefore if the mouse is somehow slipped out of the resizer again, the element will still resize base on the mouse position. He also added a mouseup event on the window object so that when he releases the mouse, the element will stop resizing.

improved makeResizableDiv
Improved makeResizableDiv

Looking at the bright screen in the middle of the night, he still not satisfied. What he wants is not just expanding the width from the right side but also from the left side, he went on coding without realizing what he is about the face, is pure evil!

But he was so naive to recognize it, all that he sees is he just need to add another if condition to the resize function. “Give me a harder challenge!” — He said and completely forgot about the old saying: “Be careful what you wish for”

His desire was granted, as soon as his code executed, a big bug slams onto his face. Of course, it was a bug in his code, or else he would have been ended up running and screaming at the same time because he has a huge fear for bugs like cockroaches.

His new plan is clear:

  • If we resizing the element by dragging oppositely to the left then just reverse the algorithm.
  • The new width of the element will equal the right position of the element minus the x position of the mouse

He refreshed the browser as soon as he finished writing code for his new idea

else if (currentResizer.classList.contains('bottom-left')) {
element.style.width = element.getBoundingClientRect().right - e.pageX + 'px'
}

However, this is what he gets

Immediately realizing an undeniable truth that the browser only resize an element from left to right, he hopelessly googles for “resizing element from right to left js”. Why I used the word “hopelessly”? Because he has to try to search for it on the 3rd page of Google. Only God knows what we’re doing from the moment we switched to the 3rd page on google.

Seeing his research is going nowhere, he put his hand up in the air — “New plan !” — He said.

element_new_width = element_old_width - (mouseX - elementX)

“Wait, why subtracting the old width?” — you may wonder.

When the mouse is moved to the left, its x position actually decreased therefor if we move the mouse to the left, its x position always less than element’s x position and they will produce a negative number if we take mouseX minuselementX. Thus, if we subtract the element’s old width with a negative number, meaning that the element width will increase and if we drag the resizer to the right, the `mouseX` will greater than the `elementX` and return a positive number if we subtract `elementX` from `mouseX`, therefore decline the element width.

else if (currentResizer.classList.contains('bottom-left')) {
element.style.width = original_width - (e.pageX - element.getBoundingClientRect().left) + 'px'
}

“It’s…basically working, but in the opposite way” — He thought.

“How about shifting the x position of the element accordingly to the increased width?” — He wonders.

“If the resizer is dragged to the left, its x position should decrease so that it would seem like the right edge was never moved.” — The voice in his head keeps on echoing while he writes down and tests the new formula:

element_new_x = element_original_x + (mouseX - elementX)

“But wait, if the element’s x position is constantly changing then the calculation for the increased width will be affected if we use this method. We need to use the original elementX or to be more precise the original mouseX (The mouseX position when the user began to hold down the mouse)” — He came up with the new idea

element_new_x = element_original_x + (mouseX - original_mouseX)
element_new_width = element_old_width - (mouseX - original_mouseX)

Quickly typing some code on the keyboard

else if (currentResizer.classList.contains('bottom-left')) {
element.style.width = original_width - (e.pageX - original_mouse_x) + 'px'
element.style.left = original_x + (e.pageX - original_mouse_x) + 'px'
}

“Huge success!” — He laughed loudly in the middle of the night like a mad scientist.

His laughter stopped when he realized that he can use the same formula for resizing the height of the element. Without wasting any seconds, he wrote down 4 formulas for 4 different resizers (he has changed his formula for the bottom-right resizer to use the same method as other resizers.

bottom-right:
new_width = element_original_width + (mouseX - original_mouseX)
new_height = element_original_height + (mouseY - original_mouseY)
bottom-left:
new_width = element_original_width - (mouseX - original_mouseX)
new_height = element_original_height + (mouseY - original_mouseY)
new_x = element_original_x - (mouseX - original_mouseX)
top-right:
new_width = element_original_width + (mouseX - original_mouseX)
new_height = element_original_height - (mouseY - original_mouseY)
new_y = element_original_y + (mouseY - original_mouseY)
top-left:
new_width = element_original_width - (mouseX - original_mouseX)
new_height = element_original_height - (mouseY - original_mouseY)
new_x = element_original_x + (mouseX - original_mouseX)
new_y = element_original_y + (mouseY - original_mouseY)

All the clock’s arrows pointed up to the sky, his neck hurts, his back burns, his mind satisfied, his mom urges him to go to bed, his girlfriend urges him to go to bed, well…if he has one. Anyway, he closed his laptop, unlike other times, this time he closed it with joy. He lay on his warm bed, waiting for a good night sleep and those next challenges to come.

His source code can easily be found here, it’s free to copy and hasn’t been patented.

Another point worth to mention is when the element is resized to the smallest size, it began to move around on its own. The most simple way to fix this (certainly not the best way) is we check if the size of the element is reached the smallest size or not. If it is, we prevent the element from resizing. I’ve modified the code above for this case. Thanks, bhargavi sabbisetty for pointing this out.


Thanks for reading, if you enjoy this article, please leave some claps (50 is good enough) and be sure to follow me to receive more articles like this. If you wish to donate me, you can buy me a coffee. Till next time.

The happy lone guy

The happy lone guy blog

Nguyễn Việt Hưng

Written by

A web developer who love to create everything using web technology

The happy lone guy

The happy lone guy blog

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade