Explain Codes LogoExplain Codes Logo

How to make HTML element resizable using pure JavaScript?

javascript
responsive-design
performance
best-practices
Alex KataevbyAlex Kataev·Sep 14, 2024
TLDR

Develop a draggable resize handle, track the mousemove event, and dynamically tweak your HTML element's size. Here's a brief example:

const elem = document.getElementById('resizable'); elem.style.position = 'relative'; const handle = document.createElement('div'); Object.assign(handle.style, { width: '8px', height: '8px', background: 'blue', position: 'absolute', right: '0', bottom: '0', cursor: 'nwse-resize' }); elem.append(handle); // Because resizing is a hungry job, we feed it Pixels, lots of them! handle.addEventListener('mousedown', e => { e.preventDefault(); const startX = e.pageX, startY = e.pageY; const startWidth = parseInt(document.defaultView.getComputedStyle(elem).width, 10); const startHeight = parseInt(document.defaultView.getComputedStyle(elem).height, 10); const doDrag = (e) => { elem.style.width = startWidth + e.pageX - startX + 'px'; elem.style.height = startHeight + e.pageY - startY + 'px'; }; const stopDrag = () => { document.documentElement.removeEventListener('mousemove', doDrag, false); document.documentElement.removeEventListener('mouseup', stopDrag, false); }; document.documentElement.addEventListener('mousemove', doDrag, false); document.documentElement.addEventListener('mouseup', stopDrag, false); }, false);

This approach allows resizing starting from the bottom-right corner, employing mouse events directly on the document.documentElement. It amends user experience, liberating the mouse movement beyond the element area only.

Refining the user interaction

Keeping page responsiveness

Securing page responsiveness while elements resize is pivotal. Users insist on smooth interactions. To enhance performance, avoid exhaustive operations in the mousemove listener and leverage requestAnimationFrame for superior updates.

let rafPending = false; const doDrag = (e) => { if (rafPending) return; // Because sometimes queues can be a drag, get it...? rafPending = true; requestAnimationFrame(() => { elem.style.width = startWidth + e.pageX - startX + 'px'; elem.style.height = startHeight + e.pageY - startY + 'px'; rafPending = false; }); };

Browser compatibility

Though the given code operates efficiently in most browsers, ensure you test it in different browsers, for example, Firefox. Cater for the limitations of older IE versions (IE <9) by providing a fallback or a simple notice to these users.

Enhancing user experience

Improving the handle's size, visibility and user-friendliness significantly enhances user experience. You can update the handler's style on hover and add transition effects for a smoother feel.

handle.style.transition = 'all 0.2s'; // Because blue can be too cool for some, let's add some heat! handle.addEventListener('mouseover', () => { handle.style.background = 'red'; }); handle.addEventListener('mouseout', () => { handle.style.background = 'blue'; });

Two-directional resizing

The default example enables resizing from the bottom-right corner. For better control, add multiple handles for varying directions: left, top, or four corners.

// ... existing code to create the bottom-right handle // Function to add handles function addResizeHandle(element, position) { const handle = document.createElement('div'); handle.style.position = 'absolute'; handle.style.background = 'blue'; handle.style.width = '8px'; handle.style.height = '8px'; handle.style[position] = '0'; handle.style.cursor = getCursorForPosition(position); // ... Adding dragging logic specific to this handle element.appendChild(handle); } // Create and append handles for all corners addResizeHandle(elem, 'top-left'); addResizeHandle(elem, 'top-right'); addResizeHandle(elem, 'bottom-left'); addResizeHandle(elem, 'bottom-right');

Setting up constraints

To prevent too small or large resizing, set a min and max limit.

const maxWidth = 800; const maxHeight = 600; // ... inside your doDrag function const newWidth = Math.min(Math.max(startWidth + e.pageX - startX, minWidth), maxWidth); const newHeight = Math.min(Math.max(startHeight + e.pageY - startY, minHeight), maxHeight); elem.style.width = newWidth + 'px'; elem.style.height = newHeight + 'px';

Accessing styles efficiently

Repeatedly using getComputedStyle, especially during mouse movement events, can be costly. Cache the initial values:

const computedStyle = getComputedStyle(elem);

Later, use computedStyle to get width and height when initializing startWidth, startHeight.