DEV Community

Frank Etoundi
Frank Etoundi

Posted on

3 1

Resizing a div using CSS resize, but the resizing is done at a fixed dimension (snap grid)

TLDR; View the working example on jsfiddle.net

Let's create a solution that resizes a div at fixed 50px increments of its parent container, with smooth transitions, debouncing and ResizeObserver for monitoring.

HTML Setup:

<div class="container">
  <div class="resizable"></div>
</div>
Enter fullscreen mode Exit fullscreen mode

CSS Setup:

.container {
  width: 500px;
  height: 400px;
  border: 2px solid #ccc;
  position: relative;
  overflow: hidden;
}

.resizable {
  width: 250px;
  height: 200px;
  background: #3498db;
  position: absolute;
  resize: both; /* This enables the resizing */
  overflow: auto;
  min-width: 50px;
  min-height: 50px;
  max-width: 100%;
  max-height: 100%;
  transition: all 0.1s ease-out; /* This is important to ensure a smooth drag */
}
Enter fullscreen mode Exit fullscreen mode
  • resize: both enables resizing in both directions
  • transition: all 0.1s ease-out adds smooth transitions to reduce visual glitching
  • min-width/height: 10% and max-width/height: 100% set the boundaries
  • overflow: auto handles content overflow
  • Container has overflow: hidden to prevent content spilling

Javascript Setup:

const resizable = document.querySelector(".resizable")
const container = document.querySelector(".container")

const snapPercent = 20 // Change to fit your needs
const snapPixel = 50 // Change to fit your needs
const usePixels = false // Change this to false to use percentage.

// Function to snap to fixed pixels increments
function snapToGridPixel(size, parentSize) {
  const snappedSize = Math.round(size / snapPixel) * snapPixel
  const value = Math.max(snapPixel, Math.min(snappedSize, parentSize))
  return Math.min(value, parentSize) + "px"
}

// Function to snap to fixed percentage increments
function snapToGridPercentage(size, parentSize) {
  const percentage = (size / parentSize) * 100
  const snappedPercentage = Math.round(percentage / snapPercent) * snapPercent
  return Math.max(snapPercent, Math.min(100, snappedPercentage)) + "%"
}

function snapToGrid(size, parentSize) {
  return usePixels
    ? snapToGridPixel(size, parentSize)
    : snapToGridPercentage(size, parentSize)
}

// Debounce function
function debounce(func, delay) {
  let timeoutId
  return function (...args) {
    clearTimeout(timeoutId)
    timeoutId = setTimeout(() => {
      func.apply(this, args)
    }, delay)
  }
}

// Debounced resize handler
const handleResize = debounce((entries) => {
  for (let entry of entries) {
    const { width, height } = entry.contentRect
    const parentWidth = container.offsetWidth
    const parentHeight = container.offsetHeight

    // Snap to fixed increments
    const newWidth = snapToGrid(width, parentWidth)
    const newHeight = snapToGrid(height, parentHeight)

    // Apply the snapped sizes with max bounds
    resizable.style.width = newWidth
    resizable.style.height = newHeight
  }
}, 100) // 100ms delay

// ResizeObserver with debounced handler
const resizeObserver = new ResizeObserver(handleResize)

// Start observing the resizable element
resizeObserver.observe(resizable)
Enter fullscreen mode Exit fullscreen mode

Let's break down the key components:

JavaScript with ResizeObserver:

  • snapToGrid function calculates the nearest 10% increment
  • Converts current size to percentage of parent
  • Rounds to nearest 20% using Math.round(percentage / 20) * 20
  • Clamps values between 20% and 100%
  • ResizeObserver continuously monitors size changes and applies snapping

The ResizeObserver ensures that whenever the user resizes the div, it immediately snaps to the nearest 10% increment, while the CSS transition makes the snapping motion smooth rather than instantaneous.

Debouncing

Debouncing ensures that a function only runs after a certain period of inactivity. It delays execution until after the triggering event has stopped firing for a specified amount of time.

Why Use Debouncing?

  • Performance: Prevents excessive function calls that could slow down your application
  • Efficiency: Reduces unnecessary computations or API calls
  • User Experience: Ensures actions complete only when the user has finished interacting

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (0)

Postmark Image

20% off for developers who'd rather build features than debug email

Stop wrestling with email delivery and get back to the code you love. Postmark handles the complexities of email infrastructure so you can ship your product faster.

Start free

👋 Kindness is contagious

Value this insightful article and join the thriving DEV Community. Developers of every skill level are encouraged to contribute and expand our collective knowledge.

A simple “thank you” can uplift someone’s spirits. Leave your appreciation in the comments!

On DEV, exchanging expertise lightens our path and reinforces our bonds. Enjoyed the read? A quick note of thanks to the author means a lot.

Okay