Glenn Teitelbaum Glenn Teitelbaum - 5 months ago 20
jQuery Question

How do you create 3 adjustable divs?

What I want:

| A | | B | | C |
^ ^


When you move the
handles
left and right
A
,
B
, and
C
resize accordingly

| A | | B | | C |


What I have is the
||
between
B
and
C
sliding, but not resizing
B
and all I get on the other one is the resize cursor. Basically
C
is a curtain and covers
A
and
B
. I did get min size working for
C
.

| A | C |


I broke somebody else's perfectly good code to get this far:



var isResizing = false,
who='',
lastDownX = 0;

$(function () {
var container = $('#container'),
left = $('#left'),
right = $('#right'),
middle = $('#middle'),
hand2 = $('#hand2'),
handle = $('#handle');

handle.on('mousedown', function (e) {
isResizing = true;
who=e.target.id;
lastDownX = e.clientX;
});

$(document).on('mousemove', function (e) {
var temp, min;
// we don't want to do anything if we aren't resizing.
if (!isResizing)
return;
min=container.width() * 0.1;
temp = container.width() - (e.clientX - container.offset().left);
if (temp < min)
temp = min;
if (who == 'handle')
right.css('width', temp);
if (who == 'hand2')
left.css('width', temp);
}).on('mouseup', function (e) {
// stop resizing
isResizing = false;
});
});

body, html {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#container {
width: 100%;
height: 100%;
/* Disable selection so it doesn't get annoying when dragging. */
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: moz-none;
-ms-user-select: none;
user-select: none;
}
#container #left {
width: 40%;
height: 100%;
float: left;
background: red;
}
#container #middle {
margin-left: 40%;
height: 100%;
background: green;
}
#container #right {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 200px;
background: rgba(0, 0, 255, 0.90);
}
#container #handle {
position: absolute;
left: -4px;
top: 0;
bottom: 0;
width: 80px;
cursor: w-resize;
}
#container #hand2 {
position: absolute;
left: 39%;
top: 0;
bottom: 0;
width: 80px;
cursor: w-resize;
}

<div id="container">
<!-- Left side -->

<div id="left"> This is the left side's content!</div>
<!-- middle -->
<div id="middle">
<div id="hand2"></div> This is the middle content!
</div>

<!-- Right side -->
<div id="right">
<!-- Actual resize handle -->
<div id="handle"></div> This is the right side's content!
</div>

</div>





Been playing with it here: https://jsfiddle.net/ju9zb1he/5/

Answer

I was looking for a solution that required less extensive CSS. It does have one minor bug(FIXED), but hopefully this should get you started. Here is a DEMO.

Also I aimed to use DOM Traversal methods like .next() and .prev() that way it wouldn't be so attribute dependent, and would be easily reusable if you needed a feature like this multiple times on a page.

Edit - Further Explanation

The idea here is onClick of a .handle we want to gather the total width (var tWidth) of the .prev() and .next() divs relative to the .handle in the DOM. We can then use the start mouse position (var sPos) to substract the amount of pixels we've moved our mouse (e.pageX). Doing so gives us the correct width that the .prev() div should have on mousemove. To get the width of the .next() div we need only to subtract the width of the .prev() div from the total width (var tWidth) that we stored onClick of the .handle. Hope this helps! Let me know if you have any further questions, however I will likely be unavailable till tomorrow.

HTML

<div class="container">
    <div id="left"></div>
    <div id="l-handle" class="handle"></div>
    <div id="middle"></div>
    <div id="r-handle" class="handle"></div>
    <div id="right"></div>
</div>

CSS

#left, #middle, #right {
    display: inline-block;
    background: #e5e5e5;
    min-height: 200px;
    margin: 0px;
}

#l-handle, #r-handle {
    display: inline-block;
    background: #000;
    width: 2px;
    min-height: 200px;
    cursor: col-resize;
    margin: 0px;
}

jQuery

var isDragging = false,
    cWidth = $('.container').width(),
    sPos,
    handle,
    tWidth;
$('#left, #middle, #right').width((cWidth / 3) - 7); // Set the initial width of content sections

$('.handle').on('mousedown', function(e){
    isDragging = true;
    sPos = e.pageX;
    handle = $(this);
    tWidth = handle.prev().width() + handle.next().width();
});

$(window).on('mouseup', function(e){
    isDragging = false;
});

$('.container').on('mousemove', function(e){
    if(isDragging){ // Added an additional condition here below
        var cPos = sPos - e.pageX;
        handle.prev().width((tWidth / 2) - cPos); // This was part of the bug...
        handle.next().width(tWidth - handle.prev().width());
        // Added an update to sPos here below
    }
});

Edit

The bug was caused by 2 things.

1) On mousemove we were dividing the total width by two, instead of an updated mouse offset.

2) The sPos was not updating on mousemove, and stayed a static number based off of the click location.

Resolution

Update the sPos on mousemove that way the mouse offset is accurately based off of the previous mousemove position, rather than the click position. When this is done we can then subtract the .next() div's width from the total width. Then we subtract our current mouse position from the remaining width. The fiddle has been updated as well.

$('.container').on('mousemove', function(e){
    var cPos = sPos - e.pageX;
    if(isDragging && ((tWidth - handle.next().width()) - cPos) <= tWidth){
        handle.prev().width((tWidth - handle.next().width()) - cPos);
        handle.next().width(tWidth - handle.prev().width());
        sPos = e.pageX;
    }
});

Edit

Added an additional condition on mousemove to prevent the drag from exceeding the total width (var tWidth).

Comments