Issue
As you can see, i have three panels (left, center, right) that scale/resize correctly horizontally. Now i would like to insert a new panel inside the central panel.
The new panel will have to resize vertically (rising above and below). I would like to achieve this in pure Javascript (without jQuery or other libraries) as per my Javascript code. I'm new to Javascript and i'm having trouble.
I would like to get this:
P.S: Naturally the 3 vertical panels (left, center, right) must continue to move left and right horizontally, as they already move correctly with my code. I don't want the new addition of the panel to break the already functioning movement of the other panels.
This is my code:
const gutters = document.querySelectorAll(".gutter");
const gutter_orizontal = document.querySelectorAll(".gutter_new");
const panes = document.querySelectorAll(".pane");
const minWidth = 200;
function resizer(e) {
window.addEventListener("mousemove", mousemove);
window.addEventListener("mouseup", mouseup);
let prevX = e.x;
const currentPane = e.currentTarget.parentNode;
const currentPanel = currentPane.getBoundingClientRect();
const prevPane = currentPane.previousElementSibling;
const prevPanel = prevPane.getBoundingClientRect();
function mousemove(e) {
let newX = prevX - e.x;
let newCurrentWidth = currentPanel.width + newX;
let newPrevWidth = prevPanel.width - newX;
if (newCurrentWidth < minWidth || newPrevWidth < minWidth) return;
currentPane.style.width = newCurrentWidth + "px";
prevPane.style.width = newPrevWidth + "px";
}
function mouseup() {
window.removeEventListener("mousemove", mousemove);
window.removeEventListener("mouseup", mouseup);
}
}
gutters.forEach((gutter) => gutter.addEventListener("mousedown", resizer));
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
}
.wrapper {
height: 100vh;
width: 100vw;
background: #333;
border: 6px solid #666;
display: flex;
}
.pane {
padding: 1em;
color: #fff;
min-width: 200px;
}
.center {
position: relative;
}
.right {
position: relative;
flex-grow: 1;
}
.new {
position: relative;
}
.gutter {
width: 10px;
height: 100%;
background: #666;
position: absolute;
top: 0;
left: 0;
cursor: col-resize;
}
.gutter_new {
height: 10px;
width: 100%;
background: #666;
position: absolute;
top: 0;
left: 0;
cursor: row-resize;
}
<div class="wrapper">
<div class="pane left">
This is the left pane.
</div>
<div class="pane center">
This is the center pane.
<div class="gutter"></div>
<div class="pane new">
This is the new pane.
<div class="gutter_new"></div>
</div>
</div>
<div class="pane right">
This is the right pane.
<div class="gutter"></div>
</div>
</div>
Solution
Add two classes
.gutter-v
&.gutter-h
,resizer()
will change.pane
's width or height depend on.gutter
's class.Use
has()
selector to change.pane
which become parent of other.pane
to flexbox.has()
already supported by latest firefox.minHeight and minWidth is now in
.wrapper
inline style, so you can get them by js.
const gutters = document.querySelectorAll(".gutter");
const panes = document.querySelectorAll(".pane");
const minWidth = document.querySelector(".wrapper").style.getPropertyValue("--min-width");
const minHeight = document.querySelector(".wrapper").style.getPropertyValue("--min-height");
function resizer(e) {
window.addEventListener("mousemove", mousemove);
window.addEventListener("mouseup", mouseup);
// check gutter if vertical or horizontal
const is_vertical = e.currentTarget.classList.contains("gutter-v");
// get previous position (x or y depending on is_vertical)
const prev = is_vertical ? e.x : e.y;
// get current pane, the parent pane of the gutter you are moving
const currentPane = e.currentTarget.parentNode;
const currentPanel = currentPane.getBoundingClientRect();
// get previous pane, when move gutter-v:
// if current pane is center, prev pane will be left pane
// if current pane is right, prev pane will be center pane
// left pane will never be current pane cause it don't have gutter
const prevPane = currentPane.previousElementSibling;
const prevPanel = prevPane.getBoundingClientRect();
function mousemove(e) {
// minWidth and minHeight are string ('200px' and '100px' in this case), change them to integer
const min = parseInt(is_vertical ? minWidth : minHeight);
// calculate distance between prev and current position
const distance = prev - (is_vertical ? e.x : e.y);
// calculate new width/height of current pane and prev pane
const newCurrentSize = (is_vertical ? currentPanel.width : currentPanel.height) + distance;
const newPrevSize = (is_vertical ? prevPanel.width : prevPanel.height) - distance;
// if new width/height is less than min, return and don't change pane style
if (newCurrentSize < min || newPrevSize < min) return;
// change pane width/height depending on is_vertical
if (is_vertical) {
currentPane.style.width = newCurrentSize + "px";
prevPane.style.width = newPrevSize + "px";
} else {
currentPane.style.height = newCurrentSize + "px";
prevPane.style.height = newPrevSize + "px";
}
}
function mouseup() {
window.removeEventListener("mousemove", mousemove);
window.removeEventListener("mouseup", mouseup);
}
}
gutters.forEach((gutter) => gutter.addEventListener("mousedown", resizer));
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
}
.wrapper {
height: 100vh;
width: 100vw;
background: #333;
border: 6px solid #666;
display: flex;
}
.pane {
position: relative;
padding: 1em;
color: #fff;
/*
--min-width in .wrapper inline style
<div class="wrapper" style="--min-width: 200px;--min-height: 100px;">
*/
min-width: var(--min-width);
}
.pane:last-child {
flex-grow: 1;
}
.pane:has(.pane) {
padding: 0;
display: flex;
flex-direction: column;
}
.pane>.pane {
/*
--min-height in .wrapper inline style
<div class="wrapper" style="--min-width: 200px;--min-height: 100px;">
*/
min-height: var(--min-height);
}
.gutter {
--l: 100%;
--s: 10px;
background: #666;
position: absolute;
z-index: 1;
top: 0;
left: 0;
}
.gutter-v {
width: var(--s);
height: var(--l);
cursor: col-resize;
}
.gutter-h {
height: var(--s);
width: var(--l);
cursor: row-resize;
}
/*
the reason I delete .right is because I use .pane:last-child to select the last pane (the bottom one or the right one)
you can keep .left and .center to set default width and height
but it's better to use some classes like .w-250px or .h-150px, it's easier to read and reuse
*/
.w-250px {
width: 250px;
}
.h-150px {
height: 150px;
}
<!-- use css variables so you only need to edit one place if you need to change min width or min height -->
<div class="wrapper" style="--min-width: 200px;--min-height: 100px;">
<div class="pane w-250px">
This is the left pane.
</div>
<div class="pane">
<div class="gutter gutter-v"></div>
<div class="pane h-150px">
This is the center pane1.
</div>
<div class="pane">
<div class="gutter gutter-h"></div>
This is the center pane2.
</div>
</div>
<div class="pane">
<div class="gutter gutter-v"></div>
This is the right pane.
</div>
</div>
Answered By - Shuo
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.