I made a basic javascript code so you can poke divs with you mouse.
Unfortunately I have to add them manualy but i wanna add so much of them with a pattern.
First i decided to use grid but i guessed it wont work because divs (which i call them squares from now on :D) can change their position.
So I was about to ask, how can I create a javascript code that i can spawn them until they fill the screen.
Also i have another question which is realted to this project, How can i make these squares just decors. I mean by decors i dont want them to effect the webside at all, when the blocks goes out of the screen the body starts to expend, is there any way to avoid that?
(Also it will be better if you make the snippet full screen!)
EDIT: I put the refresh-button on the top left on the main div so you can draw squares by clicking it!
let mouse = {
speedX: 0,
speedY: 0,
posX: 0,
posY: 0,
movement: 0,
speed: 0
}
//on mousemove update the moouse object
document.onmousemove = function(e) {
mouse.speedX = e.movementX;
mouse.speedY = e.movementY
mouse.posX = e.pageX;
mouse.posY = e.pageY;
}
//refresh the mouse movement and speed every 100ms
setInterval(() => {
mouse.movement =
Math.sqrt(Math.pow(mouse.speedX, 2) + Math.pow(mouse.speedY, 2));
mouse.speed = mouse.movement * 10;
}, 100);
//add a square div in parent element
function addSquare(parent) {
const newDiv = document.createElement("div");
newDiv.classList.add("square")
parent.appendChild(newDiv)
return newDiv;
}
//add squares in the parent element filling the available size
//gap is the space between squares, size is the edge of the square
//if skipbefore is false it will begin to draw the next square also if it won't fit entirely
function addSquares(parent, gap, size, skipbefore = true) {
const squares = [];
let rect = parent.getBoundingClientRect();
const availableWidth = rect.width;
const availableHeight = rect.height;
let top = 100;
while (top < availableHeight) {
let left = 0;
if (skipbefore && top + size > availableHeight)
break;
while (left < availableWidth) {
if (skipbefore && left + size > availableWidth)
break;
const square = addSquare(parent);
square.style.left = `${left}px`;
square.style.top = `${top}px`;
squares.push(square);
left += gap + size;
}
top += gap + size;
}
return squares;
}
//onmoveover event handler
const squareOnMouseOver = (event) => {
const element = event.target;
const y = mouse.speedY;
const x = mouse.speedX;
const rad = Math.atan2(y, x);
yAxis = mouse.movement * Math.sin(rad);
xAxis = mouse.movement * Math.cos(rad);
const rect = element.getBoundingClientRect();
const left = Math.round(rect.x + xAxis * 3);
const top = Math.round(rect.y + yAxis * 3);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
const o = rad * (180 / Math.PI);
element.style.transform = `rotate(${o}deg)`;
}
//resets the .target parent and redraw the squares inside it
function drawSquares() {
const parent = document.querySelector('.target');
parent.innerHTML = '';
const squares = addSquares(parent, 25, 75);
const colors = [
'lightcoral',
'bisque',
'aquamarine',
'cadetblue',
'greenyellow',
'yellowgreen'
];
squares.forEach(square => {
const iColor = Math.floor(Math.random() * (colors.length - 1));
const color = colors[iColor];
square.style.background = color;
square.addEventListener('mouseover', squareOnMouseOver);
});
}
body{
margin: 0;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: rgb(242, 239, 231);
color: rgb(10, 10, 9);
}
.square{
background-color: lightcoral;
width: 75px;
height: 75px;
position: absolute;
transform: rotate(0deg);
transition: all ease-out 0.5s;
}
.background {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.container .row .col > * {
display: inline-block;
}
.target {
display: block;
width: 100%;
height: 100%;
}
#draw {
font-size: 20px;
padding: .2em 1em;
cursor: pointer;
}
.name{
font-size: 40px;
}
#main-container{
position: absolute;
padding: 35px 45px;
width: 950px;
height: 285px;
box-shadow: 0px 2px 5px 2px rgb(191, 188, 182);
}
.links{
display: flex;
justify-content: center;
align-items: center;
}
.icons{
width: 55px;
height: auto;
margin: 0px 25px;
padding: 10px;
border-radius: 5px;
transition: all ease-in 0.2s;
}
.icons:hover{
background-color: rgb(144, 144, 144);
}
.refresh{
position: absolute;
}
.refresh-button{
width: 25px;
height: auto;
}
.btn:hover{
background-color: rgb(144, 144, 144);
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<link rel="stylesheet" href="css.css">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="background">
<div class="target"></div>
<div class="container text-center" id="main-container">
<div class="">
<div class="refresh">
<button class="btn" id="draw" onclick="drawSquares()"><img class="refresh-button" src="SVG/arrow-clockwise.svg"></button>
</div>
<div class="name">Berk Efe Keskin</div>
<br>
<i>This website is working in progress right now...</i>
<br>
<i>Here is some useful links.</i>
<br>
<br>
<div class="links">
<img class="icons" src="SVG/github.svg">
<img class="icons" src="SVG/linkedin.svg">
<img class="icons" src="SVG/stack-overflow.svg">
</div>
</div>
</div>
</div>
<script type="text/javascript" src = "javascript.js"></script>
</body>
</html>
You may have a function that given a container will be filled with how many squares can fit inside as long as there is still avaiable width and available height in the target.
Here in this demo I better factored your code and added a main function called drawSquares that gets called when the button reDRAW is clicked. Each time the squares are redrawn, the target content is emptied.
I'm using a button to trigger the box drawing because the available space depends on the size of the area when the action is fired. For example you can expand the snippet and decide to redraw the squares to have the whole new area filled again.
You may decide to call the action on document ready or when the window gets resized.
let mouse = {
speedX: 0,
speedY: 0,
posX: 0,
posY: 0,
movement: 0,
speed: 0
}
//on mousemove update the moouse object
document.onmousemove = function(e) {
mouse.speedX = e.movementX;
mouse.speedY = e.movementY
mouse.posX = e.pageX;
mouse.posY = e.pageY;
}
//refresh the mouse movement and speed every 100ms
setInterval(() => {
mouse.movement =
Math.sqrt(Math.pow(mouse.speedX, 2) + Math.pow(mouse.speedY, 2));
mouse.speed = mouse.movement * 10;
}, 100);
//add a square div in parent element
function addSquare(parent) {
const newDiv = document.createElement("div");
newDiv.classList.add("square")
parent.appendChild(newDiv)
return newDiv;
}
//add squares in the parent element filling the available size
//gap is the space between squares, size is the edge of the square
//if skipbefore is false it will begin to draw the next square also if it won't fit entirely
function addSquares(parent, gap, size, skipbefore = true) {
const squares = [];
let rect = parent.getBoundingClientRect();
const availableWidth = rect.width;
const availableHeight = rect.height;
let top = 100;
while (top < availableHeight) {
let left = 0;
if (skipbefore && top + size > availableHeight)
break;
while (left < availableWidth) {
if (skipbefore && left + size > availableWidth)
break;
const square = addSquare(parent);
square.style.left = `${left}px`;
square.style.top = `${top}px`;
squares.push(square);
left += gap + size;
}
top += gap + size;
}
return squares;
}
//onmoveover event handler
const squareOnMouseOver = (event) => {
const element = event.target;
const y = mouse.speedY;
const x = mouse.speedX;
const rad = Math.atan2(y, x);
yAxis = mouse.movement * Math.sin(rad);
xAxis = mouse.movement * Math.cos(rad);
const rect = element.getBoundingClientRect();
const left = Math.round(rect.x + xAxis * 3);
const top = Math.round(rect.y + yAxis * 3);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
const o = rad * (180 / Math.PI);
element.style.transform = `rotate(${o}deg)`;
}
//resets the .target parent and redraw the squares inside it
function drawSquares() {
const parent = document.querySelector('.target');
parent.innerHTML = '';
const squares = addSquares(parent, 25, 75);
const colors = [
'lightcoral',
'bisque',
'aquamarine',
'cadetblue',
'greenyellow',
'yellowgreen'
];
squares.forEach(square => {
const iColor = Math.floor(Math.random() * (colors.length - 1));
const color = colors[iColor];
square.style.background = color;
square.addEventListener('mouseover', squareOnMouseOver);
});
}
body {
margin: 0;
width: 100vw;
height: 100vh;
display: flex;
}
.square {
background-color: lightcoral;
width: 75px;
height: 75px;
position: absolute;
transform: rotate(0deg);
transition: all ease-out 0.5s;
}
#Header {
font-size: italic;
}
.background {
width: 100%;
height: 100%;
}
.target {
position: relative;
display: block;
width: 100%;
height: 100%;
z-index: -1;
}
#draw {
font-size: 20px;
padding: .2em 1em;
cursor: pointer;
}
.container .row .col > * {
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<link el="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<body>
<div class="background">
<span>Work in progress...</span>
<div class="container text-center">
<div class="row">
<div class="col">
<h1 id="Header">Work in progress...</h1>
<button id="draw" onclick="drawSquares()">reDRAW</button>
</div>
</div>
</div>
<div class="target"></div>
</div>
</body>
I had a hard time coding, so I asked a question here.
If you press the button on the upper left, you can make the color boxes invisible. At the same time, I wrote down the code the the button can make the color boxes could be placed in a random location.
I would like to make sure that the color boxes do not go out of the screen. Even if the width and height were set to 100% on the body, the color box was placed off the screen.
And I want to correct the color boxes so that they can be moved by draggable function, but they don't work together. I would also like to ask for help with this.
and I am not a coding expert man so, I need a comment with coding example.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style type="text/css">
body {margin:0; padding:0;}
button {position: absolute; width: 30px; height: 30px; background: #edd6bc;}
.random {position: absolute; width: 30px; height: 30px;}
</style>
<script>
</script>
</head>
<body >
<button onclick="showhide()" value="Zeige Features" id="button"></button>
<div style="display: none; background: #6d97b4;" class="random" ></div>
<div style="display: none; background: #c9656f;" class="random" ></div>
<div style="display: none; background: #f1eb40;" class="random" ></div>
<div style="display: none; background: #00825c;" class="random" ></div>
<div style="display: none; background: #009ce0;" class="random" ></div>
<div style="display: none; background: #cee4a6;" class="random" ></div>
<script type="text/javascript">
const btn = document.querySelector("button");
const height = document.documentElement.clientHeight;
const width = document.documentElement.clientWidth;
const boxes = document.querySelectorAll(".random");
btn.addEventListener("click", () => {
Array.from(boxes).forEach(box => {
box.style.top = Math.floor((Math.random() * height) + 1) + "px";
box.style.right = Math.floor((Math.random() * width) + 1) + "px";
})
});
function showhide() {
var x = document.querySelectorAll(".random");
var i;
for (i = 0; i < x.length; i++) {
if (x[i].style.display === "block") {
x[i].style.display = "none";
} else {
x[i].style.display =
"block";
}
}
}
</script>
</body>
</html>
If you want an explanation tell me.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style type="text/css">
body {margin:0; padding:0;}
button {position: absolute; width: 30px; height: 30px; background: #edd6bc;}
.random {position: absolute; width: 30px; height: 30px;}
</style>
<script>
</script>
</head>
<body >
<button onclick="showhide()" value="Zeige Features" id="button"></button>
<div style="display: none; background: #6d97b4;" class="random"></div>
<div style="display: none; background: #c9656f;" class="random"></div>
<div style="display: none; background: #f1eb40;" class="random"></div>
<div style="display: none; background: #00825c;" class="random"></div>
<div style="display: none; background: #009ce0;" class="random"></div>
<div style="display: none; background: #cee4a6;" class="random"></div>
<script type="text/javascript">
const btn = document.querySelector("button");
const height = document.documentElement.clientHeight - 30;
const width = document.documentElement.clientWidth - 30;
const boxes = document.querySelectorAll(".random");
btn.addEventListener("click", () => {
Array.from(boxes).forEach(box => {
box.style.top = Math.floor(Math.random() * height) + "px";
box.style.left = Math.floor(Math.random() * width) + "px";
})
});
function showhide() {
var x = document.querySelectorAll(".random");
var i;
for (i = 0; i < x.length; i++) {
if (x[i].style.display === "block") {
x[i].style.display = "none";
} else {
x[i].style.display =
"block";
}
}
}
function mouseDown(downEvent) {
var box = downEvent.srcElement;
var offX = box.getBoundingClientRect().left - downEvent.x;
var offY = box.getBoundingClientRect().top - downEvent.y;
document.onmousemove = e => {
box.style.top = Math.min(Math.max(e.y + offY, 0), height) + "px";
box.style.left = Math.min(Math.max(e.x + offX, 0), width) + "px";
}
document.onmouseup = e => {
document.onmousemove = document.onmouseup = null;
}
return false;
}
Array.from(boxes).forEach(box => {
box.onmousedown = mouseDown;
})
</script>
</body>
</html>
While generating the top * right subtract the height and width of the div
const btn = document.querySelector("button");
const height = document.documentElement.clientHeight;
const width = document.documentElement.clientWidth;
const boxes = document.querySelectorAll(".random");
btn.addEventListener("click", () => {
Array.from(boxes).forEach(box => {
const top = Math.floor(Math.random() * (height - 30) + 1) + "px";
const right = Math.floor(Math.random() * (width - 30) + 1) + "px";
box.style.top = top;
box.style.right = right;
})
});
function showhide() {
var x = document.querySelectorAll(".random");
var i;
for (i = 0; i < x.length; i++) {
if (x[i].style.display === "block") {
x[i].style.display = "none";
} else {
x[i].style.display =
"block";
}
}
}
body {
margin: 0;
padding: 0;
}
button {
position: relative;
width: 30px;
height: 30px;
background: #edd6bc;
}
.random {
position: absolute;
width: 30px;
height: 30px;
}
<button onclick="showhide()" value="Zeige Features" id="button"></button>
<div style="display: none; background: #6d97b4;" class="random"></div>
<div style="display: none; background: #c9656f;" class="random"></div>
<div style="display: none; background: #f1eb40;" class="random"></div>
<div style="display: none; background: #00825c;" class="random"></div>
<div style="display: none; background: #009ce0;" class="random"></div>
<div style="display: none; background: #cee4a6;" class="random"></div>
I am writing my own Drag'n'drop functionality for one of my projects and I am running into an issue. All my "draggable" elements are inside a container with display:flex. On mousedown event on one of this elements I set the position to absolute so I would be able to set the left and top properties of the element when I am dragging. Here is what I am doing:
let container = document.querySelector("#big-container")
var dragging = false;
var draggedObject;
let shiftX=0;
let shiftY=0;
document.querySelectorAll(".draggable").forEach((draggable,index) => {
draggable.style.order = index;
draggable.draggable =false;
draggable.ondragstart = ()=>{return false}
draggable.addEventListener("mousedown",ev =>{
draggedObject = draggable;
shiftX = ev.offsetX+5;
shiftY = ev.offsetY+5;
draggable.style.position = "absolute";
draggable.style.left = (ev.clientX - shiftX) + 'px';
draggable.style.top = (ev.clientY - shiftY) + 'px';
dragging = true;
let placeholder = document.createElement("div");
placeholder.id = "placeholder";
placeholder.style.order = draggable.style.order;
container.appendChild(placeholder);
})
})
document.addEventListener("mousemove", ev =>{
if(dragging){
draggedObject.style.left = ev.clientX - shiftX + 'px';
draggedObject.style.top = ev.clientY - shiftY + 'px';
}
})
document.addEventListener("mouseup",ev =>{
if(dragging){
draggedObject.style.position = 'static'
let placeholder = document.querySelector("#placeholder");
container.removeChild(placeholder);
dragging = false
}
})
/* the :not(:last-of-type(div)) is there so the console doesn't get affected */
*{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: black;
}
.draggable {
width: 90px;
height: 120px;
margin: 5px;
}
#placeholder {
width: 90px;
height: 120px;
margin: 5px;
background-color: rgba(0, 0, 0, 0.3);
border: dashed grey 5px;
}
<body draggable="false" ondragstart="return false;">
<div id = "big-container" style ="display: flex; background-color: rgb(76, 104, 95); width: 500px; height: 500px;">
<div style="background-color: rgb(204, 125, 111);" class="draggable"></div>
<div style="background-color: rgb(170, 214, 120);" class="draggable"></div>
<div style="background-color: rgb(129, 212, 167);" class="draggable"></div>
<div style="background-color: rgb(162, 137, 196);" class="draggable"></div>
</div>
</body>
What I am trying to achieve is that on mousedown the element should stay where it was and after that when I move my mouse to move the element as well.(the anchor point should be where I clicked the element)
I am doing shiftX = ev.offsetX+5; because I need to account for the element's margin.
The issue is when I click on a element(and don't move my mouse at all), you can see a little shift in the element's position. It is very minor(maybe 1 or 2px) and is not happening in all places(some zones in the element do not introduce this position shift)
Do you guys have any idea what might be causing it?
You can use getBoundingClientRect() to get the actual position.
let container = document.querySelector("#big-container");
var dragging = false;
var draggedObject;
let shiftX = 0;
let shiftY = 0;
document.querySelectorAll(".draggable").forEach((draggable, index) => {
draggable.style.order = index;
draggable.draggable = false;
draggable.ondragstart = () => {
return false;
};
draggable.addEventListener("mousedown", (ev) => {
draggedObject = draggable;
var x = draggable.getBoundingClientRect().top - 5;
var y = draggable.getBoundingClientRect().left - 5;
shiftX = ev.offsetX + 5;
shiftY = ev.offsetY + 5;
draggable.style.position = "absolute";
draggable.style.left = y + "px";
draggable.style.top = x + "px";
dragging = true;
let placeholder = document.createElement("div");
placeholder.id = "placeholder";
placeholder.style.order = draggable.style.order;
container.appendChild(placeholder);
});
});
document.addEventListener("mousemove", (ev) => {
if (dragging) {
draggedObject.style.left = ev.clientX - shiftX + "px";
draggedObject.style.top = ev.clientY - shiftY + "px";
}
});
document.addEventListener("mouseup", (ev) => {
if (dragging) {
draggedObject.style.position = "static";
let placeholder = document.querySelector("#placeholder");
container.removeChild(placeholder);
dragging = false;
}
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: black;
}
#big-container {
width: 500px;
height: 500px;
}
.draggable {
width: 90px;
height: 120px;
margin: 5px;
}
#placeholder {
width: 90px;
height: 120px;
margin: 5px;
background-color: rgba(0, 0, 0, 0.3);
border: dashed grey 5px;
}
<body draggable="false" ondragstart="return false;">
<div
id="big-container"
style="display: flex; background-color: rgb(76, 104, 95);"
>
<div
style="background-color: rgb(204, 125, 111);"
class="draggable"
></div>
<div
style="background-color: rgb(170, 214, 120);"
class="draggable"
></div>
<div
style="background-color: rgb(129, 212, 167);"
class="draggable"
></div>
<div
style="background-color: rgb(162, 137, 196);"
class="draggable"
></div>
</div>
</body>
I'm using Responsive File Manager for a web file manager in my project.
It is working fine.
Problem
My problem is when I open the image, It shows the preview in lightbox modal.
How can I make it Image as an image gallery like navigating between Image?
<a class="tip-right preview" title="<?php echo trans('Preview')?>" data-url="<?php echo $src;?>" data-toggle="lightbox" href="#previewLightbox">
<i class=" icon-eye-open"></i>
</a>
JS
Here a piece code JS code which is in include.js file
r.on("click", ".preview", function() {
var e = jQuery(this);
return 0 == e.hasClass("disabled") && jQuery("#full-img").attr("src", decodeURIComponent(e.attr("data-url")))
HTML
<div id="previewLightbox" class="lightbox hide fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class='lightbox-content'>
<img id="full-img" src="">
</div>
</div>
Fiddle Here
From my experience there isn't much you can do in terms of editing their manager.
However, it is quite easy to make your own with some CSS and Javascript.
Fiddle
Learning Tutorial
HTML:
<div id="slider" class="slider">
<div class="wrapper">
<div id="items" class="items">
<span class="slide"><img src="image1"/></span>
<span class="slide"><img src="image2"/></span>
<span class="slide"><img src="image3"/></span>
</div>
</div>
<a id="prev" class="control prev"></a>
<a id="next" class="control next"></a>
</div>
CSS:
.slider {
width: 300px;
height: 200px;
}
.wrapper {
overflow: hidden;
position: relative;
background: #222;
z-index: 1;
}
#items {
width: 10000px;
position: relative;
top: 0;
left: -300px;
}
#items.shifting {
transition: left .2s ease-out;
}
.slide {
width: 300px;
height: 200px;
cursor: pointer;
float: left;
display: flex;
flex-direction: column;
justify-content: center;
transition: all 1s;
position: relative;
}
.control {
position: absolute;
top: 50%;
width: 40px;
height: 40px;
background: #fff;
border-radius: 20px;
margin-top: -20px;
box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.3);
z-index: 2;
}
.prev,
.next {
background-size: 22px;
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
}
.prev {
background-image: url(ChevronLeft.png);
left: -20px;
}
.next {
background-image: url(ChevronRight-512.png);
right: -20px;
}
.prev:active,
.next:active {
transform: scale(0.8);
}
Javascript:
var slider = document.getElementById('slider'),
sliderItems = document.getElementById('items'),
prev = document.getElementById('prev'),
next = document.getElementById('next');
slide(slider, sliderItems, prev, next);
function slide(wrapper, items, prev, next) {
var posX1 = 0,
posX2 = 0,
posInitial,
posFinal,
threshold = 100,
slides = items.getElementsByClassName('slide'),
slidesLength = slides.length,
slideSize = items.getElementsByClassName('slide')[0].offsetWidth,
firstSlide = slides[0],
lastSlide = slides[slidesLength - 1],
cloneFirst = firstSlide.cloneNode(true),
cloneLast = lastSlide.cloneNode(true),
index = 0,
allowShift = true;
// Clone first and last slide
items.appendChild(cloneFirst);
items.insertBefore(cloneLast, firstSlide);
wrapper.classList.add('loaded');
// Mouse and Touch events
items.onmousedown = dragStart;
// Touch events
items.addEventListener('touchstart', dragStart);
items.addEventListener('touchend', dragEnd);
items.addEventListener('touchmove', dragAction);
// Click events
prev.addEventListener('click', function () { shiftSlide(-1) });
next.addEventListener('click', function () { shiftSlide(1) });
// Transition events
items.addEventListener('transitionend', checkIndex);
function dragStart (e) {
e = e || window.event;
e.preventDefault();
posInitial = items.offsetLeft;
if (e.type == 'touchstart') {
posX1 = e.touches[0].clientX;
} else {
posX1 = e.clientX;
document.onmouseup = dragEnd;
document.onmousemove = dragAction;
}
}
function dragAction (e) {
e = e || window.event;
if (e.type == 'touchmove') {
posX2 = posX1 - e.touches[0].clientX;
posX1 = e.touches[0].clientX;
} else {
posX2 = posX1 - e.clientX;
posX1 = e.clientX;
}
items.style.left = (items.offsetLeft - posX2) + "px";
}
function dragEnd (e) {
posFinal = items.offsetLeft;
if (posFinal - posInitial < -threshold) {
shiftSlide(1, 'drag');
} else if (posFinal - posInitial > threshold) {
shiftSlide(-1, 'drag');
} else {
items.style.left = (posInitial) + "px";
}
document.onmouseup = null;
document.onmousemove = null;
}
function shiftSlide(dir, action) {
items.classList.add('shifting');
if (allowShift) {
if (!action) { posInitial = items.offsetLeft; }
if (dir == 1) {
items.style.left = (posInitial - slideSize) + "px";
index++;
} else if (dir == -1) {
items.style.left = (posInitial + slideSize) + "px";
index--;
}
};
allowShift = false;
}
function checkIndex (){
items.classList.remove('shifting');
if (index == -1) {
items.style.left = -(slidesLength * slideSize) + "px";
index = slidesLength - 1;
}
if (index == slidesLength) {
items.style.left = -(1 * slideSize) + "px";
index = 0;
}
allowShift = true;
}
}
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
Is there a good technique to make a resizable split pane in HTML?
May it be done using CSS / jQuery / JavaScript or is there a good JavaScript library that have been used?
(An example of a split pane is the favorites bar in Internet Explorer which you may have docked to the left of your main browser window.)
I wanted a vanilla, lightweight (jQuery UI Layout weighs in at 185 KB), no dependency option (all existing libraries require jQuery), so I wrote Split.js.
It weights less than 2 KB and does not require any special markup. It supports older browsers back to Internet Explorer 9 (or Internet Explorer 8 with polyfills). For modern browsers, you can use it with Flexbox and grid layouts.
Simplest HTML + CSS accordion, with just CSS resize.
div {
resize: vertical;
overflow: auto;
border: 1px solid
}
.menu {
display: grid
/* Try height: 100% or height: 100vh */
}
<div class="menu">
<div>
Hello, World!
</div>
<div>
Hello, World!
</div>
<div>
Hello, World!
</div>
</div>
Simplest HTML + CSS vertical resizable panes:
div {
resize: horizontal;
overflow: auto;
border: 1px solid;
display: inline-flex;
height: 90vh
}
<div>
Hello, World!
</div>
<div>
Hello, World!
</div>
The plain HTML, details element!.
<details>
<summary>Morning</summary>
<p>Hello, World!</p>
</details>
<details>
<summary>Evening</summary>
<p>How sweat?</p>
</details>
Simplest HTML + CSS topbar foldable menu
div{
display: flex
}
summary,p{
margin: 0px 0 -1px 0px;
padding: 0 0 0 0.5rem;
border: 1px black solid
}
summary {
padding: 0 1rem 0 0.5rem
}
<div>
<details>
<summary>FILE</summary>
<p>Save</p>
<p>Save as</p>
</details>
<details>
<summary>EDIT</summary>
<p>Pump</p>
<p>Transfer</p>
<p>Review</p>
<p>Compile</p>
</details>
<details>
<summary>PREFERENCES</summary>
<p>How sweat?</p>
<p>Powered by HTML</p>
</details>
</div>
Fixed bottom menu bar, unfolding upward.
div{
display: flex;
position: fixed;
bottom: 0;
transform: rotate(180deg)
}
summary,p{
margin: 0px 0 -1px 0px;
padding: 0 0 0 0.5rem;
border: 1px black solid;
transform: rotate(180deg)
}
summary {
padding: 0 1rem 0 0.5rem;
}
<div>
<details>
<summary>FILE</summary>
<p>Save</p>
<p>Save as</p>
</details>
<details>
<summary>EDIT</summary>
<p>Pump</p>
<p>Transfer</p>
<p>Review</p>
<p>Compile</p>
</details>
<details>
<summary>PREF</summary>
<p>How?</p>
<p>Power</p>
</details>
</div>
Simplest HTML full-screen modal popup
.popup > p {
padding: 1rem;
margin: 0;
display: flex;
flex-direction: column;
width: 25vw
}
.popup summary {
padding: 1rem 0.5rem;
cursor: pointer;
max-height: 90vh;
overflow: auto
}
.popup[open] summary {
background: black;
color: white;
padding: 0.5rem;
}
.popup[open] {
position: fixed;
/* top: calc(50% - 25vw); */
left: calc(50% - 15vw);
outline: 5000px #00000090 solid;
border: 5px red solid;
border-radius: 0.5rem;
z-index: 1;
max-height: 90vh;
overflow-y: auto;
overflow-x: hidden
}
.popup[open] summary::after {
content: '❌';
float: right;
}
<details class="popup">
<summary>HTML popup</summary>
<p>
<span>Name</span>
<input value="HTML" />
<br>
<span>Difficulty</span>
<input type="number" value="3" />
<br>
<span>Coolness</span>
<input type="number" value="100" />
<br>
<p><span>Powered by HTML</span></p>
</p>
</details>
Simplest resizable pane, using JavaScript.
let ismdwn = 0
rpanrResize.addEventListener('mousedown', mD)
function mD(event) {
ismdwn = 1
document.body.addEventListener('mousemove', mV)
document.body.addEventListener('mouseup', end)
}
function mV(event) {
if (ismdwn === 1) {
pan1.style.flexBasis = event.clientX + "px"
} else {
end()
}
}
const end = (e) => {
ismdwn = 0
document.body.removeEventListener('mouseup', end)
rpanrResize.removeEventListener('mousemove', mV)
}
div {
display: flex;
border: 1px black solid;
width: 100%;
height: 200px;
}
#pan1 {
flex-grow: 1;
flex-shrink: 0;
flex-basis: 50%; /* initial status */
}
#pan2 {
flex-grow: 0;
flex-shrink: 1;
overflow-x: auto;
}
#rpanrResize {
flex-grow: 0;
flex-shrink: 0;
background: #1b1b51;
width: 0.2rem;
cursor: col-resize;
margin: 0 0 0 auto;
}
<div>
<div id="pan1">MENU</div>
<div id="rpanrResize"> </div>
<div id="pan2">BODY</div>
</div>
Improving on Reza's answer:
prevent the browser from interfering with a drag
prevent setting an element to a negative size
prevent drag getting out of sync with the mouse due to incremental delta interaction with element width saturation
<html><head><style>
.splitter {
width: 100%;
height: 100px;
display: flex;
}
#separator {
cursor: col-resize;
background-color: #aaa;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='30'><path d='M2 0 v30 M5 0 v30 M8 0 v30' fill='none' stroke='black'/></svg>");
background-repeat: no-repeat;
background-position: center;
width: 10px;
height: 100%;
/* Prevent the browser's built-in drag from interfering */
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#first {
background-color: #dde;
width: 20%;
height: 100%;
min-width: 10px;
}
#second {
background-color: #eee;
width: 80%;
height: 100%;
min-width: 10px;
}
</style></head><body>
<div class="splitter">
<div id="first"></div>
<div id="separator" ></div>
<div id="second" ></div>
</div>
<script>
// A function is used for dragging and moving
function dragElement(element, direction)
{
var md; // remember mouse down info
const first = document.getElementById("first");
const second = document.getElementById("second");
element.onmousedown = onMouseDown;
function onMouseDown(e)
{
//console.log("mouse down: " + e.clientX);
md = {e,
offsetLeft: element.offsetLeft,
offsetTop: element.offsetTop,
firstWidth: first.offsetWidth,
secondWidth: second.offsetWidth
};
document.onmousemove = onMouseMove;
document.onmouseup = () => {
//console.log("mouse up");
document.onmousemove = document.onmouseup = null;
}
}
function onMouseMove(e)
{
//console.log("mouse move: " + e.clientX);
var delta = {x: e.clientX - md.e.clientX,
y: e.clientY - md.e.clientY};
if (direction === "H" ) // Horizontal
{
// Prevent negative-sized elements
delta.x = Math.min(Math.max(delta.x, -md.firstWidth),
md.secondWidth);
element.style.left = md.offsetLeft + delta.x + "px";
first.style.width = (md.firstWidth + delta.x) + "px";
second.style.width = (md.secondWidth - delta.x) + "px";
}
}
}
dragElement( document.getElementById("separator"), "H" );
</script></body></html>
I wrote simple code for it without any third-party library. This code is only for a horizontal splitter (vertical is the same).
function onload()
{
dragElement( document.getElementById("separator"), "H" );
}
// This function is used for dragging and moving
function dragElement( element, direction, handler )
{
// Two variables for tracking positions of the cursor
const drag = { x : 0, y : 0 };
const delta = { x : 0, y : 0 };
/* If present, the handler is where you move the DIV from
otherwise, move the DIV from anywhere inside the DIV */
handler ? ( handler.onmousedown = dragMouseDown ): ( element.onmousedown = dragMouseDown );
// A function that will be called whenever the down event of the mouse is raised
function dragMouseDown( e )
{
drag.x = e.clientX;
drag.y = e.clientY;
document.onmousemove = onMouseMove;
document.onmouseup = () => { document.onmousemove = document.onmouseup = null; }
}
// A function that will be called whenever the up event of the mouse is raised
function onMouseMove( e )
{
const currentX = e.clientX;
const currentY = e.clientY;
delta.x = currentX - drag.x;
delta.y = currentY - drag.y;
const offsetLeft = element.offsetLeft;
const offsetTop = element.offsetTop;
const first = document.getElementById("first");
const second = document.getElementById("second");
let firstWidth = first.offsetWidth;
let secondWidth = second.offsetWidth;
if (direction === "H" ) // Horizontal
{
element.style.left = offsetLeft + delta.x + "px";
firstWidth += delta.x;
secondWidth -= delta.x;
}
drag.x = currentX;
drag.y = currentY;
first.style.width = firstWidth + "px";
second.style.width = secondWidth + "px";
}
}
.splitter {
width: 500px;
height: 100px;
display: flex;
}
#separator {
cursor: col-resize;
background: url(https://raw.githubusercontent.com/RickStrahl/jquery-resizable/master/assets/vsizegrip.png) center center no-repeat #535353;
width: 10px;
height: 100px;
min-width: 10px;
}
#first {
background-color: green;
width: 100px;
height: 100px;
min-width: 10px;
}
#second {
background-color: red;
width: 390px;
height: 100px;
min-width: 10px;
}
<html>
<head>
<link rel="stylesheet" href="T10-Splitter.css">
<script src="T10-Splitter.js"></script>
</head>
<body onload="onload()">
<div class="splitter">
<div id="first"></div>
<div id="separator"></div>
<div id="second"></div>
</div>
</body>
</html>
Here is my lightweight vanilla JavaScript approach, using Flexbox:
http://codepen.io/lingtalfi/pen/zoNeJp
It was tested successfully in Google Chrome 54, Firefox 50, Safari 10, don't know about other browsers.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.rawgit.com/lingtalfi/simpledrag/master/simpledrag.js"></script>
<style type="text/css">
html, body {
height: 100%;
}
.panes-container {
display: flex;
width: 100%;
overflow: hidden;
}
.left-pane {
width: 18%;
background: #ccc;
}
.panes-separator {
width: 2%;
background: red;
position: relative;
cursor: col-resize;
}
.right-pane {
flex: auto;
background: #eee;
}
.panes-container,
.panes-separator,
.left-pane,
.right-pane {
margin: 0;
padding: 0;
height: 100%;
}
</style>
</head>
<body>
<div class="panes-container">
<div class="left-pane" id="left-pane">
<p>I'm the left pane</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<div class="panes-separator" id="panes-separator"></div>
<div class="right-pane" id="right-pane">
<p>And I'm the right pane</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. A accusantium at cum cupiditate dolorum, eius eum
eveniet facilis illum maiores molestiae necessitatibus optio possimus sequi sunt, vel voluptate. Asperiores,
voluptate!
</p>
</div>
</div>
<script>
var leftPane = document.getElementById('left-pane');
var rightPane = document.getElementById('right-pane');
var paneSep = document.getElementById('panes-separator');
// The script below constrains the target to move horizontally between a left and a right virtual boundaries.
// - the left limit is positioned at 10% of the screen width
// - the right limit is positioned at 90% of the screen width
var leftLimit = 10;
var rightLimit = 90;
paneSep.sdrag(function (el, pageX, startX, pageY, startY, fix) {
fix.skipX = true;
if (pageX < window.innerWidth * leftLimit / 100) {
pageX = window.innerWidth * leftLimit / 100;
fix.pageX = pageX;
}
if (pageX > window.innerWidth * rightLimit / 100) {
pageX = window.innerWidth * rightLimit / 100;
fix.pageX = pageX;
}
var cur = pageX / window.innerWidth * 100;
if (cur < 0) {
cur = 0;
}
if (cur > window.innerWidth) {
cur = window.innerWidth;
}
var right = (100-cur-2);
leftPane.style.width = cur + '%';
rightPane.style.width = right + '%';
}, null, 'horizontal');
</script>
</body>
</html>
This HTML code depends on the simpledrag vanilla JavaScript lightweight library (less than 60 lines of code).
Hmm, I came across this property in CSS 3.
This might be easier to use.
CSS resize Property
In the old days, you would use frames to achieve this. There are several reasons why this approach is not so good. See Reece's response to Why are HTML frames bad?. See also Jakob Nielson's Why Frames Suck (Most of the Time).
A somewhat newer approach is to use inline frames. This has pluses and minuses as well: Are iframes considered 'bad practice'?
An even better approach is to use fixed positioning. By placing the navigation content (e.g. the favorites links in your example) in a block element (like a div) then applying position:fixed to that element and setting the left, top and bottom properties like this:
#myNav {
position: fixed;
left: 0px;
top: 0px;
bottom: 0px;
width: 200px;
}
... you will achieve a vertical column down the left side of the page that will not move when the user scrolls the page.
The rest of the content on the page will not "feel" the presence of this nav element, so it must take into account the 200px of space it occupies. You can do this by placing the rest for the content in another div and setting margin-left:200px;.
Many missed this post from Barguast on Feb 27 '15 where shows a interesting generic flexbox vertical and horizontal resizer.
Take a look: Flexbox Resizing
Barguast note that "... it only handles items sized with flex-grow. If flex-shrink or flex-basis is defined, then the calculations simply don't work.", and he is looking for a better solution, so do I.
Here is his code for reference:
function manageResize(md, sizeProp, posProp)
{
var r = md.target;
var prev = r.previousElementSibling;
var next = r.nextElementSibling;
if (!prev || !next) {
return;
}
md.preventDefault();
var prevSize = prev[sizeProp];
var nextSize = next[sizeProp];
var sumSize = prevSize + nextSize;
var prevGrow = Number(prev.style.flexGrow);
var nextGrow = Number(next.style.flexGrow);
var sumGrow = prevGrow + nextGrow;
var lastPos = md[posProp];
function onMouseMove(mm)
{
var pos = mm[posProp];
var d = pos - lastPos;
prevSize += d;
nextSize -= d;
if (prevSize < 0) {
nextSize += prevSize;
pos -= prevSize;
prevSize = 0;
}
if (nextSize < 0) {
prevSize += nextSize;
pos += nextSize;
nextSize = 0;
}
var prevGrowNew = sumGrow * (prevSize / sumSize);
var nextGrowNew = sumGrow * (nextSize / sumSize);
prev.style.flexGrow = prevGrowNew;
next.style.flexGrow = nextGrowNew;
lastPos = pos;
}
function onMouseUp(mu)
{
window.removeEventListener("mousemove", onMouseMove);
window.removeEventListener("mouseup", onMouseUp);
}
window.addEventListener("mousemove", onMouseMove);
window.addEventListener("mouseup", onMouseUp);
}
function setupResizerEvents()
{
document.body.addEventListener("mousedown", function (md) {
var target = md.target;
if (target.nodeType !== 1 || target.tagName !== "FLEX-RESIZER") {
return;
}
var parent = target.parentNode;
var h = parent.classList.contains("h");
var v = parent.classList.contains("v");
if (h && v) {
return;
} else if (h) {
manageResize(md, "scrollWidth", "pageX");
} else if (v) {
manageResize(md, "scrollHeight", "pageY");
}
});
}
setupResizerEvents();
flex {
display: flex;
}
flex-item > flex {
position: absolute;
width: 100%;
height: 100%;
}
flex.h {
-ms-flex-direction: row;
flex-direction: row;
}
flex.v {
-ms-flex-direction: column;
flex-direction: column;
}
flex-item {
display: flex;
position: relative;
overflow: hidden;
}
flex > flex-resizer {
-ms-flex: 0 0 8px;
flex: 0 0 8px;
background: white;
}
flex.h > flex-resizer {
cursor: ew-resize;
}
flex.v > flex-resizer {
cursor: ns-resize;
}
<body>
<flex class="v" style="height: 500px">
<flex-item style="flex: 1; background: red">Flex 1</flex-item>
<flex-resizer></flex-resizer>
<flex-item style="flex: 1; background: blue">
<flex class="h">
<flex-item style="flex: 1">Flex 2</flex-item>
<flex-resizer></flex-resizer>
<flex-item style="flex: 2; background: green">
<flex class="v">
<flex-item style="flex: 1; background: pink;">Flex 3</flex-item>
<flex-resizer></flex-resizer>
<flex-item style="flex: 1">
<flex class="h">
<flex-item style="flex: 1">Flex 4</flex-item>
<flex-resizer></flex-resizer>
<flex-item style="flex: 2; background: yellow">Flex 5</flex-item>
<flex-item style="flex: 2; background: yellow">Flex 6</flex-item>
</flex>
</flex-item>
</flex>
</flex-item>
</flex>
</flex-item>
</flex>
</body>
And here is my improved version:
function manageResize(md, sizeProp, posProp) {
var r = md.target;
var prev = r.previousElementSibling;
var next = r.nextElementSibling;
if (!prev || !next) {
return;
}
md.preventDefault();
var prevSize = prev[sizeProp];
var nextSize = next[sizeProp];
var sumSize = prevSize + nextSize;
var prevGrow = Number(prev.style.flexGrow);
var nextGrow = Number(next.style.flexGrow);
var sumGrow = prevGrow + nextGrow;
var lastPos = md[posProp];
function onMouseMove(mm) {
var pos = mm[posProp];
var d = pos - lastPos;
prevSize += d;
nextSize -= d;
if (prevSize < 0) {
nextSize += prevSize;
pos -= prevSize;
prevSize = 0;
}
if (nextSize < 0) {
prevSize += nextSize;
pos += nextSize;
nextSize = 0;
}
var prevGrowNew = sumGrow * (prevSize / sumSize);
var nextGrowNew = sumGrow * (nextSize / sumSize);
prev.style.flexGrow = prevGrowNew;
next.style.flexGrow = nextGrowNew;
lastPos = pos;
}
function onMouseUp(mu) {
// Change cursor to signal a state's change: stop resizing.
const html = document.querySelector('html');
html.style.cursor = 'default';
if (posProp === 'pageX') {
r.style.cursor = 'ew-resize';
} else {
r.style.cursor = 'ns-resize';
}
window.removeEventListener("mousemove", onMouseMove);
window.removeEventListener("mouseup", onMouseUp);
}
window.addEventListener("mousemove", onMouseMove);
window.addEventListener("mouseup", onMouseUp);
}
function setupResizerEvents() {
document.body.addEventListener("mousedown", function (md) {
// Used to avoid cursor's flickering
const html = document.querySelector('html');
var target = md.target;
if (target.nodeType !== 1 || target.tagName !== "FLEX-RESIZER") {
return;
}
var parent = target.parentNode;
var h = parent.classList.contains("h");
var v = parent.classList.contains("v");
if (h && v) {
return;
} else if (h) {
// Change cursor to signal a state's change: begin resizing on H.
target.style.cursor = 'col-resize';
html.style.cursor = 'col-resize'; // avoid cursor's flickering
// use offsetWidth versus scrollWidth (and clientWidth) to avoid splitter's jump on resize when a flex-item content overflow (overflow: auto).
manageResize(md, "offsetWidth", "pageX");
} else if (v) {
// Change cursor to signal a state's change: begin resizing on V.
target.style.cursor = 'row-resize';
html.style.cursor = 'row-resize'; // avoid cursor's flickering
manageResize(md, "offsetHeight", "pageY");
}
});
}
setupResizerEvents();
body {
/* margin:0; */
border: 10px solid #aaa;
}
flex {
display: flex;
overflow: hidden;
}
/* flex-item > flex {
position: absolute;
width: 100%;
height: 100%;
} */
flex.h {
flex-direction: row;
}
flex.v {
flex-direction: column;
}
flex-item {
/* display: flex; */
/* position: relative; */
/* overflow: hidden; */
overflow: auto;
}
flex > flex-resizer {
flex: 0 0 10px;
/* background: white; */
background-color: #aaa;
background-repeat: no-repeat;
background-position: center;
}
flex.h > flex-resizer {
cursor: ew-resize;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='30'><path d='M2 0 v30 M5 0 v30 M8 0 v30' fill='none' stroke='black'/></svg>");
}
flex.v > flex-resizer {
cursor: ns-resize;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='10'><path d='M0 2 h30 M0 5 h30 M0 8 h30' fill='none' stroke='black'/></svg>");
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>flex-splitter</title>
<link rel="stylesheet" href="./src/styles.css">
<script src="./src/index.js" defer></script>
</head>
<body>
<flex class="v" style="flex: 1; height: 500px;">
<flex-item style="flex: 1;">Flex 1</flex-item>
<flex-resizer></flex-resizer>
<flex class="h" style="flex: 1;">
<flex-item style="flex: 1; background-color: aqua;">
<!--
The next section is an example to test the splitter when there is content inside a flex-item
-->
<section>
<div>
<label for="CursorCoor" style="display: block;">showCursorCoor: </label>
<textarea id="CursorCoor" rows="6" cols="50" wrap="soft" readonly></textarea>
</div>
<br />
<div>
<label for="boxInfo" style="display: block;">showBoxInfo: </label>
<textarea id="boxInfo" rows="6" cols="50" wrap="soft" readonly></textarea>
</div>
</section>
</flex-item>
<flex-resizer></flex-resizer>
<flex class="v" style="flex: 2; ">
<flex-item style="flex: 1; background: pink;">Flex 3</flex-item>
<flex-resizer></flex-resizer>
<flex class="h" style="flex: 1">
<flex-item style="flex: 1; background: green;">Flex 4</flex-item>
<flex-resizer></flex-resizer>
<flex-item style="flex: 2;">Flex 5</flex-item>
<!-- <flex-resizer></flex-resizer> -->
<flex-item style="flex: 3; background: darkorange;">Flex 6</flex-item>
</flex>
</flex>
</flex>
</flex>
</body>
</html>
Or see it on Codesandbox:
You can do it with jQuery UI without another JavaScript library. Just add a function to the .resizable resize event to adjust the width of the other div.
$("#left_pane").resizable({
handles: 'e', // 'East' side of div draggable
resize: function() {
$("#right_pane").outerWidth( $("#container").innerWidth() - $("#left_pane").outerWidth() );
}
});
Here's the complete JSFiddle.
One totally different approach is to put things in a grid, such as ui-grid or Kendo's grid, and have the columns be resizable. A downside is that users would not be able to resize the rows, though the row size could be set programmatically.
You can use absolute of fixed positioning. This CSS for example will dock a 2em-bar on the left side of your page:
body {
padding-left: 2.5em;
}
body > #bar {
position:fixed;
top:0; left:0;
width: 2em;
height: 100%;
border-right: 2px solid #55F; background: #ddd;
}
(Demo at jsfiddle.net)
The Angular version with no third-party libraries (based on personal_cloud's answer):
import { Component, Renderer2, ViewChild, ElementRef, AfterViewInit, OnDestroy } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit, OnDestroy {
#ViewChild('leftPanel', {static: true})
leftPanelElement: ElementRef;
#ViewChild('rightPanel', {static: true})
rightPanelElement: ElementRef;
#ViewChild('separator', {static: true})
separatorElement: ElementRef;
private separatorMouseDownFunc: Function;
private documentMouseMoveFunc: Function;
private documentMouseUpFunc: Function;
private documentSelectStartFunc: Function;
private mouseDownInfo: any;
constructor(private renderer: Renderer2) {
}
ngAfterViewInit() {
// Init page separator
this.separatorMouseDownFunc = this.renderer.listen(this.separatorElement.nativeElement, 'mousedown', e => {
this.mouseDownInfo = {
e: e,
offsetLeft: this.separatorElement.nativeElement.offsetLeft,
leftWidth: this.leftPanelElement.nativeElement.offsetWidth,
rightWidth: this.rightPanelElement.nativeElement.offsetWidth
};
this.documentMouseMoveFunc = this.renderer.listen('document', 'mousemove', e => {
let deltaX = e.clientX - this.mouseDownInfo.e.x;
// set min and max width for left panel here
const minLeftSize = 30;
const maxLeftSize = (this.mouseDownInfo.leftWidth + this.mouseDownInfo.rightWidth + 5) - 30;
deltaX = Math.min(Math.max(deltaX, minLeftSize - this.mouseDownInfo.leftWidth), maxLeftSize - this.mouseDownInfo.leftWidth);
this.leftPanelElement.nativeElement.style.width = this.mouseDownInfo.leftWidth + deltaX + 'px';
});
this.documentSelectStartFunc = this.renderer.listen('document', 'selectstart', e => {
e.preventDefault();
});
this.documentMouseUpFunc = this.renderer.listen('document', 'mouseup', e => {
this.documentMouseMoveFunc();
this.documentSelectStartFunc();
this.documentMouseUpFunc();
});
});
}
ngOnDestroy() {
if (this.separatorMouseDownFunc) {
this.separatorMouseDownFunc();
}
if (this.documentMouseMoveFunc) {
this.documentMouseMoveFunc();
}
if (this.documentMouseUpFunc) {
this.documentMouseUpFunc();
}
if (this.documentSelectStartFunc()) {
this.documentSelectStartFunc();
}
}
}
.main {
display: flex;
height: 400px;
}
.left {
width: calc(50% - 5px);
background-color: rgba(0, 0, 0, 0.1);
}
.right {
flex: auto;
background-color: rgba(0, 0, 0, 0.2);
}
.separator {
width: 5px;
background-color: red;
cursor: col-resize;
}
<div class="main">
<div class="left" #leftPanel></div>
<div class="separator" #separator></div>
<div class="right" #rightPanel></div>
</div>
Running example on Stackblitz
I found a working splitter, http://www.dreamchain.com/split-pane/, which works with jQuery v1.9. Note I had to add the following CSS code to get it working with a fixed bootstrap navigation bar.
fixed-left {
position: absolute !important; /* to override relative */
height: auto !important;
top: 55px; /* Fixed navbar height */
bottom: 0px;
}
A good library is Shield UI - you can take a look at their flexible Splitter widget and the rest of the powerful components the framework offers.