Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making a dragbar to resize divs inside CSS grids

I have 2 boxes and a vertical div line in one unique container div (code and fiddle below).

I'm using CSS grids to position my elements inside the container

What I'd like to accomplish is to use the vertical line to resize horizontally the two boxes based on the position of the vertical line.

I apologize if the question is noobish, I am new to web development, only used Python before, already tried to google and stackoverflow search but all solutions seem overly complicated and generally require additional libraries, I was looking for something simpler and JS only.

HTML:

<div class="wrapper">   <div class="box a">A</div>   <div class="handler"></div>   <div class="box b">B</div> </div> 

CSS:

body {   margin: 40px; }  .wrapper {   display: grid;   grid-template-columns: 200px 8px 200px;   grid-gap: 10px;   background-color: #fff;   color: #444; }  .box {   background-color: #444;   color: #fff;   border-radius: 5px;   padding: 20px;   font-size: 150%;   resize: both; }  .handler{     width: 3px;     height: 100%;     padding: 0px 0;     top: 0;     background: red;     draggable: true; } 

https://jsfiddle.net/gv8Lwckh/6/

like image 492
Aquazi Avatar asked Oct 25 '17 11:10

Aquazi


People also ask

How do I make my grid resizable CSS?

Use overflow: auto; along with specifying resize: vertical/horizontal. Without setting overflow, resize will fail. Use min/max width/height values to create boundaries for resizing.

How do I resize a DIV container in CSS?

Answer: Use the CSS max-width Property You can simply use the CSS max-width property to auto-resize a large image so that it can fit into a smaller width <div> container while maintaining its aspect ratio.

Can you nest grids in CSS?

You can "nest" grids by making a grid item a grid container. These grids however are independent of the parent grid and of each other, meaning that they do not take their track sizing from the parent grid. This makes it difficult to line nested grid items up with the main grid.


1 Answers

What you intend to do can be done using CSS flexbox—there is no need to use CSS grid. The bad news is that HTML + CSS is not so smart that declaring resize and draggable will make the layout flexible and adjustable by user interaction. For that, you will have to use JS. The good news is that this is actually not too complicated.

Here is a quick screen grab of output the code below:

However, for you to understand the code I will post below, you will have to familiarize yourself with:

  • Event binding using .addEventListener. In this case, we will use a combination of mousedown, mouseup and mousemove to determine whether the user is in the middle of dragging the element
  • CSS flexbox layout

Description of the solution

Initial layout using CSS

Firstly, you will want to layout your boxes using CSS flexbox. We simply declare display: flex on the parent, and then use flex: 1 1 auto (which translates to "let the element grow, let the element shrink, and have equal widths). This layout is only valid at the initial rendering of the page:

.wrapper {   /* Use flexbox */   display: flex; }  .box {   /* Use box-sizing so that element's outerwidth will match width property */   box-sizing: border-box;    /* Allow box to grow and shrink, and ensure they are all equally sized */   flex: 1 1 auto; } 

Listen to drag interaction

You want to listen to mouse events that might have originated from your .handler element, and you want a global flag that remembers whether the user is dragging or not:

var handler = document.querySelector('.handler'); var isHandlerDragging = false; 

Then you can use the following logic to check if the user is dragging or not:

document.addEventListener('mousedown', function(e) {   // If mousedown event is fired from .handler, toggle flag to true   if (e.target === handler) {     isHandlerDragging = true;   } });  document.addEventListener('mousemove', function(e) {   // Don't do anything if dragging flag is false   if (!isHandlerDragging) {     return false;   }    // Set boxA width properly   // [...more logic here...] });  document.addEventListener('mouseup', function(e) {   // Turn off dragging flag when user mouse is up   isHandlerDragging = false; }); 

Computing the width of box A

All you are left with now is to compute the width of box A (to be inserted in the [...more logic here...] placeholder in the code above), so that it matches that of the movement of the mouse. Flexbox will ensure that box B will fill up the remaining space:

// Get offset var containerOffsetLeft = wrapper.offsetLeft;  // Get x-coordinate of pointer relative to container var pointerRelativeXpos = e.clientX - containerOffsetLeft;  // Resize box A // * 8px is the left/right spacing between .handler and its inner pseudo-element // * Set flex-grow to 0 to prevent it from growing boxA.style.width = (pointerRelativeXpos - 8) + 'px'; boxA.style.flexGrow = 0; 

Working example

var handler = document.querySelector('.handler');  var wrapper = handler.closest('.wrapper');  var boxA = wrapper.querySelector('.box');  var isHandlerDragging = false;    document.addEventListener('mousedown', function(e) {    // If mousedown event is fired from .handler, toggle flag to true    if (e.target === handler) {      isHandlerDragging = true;    }  });    document.addEventListener('mousemove', function(e) {    // Don't do anything if dragging flag is false    if (!isHandlerDragging) {      return false;    }      // Get offset    var containerOffsetLeft = wrapper.offsetLeft;      // Get x-coordinate of pointer relative to container    var pointerRelativeXpos = e.clientX - containerOffsetLeft;        // Arbitrary minimum width set on box A, otherwise its inner content will collapse to width of 0    var boxAminWidth = 60;      // Resize box A    // * 8px is the left/right spacing between .handler and its inner pseudo-element    // * Set flex-grow to 0 to prevent it from growing    boxA.style.width = (Math.max(boxAminWidth, pointerRelativeXpos - 8)) + 'px';    boxA.style.flexGrow = 0;  });    document.addEventListener('mouseup', function(e) {    // Turn off dragging flag when user mouse is up    isHandlerDragging = false;  });
body {    margin: 40px;  }    .wrapper {    background-color: #fff;    color: #444;    /* Use flexbox */    display: flex;  }    .box {    background-color: #444;    color: #fff;    border-radius: 5px;    padding: 20px;    font-size: 150%;        /* Use box-sizing so that element's outerwidth will match width property */    box-sizing: border-box;        /* Allow box to grow and shrink, and ensure they are all equally sized */    flex: 1 1 auto;  }    .handler {    width: 20px;    padding: 0;    cursor: ew-resize;    flex: 0 0 auto;  }    .handler::before {    content: '';    display: block;    width: 4px;    height: 100%;    background: red;    margin: 0 auto;  }
<div class="wrapper">    <div class="box">A</div>    <div class="handler"></div>    <div class="box">B</div>  </div>
like image 155
Terry Avatar answered Sep 22 '22 13:09

Terry