How to build a resizable DIV that works with overflow: auto?

Viewed 44

I have a resizable DIV that is resizable in the 4 sides. How ever, in some cases the content of this DIV may exceed its size. So I've added the option overflow: auto, but when I use this option, the component responsible to resize the DIV moves with the scroll.

Here's the code: https://codesandbox.io/s/23kt1?file=/index.html

How can I fix the component always on the edges of the DIV in such a way that it's not moved when the user scroll the div?

1 Answers

You need an extra container for the dots to align to.

function resizeable() {
  var resizers = document.querySelectorAll('.n, .s, .w, .e, .nw, .ne, .se, .sw');
  const min = 40;
  for (let i = 0; i < resizers.length; i++) {
    const currentResizer = resizers[i];
    const element = currentResizer.parentElement;
    const parent = currentResizer.parentElement.parentElement;
    let p;
    let c;
    let original_w = 0;
    let original_h = 0;
    let parent_x = 0;
    let parent_y = 0;
    let parent_sx = 0;
    let parent_sy = 0;
    let child_x = 0;
    let child_y = 0;
    let mouse_x = 0;
    let mouse_y = 0;
    let scale_x = 0;
    let scale_y = 0;
    let scroll_x = 0;
    let scroll_y = 0;
    // Mouse events
    currentResizer.addEventListener('mousedown', function(e) {
      first(e);
      document.addEventListener('mousemove', resize);
      document.addEventListener('mouseup', stopResize);
      e.preventDefault();
    });

    // First location & width
    function first(e) {
      c = element.getBoundingClientRect();
      child_y = c.top;
      child_x = c.left;
      p = parent.getBoundingClientRect();
      parent_y = p.top;
      parent_x = p.left;
      parent_sy = parent.scrollTop;
      parent_sx = parent.scrollLeft;
      scroll_y = window.scrollY;
      scroll_x = window.scrollX;
      original_w = parseFloat(c.width).toFixed(2);
      original_h = parseFloat(c.height).toFixed(2);
      scale_y = parseFloat(c.height / element.offsetHeight).toFixed(2);
      scale_x = parseFloat(c.width / element.offsetWidth).toFixed(2);
      mouse_y = e.pageY - scroll_y;
      mouse_x = e.pageX - scroll_x;
    }
    // Resize process
    function resize(e) {
      element.style.position = "absolute";
      if (currentResizer.classList.contains('se')) {
        const width = e.pageX - scroll_x - child_x;
        const height = e.pageY - scroll_y - child_y;
        if (width > min) {
          element.style.width = (width / scale_x) + 'px';
        }
        if (height > min) {
          element.style.height = (height / scale_y) + 'px';
        }
        if (parent.scrollTop < parent.scrollHeight) {
          parent.scrollTop = parent.scrollHeight;
        }
        if (parent.scrollLeft < parent.scrollWidth) {
          parent.scrollLeft = parent.scrollWidth;
        }
      } else if (currentResizer.classList.contains('sw')) {
        const width = original_w - (e.pageX - scroll_x - child_x);
        const height = e.pageY - scroll_y - child_y;
        if (height > min) {
          element.style.height = (height / scale_y) + 'px';
        }
        if (width > min) {
          element.style.left = e.pageX - scroll_x - parent_x + parent_sx + 'px';
          element.style.width = (width / scale_x) + 'px';
        }
        if (parent.scrollTop < parent.scrollHeight) {
          parent.scrollTop = parent.scrollHeight;
        }
      } else if (currentResizer.classList.contains('ne')) {
        const width = e.pageX - child_x - scroll_x;
        const height = original_h - (e.pageY - mouse_y - scroll_y);
        if (width > min) {
          element.style.width = (width / scale_x) + 'px';
        }
        if (height > min) {
          element.style.height = (height / scale_y) + 'px';
          element.style.top = e.pageY - parent_y - scroll_y + parent_sy + 'px';
        }
        if (parent.scrollLeft < parent.scrollWidth) {
          parent.scrollLeft = parent.scrollWidth;
        }
      } else if (currentResizer.classList.contains('nw')) {
        const width = original_w - (e.pageX - scroll_x - child_x);
        const height = original_h - (e.pageY - scroll_y - mouse_y);
        if (width > min) {
          element.style.left = e.pageX - parent_x - scroll_x + parent_sx + 'px';
          element.style.width = (width / scale_x) + 'px';
        }
        if (height > min) {
          element.style.height = (height / scale_y) + 'px';
          element.style.top = e.pageY - parent_y - scroll_y + parent_sy + 'px';
        }
      } else if (currentResizer.classList.contains('e')) {
        const width = e.pageX - scroll_x - child_x;
        if (width > min) {
          element.style.width = (width / scale_x) + 'px';
        }
        if (parent.scrollLeft < parent.scrollWidth) {
          parent.scrollLeft = parent.scrollWidth;
        }
      } else if (currentResizer.classList.contains('s')) {
        const height = e.pageY - scroll_y - child_y;
        if (height > min) {
          element.style.height = (height / scale_y) + 'px';
        }
        if (parent.scrollTop < parent.scrollHeight) {
          parent.scrollTop = parent.scrollHeight;
        }
      } else if (currentResizer.classList.contains('w')) {
        const width = original_w - (e.pageX - scroll_x - child_x);
        if (width > min) {
          element.style.width = (width / scale_x) + 'px';
          element.style.left = (e.pageX - scroll_x - parent_x + parent_sx) + 'px';
        }
      } else if (currentResizer.classList.contains('n')) {
        const height = original_h - (e.pageY - scroll_y - mouse_y);
        if (height > min) {
          element.style.height = (height / scale_y) + 'px';
          element.style.top = e.pageY - parent_y - scroll_y + parent_sy + 'px';
        }
      }

    }
    // When mouse released stop
    function stopResize(e) {
      first(e);
      document.removeEventListener('mousemove', resize);
    }
  }
}
resizeable();
body {
  width: 1200px;
}

