Search code examples
javascripthtmlcssresize

Creating resizable two panels with HTML & CSS & JS


So, I want to create two panels that are resizable with a horizontal draggable button. I showed in the picture below:

enter image description here

So, have it can be created with HTML, CSS and JavaScript?

What i tried first?

I used interact.js's Resizable feature; but it didn't solve my problem. It just resized one panel, but I wanted to resize two panels at once.

The Code I tried:

<style>
  .container {
    width: 100%;
height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #eee;
}

.container .section {
    resize: horizontal;
    padding: 0;
    margin: 0;
    width: 100% !important;
    height: 100% !important;
    box-sizing: border-box;
}

.container .section:nth-child(odd) {
    border-right: 5px solid rgb(17,17,17);
}

.container .section:nth-child(even) {
    border-left: 5px solid rgb(17,17,17);
}
</style>
<div class="container">
   <div class="section">Section 0</div>
   <div class="section">Section 1</div>
</div>

<script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>
<script>
interact('.container .section:nth-child(odd)')
        .resizable({
            edges: { top: false, left: false, bottom: false, right: true },
            listeners: {
            move: function (event) {
                let { x, y } = event.target.dataset

                x = (parseFloat(x) || 0) + event.deltaRect.left
                y = (parseFloat(y) || 0) + event.deltaRect.top

                Object.assign(event.target.style, {
                width: `${event.rect.width}px`,
                height: `${event.rect.height}px`,
                transform: `translate(${x}px, ${y}px)`
                })

                Object.assign(event.target.dataset, { x, y })
            }
            }
        })

        interact('.container .section:nth-child(even)')
        .resizable({
            edges: { top: false, left: true, bottom: false, right: false },
            listeners: {
            move: function (event) {
                let { x, y } = event.target.dataset

                x = (parseFloat(x) || 0) + event.deltaRect.left
                y = (parseFloat(y) || 0) + event.deltaRect.top

                Object.assign(event.target.style, {
                width: `${event.rect.width}px`,
                height: `${event.rect.height}px`,
                transform: `translate(${x}px, ${y}px)`
                })

                Object.assign(event.target.dataset, { x, y })
            }
            }
        })
</script>

What i wanted to do is; when I resized the second panel's width at 30% of viewport-width, the first panel's width will be at 70% of viewport-width.


Solution

  • To achieve the desired functionality, you can use the mousemove event on the draggable button to update the width of the two panels dynamically. Here's an example implementation using HTML, CSS, and vanilla JavaScript:

    const draggable = document.querySelector('.draggable');
    const leftPanel = document.querySelector('.left-panel');
    const rightPanel = document.querySelector('.right-panel');
    
    let isDragging = false;
    let initialWidth;
    
    draggable.addEventListener('mousedown', (event) => {
      isDragging = true;
      initialWidth = leftPanel.offsetWidth;
    });
    
    document.addEventListener('mousemove', (event) => {
      if (isDragging) {
        const currentWidth = initialWidth + event.clientX - draggable.offsetLeft;
        const containerWidth = leftPanel.offsetWidth + rightPanel.offsetWidth;
        const leftPanelWidth = (currentWidth / containerWidth) * 100;
        const rightPanelWidth = 100 - leftPanelWidth;
    
        leftPanel.style.width = `${leftPanelWidth}%`;
        rightPanel.style.width = `${rightPanelWidth}%`;
      }
    });
    
    document.addEventListener('mouseup', (event) => {
      isDragging = false;
    });
    .container {
      position: relative;
      display: flex;
      height: 100vh;
      overflow: hidden;
    }
    
    .panel {
      height: 100%;
      overflow-y: auto;
      padding: 20px;
    }
    
    .left-panel {
      flex-grow: 1;
      background-color: #eee;
    }
    
    .right-panel {
      flex-grow: 1;
      background-color: #fff;
    }
    
    .draggable {
      position: absolute;
      top: 0;
      bottom: 0;
      width: 10px;
      cursor: ew-resize;
      background-color: #ccc;
      z-index: 1;
    }
    <div class="container">
      <div class="panel left-panel">
        <h2>Left Panel</h2>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus accumsan in sem in aliquam.</p>
      </div>
      <div class="draggable"></div>
      <div class="panel right-panel">
        <h2>Right Panel</h2>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus accumsan in sem in aliquam.</p>
      </div>
    </div>

    In this implementation, we use the mousedown event on the draggable button to set a flag that indicates the button is being dragged. We also store the initial width of the left panel.

    On mousemove, we calculate the new width of the left panel based on the distance the mouse has moved since the mousedown event. We also calculate the new width of the right panel by subtracting the new left panel width from 100.

    Finally, on mouseup, we clear the dragging flag to indicate that the button is no longer being dragged.

    Note that this implementation assumes that the container element has a fixed height (in this case, 100vh). If the container's height can vary, you may need to adjust the calculations to take this into account.