im trying to make static grid with a button that can change number of boxes in it (from 16x16 to 64x64 and anything between). Grid is 40rem x 40rem, when i try to change manually number of boxes in makeGrid() function it works fine up to 20 (boxes change size accordingly), but anything above 20 stays the same size and gets cutoff from my grid. If there is no grid css overflow property stated, grid width change depending on number of boxes but boxes themself won't shrink
my code:
size button is not working yet, grid size need to be changed mannualy in makeGrid function
const grid = document.getElementById('grid');
const size = document.getElementById('size');
const eraser = document.getElementById('eraser');
const color = document.getElementById('color');
const gridBorder = document.getElementById('grid-borders');
const clear = document.getElementById('clear');
// grid
function makeGrid(number) {
number = number || 16;
let cellWidth = 40 / number + 'rem';
let cellHeight = 40 / number + 'rem';
grid.style.gridTemplateColumns = `repeat( ${number}, 1fr)`;
grid.style.gridTemplateRows = `repeat(${number}, 1fr)`;
for (let i = 0; i < number * number; i++) {
let cell = document.createElement('div');
grid.appendChild(cell).id = 'box';
cell.classList.add('border');
cell.classList.add('box');
cell.style.backgroundColor = 'white';
cell.style.width = cellWidth;
cell.style.height = cellHeight;
}
size.textContent = `${number} x ${number}`;
}
makeGrid();
// drawing on hover
color.addEventListener('click', function () {
grid.addEventListener('mouseover', function (e) {
e.target !== grid ? (e.target.style.backgroundColor = 'black') : null;
});
});
function changeColor(event) {
event.target.style.backgroundColor = 'black';
}
// erase functionality
eraser.addEventListener('click', function () {
grid.addEventListener('mouseover', function (e) {
e.target !== grid ? (e.target.style.backgroundColor = 'white') : null;
});
});
// grid borders
const allBoxes = document.querySelectorAll('.box');
gridBorder.addEventListener('click', function () {
allBoxes.forEach((box) => {
box.classList.toggle('no-border');
box.classList.toggle('border');
});
});
// clear button
clear.addEventListener('click', function () {
allBoxes.forEach((box) => {
box.style.backgroundColor = 'white';
});
});
// size button
// size.addEventListener('click', function () {
// let number = prompt(`Enter grid size less or equal to 100`);
// if (number !== Number.isInteger()) {
// return;
// } else if (number > 100) {
// number = prompt(`Enter grid size greater or equal to 100`);
// }
// });
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
background-color: aquamarine;
}
#grid {
display: grid;
justify-content: center;
border: 1px solid #ccc;
width: 40rem;
height: 40rem;
min-width: 0;
min-height: 0;
overflow: hidden;
}
.box {
padding: 1em;
}
#title {
display: flex;
align-items: flex-end;
justify-content: center;
height: 180px;
}
#container {
display: flex;
height: 60%;
width: 1259px;
align-items: flex-start;
justify-content: flex-end;
gap: 20px;
padding-top: 20px;
}
#menu {
display: flex;
flex-direction: column;
gap: 10px;
}
.border {
outline: 1px solid black;
}
.no-border {
outline: none;
}
.black-bg {
background: black;
}
.white-bg {
background: white;
}
<!DOCTYPE html>
<html lang="en">
<head>
<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>Etch-a-Sketch</title>
<link rel="stylesheet" href="style.css" />
<script src="script.js" defer></script>
</head>
<body>
<div id="title">
<h1>Etch-a-Sketch</h1>
</div>
<main id="container">
<div id="menu">
<button id="size"></button>
<button id="color">Color</button>
<button id="eraser">Eraser</button>
<button id="clear">Clear</button>
<button id="grid-borders">Grid Borders</button>
</div>
<div id="grid"></div>
</main>
</body>
</html>
"Why won't my grid cells go below 32px?" - have you checked your padding (hint: 32px is exactly equal to 2 * 16px which in turn is exactly equal to your padding of 1em with most browsers implementing a default font-size of 16px). –
David Thomas
box padding was set to 1em which caused my problem, after deleting it my grid worked as intended
I followed this guide and I tried implementing the code myself, but when I run it I get a "move is not defined" error. Any suggestions on what I did wrong? Tried moving the script reference around or not including the event listeners in the html tag which would constrain the movement to only inside the container, but nothing seems to change? Any suggestions? Thanks!
let moving = null;
function pickup(event) {
moving = event.target;
moving.style.height = moving.clientHeight;
moving.style.width = moving.clientWidth;
moving.style.position = 'fixed';
moving.style.zIndex = '-10';
}
function move(event) {
if (moving) {
if (event.clientX) {
// mousemove
moving.style.left = event.clientX - moving.clientWidth/2;
moving.style.top = event.clientY - moving.clientHeight/2;
} else {
// touchmove - assuming a single touchpoint
moving.style.left = event.changedTouches[0].clientX - moving.clientWidth/2;
moving.style.top = event.changedTouches[0].clientY - moving.clientHeight/2;
}
}
}
function drop(event) {
if (moving) {
if (event.currentTarget.tagName !== 'HTML') {
let target = null;
if (event.clientX) {
target = document.elementFromPoint(event.clientX, event.clientY);
} else {
target = document.elementFromPoint(event.changedTouches[0].clientX, event.changedTouches[0].clientY);
}
target.appendChild(moving);
}
// reset our element
moving.style.left = '';
moving.style.top = '';
moving.style.height = '';
moving.style.width = '';
moving.style.position = '';
moving.style.zIndex = '';
moving = null;
}
}
* {
box-sizing: border-box;
}
#container {
display: flex;
}
#container > div {
border: 1px solid gray;
padding: 1em;
height: 10em;
width: 50%;
}
#movable-element {
border: 1px solid green;
background-color: #00ff0033;
height: 100%;
width: 100%;
}
<html onmouseup="drop(event)" ontouchend="drop(event)" onmousemove="move(event)" ontouchmove="move(event)">
<head>
<title>Drag and Drop</title>
<script src="script.js"></script>
<link rel="stylesheet" href="style.css">
<body>
<div id="container">
<div id="left-parent" onmouseup="drop(event)" ontouchend="drop(event)">
<div id="movable-element" onmousedown="pickup(event)" ontouchstart="pickup(event)"></div>
</div>
<div id="right-parent" onmouseup="drop(event)" ontouchend="drop(event)"></div>
</div>
</body>
The problem with the "move is not defined" is because you're setting the move event listener on the HTML tag, which is before you defined the "move" function.
The solution is to move the event listeners to the body tag, which is after the move function is defined.
There are other issues with this code, but that addresses the one issue in your question. If you need further help with the other issues please post a new question.
let moving = null;
function pickup(event) {
moving = event.target;
moving.style.height = moving.clientHeight;
moving.style.width = moving.clientWidth;
moving.style.position = 'fixed';
moving.style.zIndex = '-10';
}
function move(event) {
if (moving) {
if (event.clientX) {
// mousemove
moving.style.left = event.clientX - moving.clientWidth/2;
moving.style.top = event.clientY - moving.clientHeight/2;
} else {
// touchmove - assuming a single touchpoint
moving.style.left = event.changedTouches[0].clientX - moving.clientWidth/2;
moving.style.top = event.changedTouches[0].clientY - moving.clientHeight/2;
}
}
}
function drop(event) {
if (moving) {
if (event.currentTarget.tagName !== 'HTML') {
let target = null;
if (event.clientX) {
target = document.elementFromPoint(event.clientX, event.clientY);
} else {
target = document.elementFromPoint(event.changedTouches[0].clientX, event.changedTouches[0].clientY);
}
target.appendChild(moving);
}
// reset our element
moving.style.left = '';
moving.style.top = '';
moving.style.height = '';
moving.style.width = '';
moving.style.position = '';
moving.style.zIndex = '';
moving = null;
}
}
* {
box-sizing: border-box;
}
#container {
display: flex;
}
#container > div {
border: 1px solid gray;
padding: 1em;
height: 10em;
width: 50%;
}
#movable-element {
border: 1px solid green;
background-color: #00ff0033;
height: 100%;
width: 100%;
}
<html>
<head>
<title>Drag and Drop</title>
<script src="script.js"></script>
<link rel="stylesheet" href="style.css">
<body onmouseup="drop(event)" ontouchend="drop(event)" onmousemove="move(event)" ontouchmove="move(event)">
<div id="container">
<div id="left-parent" onmouseup="drop(event)" ontouchend="drop(event)">
<div id="movable-element" onmousedown="pickup(event)" ontouchstart="pickup(event)"></div>
</div>
<div id="right-parent" onmouseup="drop(event)" ontouchend="drop(event)"></div>
</div>
</body>
I have an inner circle which tracks the position of my pointer, Now I want the circle color to change when it hovers on the h2 tag.
can anyone help??
I have tried searching few places but all were on "mouseover".
note: only when the inner-circle hovers on h2 not the mouse pointer, the inner circle's color should change.
<!doctype>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Roll Over Eyes</title>
<style>
body
{
display: flex;
align-items: center;
justify-content: center;
height: 900px;
border: 1px solid black;
}
.main-circle
{
display: flex;
align-items: center;
justify-content: center;
position: relative;
height: 350px;
width: 350px;
border: 1px solid black;
border-radius: 10%;
}
#inner-circle
{
position: absolute;
height: 50px;
width: 50px;
border: 1px solid black;
border-radius: 50%;
background: #af4b23;
}
.message
{
display:none;
width: 75%;
}
</style>
</head>
<body>
<div class="main-circle">
<div id="inner-circle">
</div>
<h2 class="message">Your Outside the box</h2>
<h2 class="message">Your inside the box, now move the mouse to move the circle.</h2>
</div>
<script>
(function()
{
var ele= document.getElementById("inner-circle");
var hea= document.getElementsByClassName("message");
var body=document.body;
var height=body.clientHeight;
var width=body.clientWidth;
var move = function(event)
{
var x= event.clientX;
var y= event.clientY;
ele.style.left = ( x/width )*300;
ele.style.top = ( y/height )*300;
}
var display = function()
{
console.log("done");
hea[0].style.display="inline-block";
hea[1].style.display="none";
}
var hide = function()
{
console.log("done hiden");
hea[0].style.display="none";
hea[1].style.display="inline-block";
}
var effect = function()
{
ele.style.backgroundColor= "rgba(0,0,0,0.5)";
}
var deflt = function()
{
ele.style.backgroundColor= "#af4b23";
}
body.addEventListener("mousemove",function(){ move(event) },false);
body.addEventListener("mouseout",display,false);
body.addEventListener("mouseover",hide,false);
hea[1].addEventListener("mouseover",effect,false);
hea[1].addEventListener("mouseout",deflt,false);
})();
</script>
</body>
</html>
You can try this:
(function()
{
var ele= document.getElementById("inner-circle");
var hea= document.getElementsByClassName("message");
var body=document.body;
var height=body.clientHeight;
var width=body.clientWidth;
var move = function(event)
{
var x= event.clientX;
var y= event.clientY;
ele.style.left = ( x/width )*300;
ele.style.top = ( y/height )*300;
var e_pos = ele.getBoundingClientRect();
var h_pos = hea[1].getBoundingClientRect();
if(e_pos.bottom>=h_pos.top && e_pos.top<=h_pos.bottom && e_pos.right>=h_pos.left && e_pos.left<=h_pos.right){
ele.style.backgroundColor= "rgba(0,0,0,0.5)";
}else{
ele.style.backgroundColor= "#af4b23";
};
}
var display = function()
{
//console.log("done");
hea[0].style.display="inline-block";
hea[1].style.display="none";
}
var hide = function()
{
//console.log("done hiden");
hea[0].style.display="none";
hea[1].style.display="inline-block";
}
body.addEventListener("mousemove",function(){ move(event) },false);
body.addEventListener("mouseout",display,false);
body.addEventListener("mouseover",hide,false);
})();
in this answer i set background-color after setting left and top of ele. also i have removed two last eventListeners.
Look at the result online and change it yorself!
the border is added to detect h2's bounds.
May be this is small question. But I couldn't found reason for this. I made a script to change a position of div by dragging it. the code is working fine with chrome browser. but when I trying to test it on Firefox it is not working.
var h = window.innerHeight|| document.documentElement.clientHeight || document.body.clientHeight;
window.onload = function () {
// ------------------lock the div with mouse pointer--------------
// variable dragged is for identified that you are click on the button or not
var dragged = false,
y = 0,pointerDis = 0,
boxElement = document.getElementById('drag'),
drgElement = document.getElementById('titl');
if (boxElement) {
// -----------------check whether the title div is holding by the mouse to lock it with mouse-------
drgElement.addEventListener('mousedown', function() {
dragged = true;
pointerDis = event.clientY - parseInt(window.getComputedStyle(boxElement, null).getPropertyValue("top"));
});
//------------------check whether the title div is released to drop the div-------------------------
document.addEventListener('mouseup', function() {
dragged = false;
});
document.addEventListener('mousemove', function () {
y = event.clientY;
if(dragged == true)
{
y = y -pointerDis;
if(y<0)
{
y = 0;
}
else if(y > window.innerHeight - boxElement.offsetHeight)
{
y = window.innerHeight - boxElement.offsetHeight;
}
boxElement.style.top = y + 'px';
}
});
}
};
.drg {
position: absolute;
top:0;
right: 0;
background: red;
border-top-left-radius: 45px;
border-bottom-left-radius: 45px;
}
#titl{
background: blue;
width: 50px;
text-align: center;
color: white;
font-size: 30px;
border-top-left-radius: 10px;
}
#det{
background: #f9c500;
width: 50px;
border-bottom-left-radius: 10px;
text-align: center;
}
<!DOCTYPE html>
<html>
<head>
<title>test 4</title>
</head>
<body>
<div class = "drg" id="drag">
<div id="titl" unselectable="on" onselectstart="return false;">....</div>
<div id="det">this is the details menu</div>
</div>
</body>
</html>
You can drag it through Y axis by click and drag from blue div. I don't know the reason or I couldn't find a way to fix this work on Firefox. Please help me!
You have to catch the (mousemove or mousedown) events as the input of wrapped functions
drgElement.addEventListener('mousedown', function(event)...
var h = window.innerHeight|| document.documentElement.clientHeight || document.body.clientHeight;
window.onload = function () {
// ------------------lock the div with mouse pointer--------------
// variable dragged is for identified that you are click on the button or not
var dragged = false,
y = 0,pointerDis = 0,
boxElement = document.getElementById('drag'),
drgElement = document.getElementById('titl');
if (boxElement) {
// -----------------check whether the title div is holding by the mouse to lock it with mouse-------
drgElement.addEventListener('mousedown', function(event) {
dragged = true;
pointerDis = event.clientY - parseInt(window.getComputedStyle(boxElement, null).getPropertyValue("top"));
});
//------------------check whether the title div is released to drop the div-------------------------
document.addEventListener('mouseup', function() {
dragged = false;
});
document.addEventListener('mousemove', function (event) {
y = event.clientY;
if(dragged == true)
{
y = y -pointerDis;
if(y<0)
{
y = 0;
}
else if(y > window.innerHeight - boxElement.offsetHeight)
{
y = window.innerHeight - boxElement.offsetHeight;
}
boxElement.style.top = y + 'px';
}
});
}
};
.drg {
position: absolute;
top:0;
right: 0;
background: red;
border-top-left-radius: 45px;
border-bottom-left-radius: 45px;
}
#titl{
background: blue;
width: 50px;
text-align: center;
color: white;
font-size: 30px;
border-top-left-radius: 10px;
}
#det{
background: #f9c500;
width: 50px;
border-bottom-left-radius: 10px;
text-align: center;
}
<!DOCTYPE html>
<html>
<head>
<title>test 4</title>
</head>
<body>
<div class = "drg" id="drag">
<div id="titl" unselectable="on" onselectstart="return false;">....</div>
<div id="det">this is the details menu</div>
</div>
</body>
</html>
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.