HadarZH HadarZH - 2 months ago 25
CSS Question

Draggable split-pane windows in flexbox can't get past child elements

I implemented my own split-pane with HTML/JS/CSS Flexbox.

I'm having trouble with the splitter in the following case- one of the panels has a fixed size (in px), and the other one is set to grow (

flex-grow: 1
).

In case the other panel has children with size, it won't scroll to the end. It gets stuck at the size of the children.

Can this be fixed with CSS on the split-pane panels but not on the children?

It's very important for me to use flex as I want to maintain responsiveness of my application, and want to avoid fixed sizes wherever I can.

This is a JSFiddle sample
of my question.

Code snippet given below. Thanks!



function startDrag() {
glass.style = 'display: block;';
glass.addEventListener('mousemove', drag, false);
}

function endDrag() {
glass.removeEventListener('mousemove', drag, false);
glass.style = '';
}

function drag(event) {
var splitter = getSplitter();
var panel = document.getElementById('c2');
var currentWidth = panel.offsetWidth;
var currentLeft = panel.offsetLeft;
panel.style.width = (currentWidth - (event.clientX - currentLeft)) + "px";
}

function getSplitter() {
return document.getElementById('splitter');
}

var con = document.getElementById('container');
var splitter = document.createElement('div');
var glass = document.getElementById('glass');
splitter.className = 'splitter';
splitter.id = 'splitter';
con.insertBefore(splitter, con.lastElementChild);
splitter.addEventListener('mousedown', startDrag, false);
glass.addEventListener('mouseup', endDrag, false);

.container {
display: flex;
border: 1px solid;
width: 500px;
height: 300px;
position: absolute;
}
.c1 {
background-color: blue;
flex: 1;
height: 100%;
}
.c2 {
background-color: green;
width: 150px;
}
.splitter {
width: 20px;
cursor: col-resize;
}
.glass {
height: 100%;
width: 100%;
cursor: col-resize;
display: none;
position: absolute;
}
.grandchild {
background-color: red;
width: 50px;
height: 50px;
}

<div id="container" class="container">
<div id="glass" class="glass"></div>
<div class="c1">
<div class="grandchild"></div>
</div>
<div id="c2" class="c2"></div>
</div>




Answer

It gets stuck at the size of the children

This is expected behavior when using a flexbox. I guess if you want to scroll to the end then you can use position: absolute for the grandchild relative to c1:

.grandchild {
  background-color: red;
  width: 50px;
  height: 50px;
  position: absolute;
  top: 0;
  left: 0;
}

Give overflow: hidden to c1 too:

.c1 {
  background-color: blue;
  flex: 1;
  height: 100%;
  position: relative;
  overflow: hidden;
}

Cheers!

function startDrag() {
  glass.style = 'display: block;';
  glass.addEventListener('mousemove', drag, false);
}

function endDrag() {
  glass.removeEventListener('mousemove', drag, false);
  glass.style = '';
}

function drag(event) {
  var splitter = getSplitter();
  var panel = document.getElementById('c2');
  var currentWidth = panel.offsetWidth;
  var currentLeft = panel.offsetLeft;
  panel.style.width = (currentWidth - (event.clientX - currentLeft)) + "px";
}

function getSplitter() {
  return document.getElementById('splitter');
}

var con = document.getElementById('container');
var splitter = document.createElement('div');
var glass = document.getElementById('glass');
splitter.className = 'splitter';
splitter.id = 'splitter';
con.insertBefore(splitter, con.lastElementChild);
splitter.addEventListener('mousedown', startDrag, false);
glass.addEventListener('mouseup', endDrag, false);
.container {
  display: flex;
  border: 1px solid;
  width: 500px;
  height: 300px;
  position: absolute;
}

.c1 {
  background-color: blue;
  flex: 1;
  height: 100%;
  position: relative;
  overflow: hidden;
}

.c2 {
  background-color: green;
  width: 150px;
}

.splitter {
  width: 20px;
  cursor: col-resize;
}

.glass {
  height: 100%;
  width: 100%;
  cursor: col-resize;
  display: none;
  position: absolute;
}

.grandchild {
  background-color: red;
  width: 50px;
  height: 50px;
  position: absolute;
  top: 0;
  left: 0;
}
<div id="container" class="container">
  <div id="glass" class="glass"></div>
  <div class="c1">
    <div class="grandchild"></div>
  </div>
  <div id="c2" class="c2"></div>
</div>

Solution:

So I guess your strategy should be to use an absolute grandchild that fills the whole side-panel, and then put the content inside like:

<div class="grandchild">
  <div class="content"></div>
</div>

and change these styles:

.grandchild {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}
.grandchild .content{
  background-color: red;
  width: 50px;
  height: 50px;
}

Example below:

function startDrag() {
  glass.style = 'display: block;';
  glass.addEventListener('mousemove', drag, false);
}

function endDrag() {
  glass.removeEventListener('mousemove', drag, false);
  glass.style = '';
}

function drag(event) {
  var splitter = getSplitter();
  var panel = document.getElementById('c2');
  var currentWidth = panel.offsetWidth;
  var currentLeft = panel.offsetLeft;
  panel.style.width = (currentWidth - (event.clientX - currentLeft)) + "px";
}

function getSplitter() {
  return document.getElementById('splitter');
}

var con = document.getElementById('container');
var splitter = document.createElement('div');
var glass = document.getElementById('glass');
splitter.className = 'splitter';
splitter.id = 'splitter';
con.insertBefore(splitter, con.lastElementChild);
splitter.addEventListener('mousedown', startDrag, false);
glass.addEventListener('mouseup', endDrag, false);
.container {
  display: flex;
  border: 1px solid;
  width: 500px;
  height: 300px;
  position: absolute;
}

.c1 {
  background-color: blue;
  flex: 1;
  height: 100%;
  position: relative;
  overflow: hidden;
}

.c2 {
  background-color: green;
  width: 150px;
}

.splitter {
  width: 20px;
  cursor: col-resize;
}

.glass {
  height: 100%;
  width: 100%;
  cursor: col-resize;
  display: none;
  position: absolute;
}
.grandchild {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}
.grandchild .content{
  background-color: red;
  width: 50px;
  height: 50px;
}
<div id="container" class="container">
  <div id="glass" class="glass"></div>
  <div class="c1">
    <div class="grandchild">
      <div class="content"></div>
    </div>
  </div>
  <div id="c2" class="c2"></div>
</div>

Comments