div {
  position: absolute;
  background-color: grey;
}

.regular {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: red;
  overflow: auto;
  position: absolute;
}

.n,
.s,
.w,
.e,
.nw,
.ne,
.se,
.sw {
  position: absolute;
  width: 18px;
  height: 18px;
  border: 1px solid grey;
  border-radius: 20px;
  background-color: #fff;
  z-index: 1;
}

.n:hover,
.s:hover,
.w:hover,
.e:hover,
.nw:hover,
.ne:hover,
.se:hover,
.sw:hover {
  background-color: red;
}

.nw {
  top: -10px;
  left: -10px;
  cursor: nw-resize;
}

.ne {
  top: -10px;
  left: calc(100% - 10px);
  cursor: ne-resize;
}

.sw {
  top: calc(100% - 10px);
  left: -10px;
  cursor: sw-resize;
}

.se {
  top: calc(100% - 10px);
  left: calc(100% - 10px);
  cursor: se-resize;
}

.n {
  top: -10px;
  left: calc(50% - 10px);
  cursor: n-resize;
}

.w {
  top: calc(50% - 10px);
  left: -10px;
  cursor: w-resize;
}

.e {
  top: calc(50% - 10px);
  left: calc(100% - 10px);
  cursor: e-resize;
}

.s {
  top: calc(100% - 10px);
  left: calc(50% - 10px);
  cursor: s-resize;
}

.container {
  position: relative;
  width: 300px;
  height: 300px;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="/src/styles.css" />
</head>

<body>


  <div class="container">
    <div class="nw"></div>
    <div class="ne"></div>
    <div class="sw"></div>
    <div class="se"></div>
    <div class="n"></div>
    <div class="s"></div>
    <div class="w"></div>
    <div class="e"></div>
    <div class="regular">

      <p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
      <p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
      <p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
      <p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
      <p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
      <p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
      <p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
      <p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
      <p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
      <p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>

    </div>
  </div>



  <script src="/src/index.js"></script>
</body>

</html>

Related