Skip to content

Instantly share code, notes, and snippets.

@tech-andgar
Created April 6, 2025 23:03
Show Gist options
  • Select an option

  • Save tech-andgar/4a971b92925226cacc5c244f58fea481 to your computer and use it in GitHub Desktop.

Select an option

Save tech-andgar/4a971b92925226cacc5c244f58fea481 to your computer and use it in GitHub Desktop.
Resizable split views native HTML / JS / CSS - ALL Vanilla
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Split View</title>
<style>
/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
overflow: hidden;
height: 100%;
width: 100%;
}
.container {
display: flex;
width: 100%;
height: 100vh;
/* Use viewport height to ensure full height */
flex-direction: row;
/* Horizontal by default (for desktop) */
}
.panel {
overflow: auto;
padding: 20px;
box-sizing: border-box;
}
.left-panel {
background-color: #f0f0f0;
width: 50%;
min-width: 100px;
max-width: calc(100% - 108px);
height: 100%;
/* Ensure full height */
}
.right-panel {
background-color: #e0e0e0;
flex-grow: 1;
width: 50%;
min-width: 100px;
max-width: calc(100% - 108px);
height: 100%;
/* Ensure full height */
}
.divider {
width: 8px;
background-color: #ccc;
cursor: col-resize;
display: flex;
justify-content: center;
align-items: center;
transition: background-color 0.2s;
flex-shrink: 0;
height: 100%;
/* Ensure full height */
}
.divider:hover,
.divider.active {
background-color: #999;
}
.divider-handle {
height: 30px;
width: 4px;
background-color: #888;
border-radius: 2px;
}
/* Vertical layout styles (for mobile) */
.container.vertical {
flex-direction: column;
height: 100vh;
/* Ensure full viewport height */
}
.left-panel.vertical {
height: 50%;
width: 100%;
max-width: 100%;
max-height: calc(100% - 108px);
}
.right-panel.vertical {
height: 50%;
width: 100%;
max-width: 100%;
max-height: calc(100% - 108px);
}
.divider.vertical {
height: 8px;
width: 100%;
cursor: row-resize;
}
.divider-handle.vertical {
height: 4px;
width: 30px;
}
/* Media query for mobile devices */
@media (max-width: 768px) {
.container {
flex-direction: column;
height: 100vh;
/* Ensure full viewport height */
}
.left-panel {
height: 50%;
width: 100%;
max-width: 100%;
max-height: calc(100% - 108px);
}
.right-panel {
height: 50%;
width: 100%;
max-width: 100%;
max-height: calc(100% - 108px);
}
.divider {
height: 8px;
width: 100%;
cursor: row-resize;
}
.divider-handle {
height: 4px;
width: 30px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="panel left-panel">
<h2>Left/Top Panel</h2>
<p>This is the content for the first panel. You can resize by dragging the divider.</p>
<p>The layout automatically switches to vertical on mobile devices and horizontal on desktop.</p>
<button onclick="toggleLayout()">Toggle Layout</button>
</div>
<div class="divider">
<div class="divider-handle"></div>
</div>
<div class="panel right-panel">
<h2>Right/Bottom Panel</h2>
<p>This is the content for the second panel. You can resize by dragging the divider.</p>
<p>The panels automatically adjust to the screen size and orientation.</p>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const container = document.querySelector('.container');
const divider = document.querySelector('.divider');
const leftPanel = document.querySelector('.left-panel');
const rightPanel = document.querySelector('.right-panel');
const dividerHandle = document.querySelector('.divider-handle');
let isDragging = false;
let initialPos = 0;
let initialLeftSize = 0;
let initialRightSize = 0;
let containerSize = 0;
let currentLayout = window.innerWidth <= 768 ? 'vertical' : 'horizontal';
// Function to check if the layout should be vertical based on screen size
function shouldBeVerticalLayout() {
return window.innerWidth <= 768;
}
// Function to check if the layout is currently vertical
function isVerticalLayout() {
return container.classList.contains('vertical');
}
// Function to update layout classes
function updateLayoutClasses(forceLayout = null) {
// Determine if layout should change
const shouldBeVertical = forceLayout !== null ? forceLayout === 'vertical' : shouldBeVerticalLayout();
// Only change if needed
if (shouldBeVertical !== isVerticalLayout()) {
if (shouldBeVertical) {
// Switch to vertical layout
container.classList.add('vertical');
leftPanel.classList.add('vertical');
rightPanel.classList.add('vertical');
divider.classList.add('vertical');
dividerHandle.classList.add('vertical');
// Reset panel sizes for vertical layout
leftPanel.style.width = '100%';
leftPanel.style.height = '50%';
rightPanel.style.width = '100%';
rightPanel.style.height = '50%';
currentLayout = 'vertical';
} else {
// Switch to horizontal layout
container.classList.remove('vertical');
leftPanel.classList.remove('vertical');
rightPanel.classList.remove('vertical');
divider.classList.remove('vertical');
dividerHandle.classList.remove('vertical');
// Reset panel sizes for horizontal layout
leftPanel.style.height = '100%';
leftPanel.style.width = '50%';
rightPanel.style.height = '100%';
rightPanel.style.width = '50%';
currentLayout = 'horizontal';
}
}
}
// Initialize layout on load
updateLayoutClasses();
// Set initial container size
function updateContainerSize() {
if (isVerticalLayout()) {
containerSize = container.offsetHeight;
} else {
containerSize = container.offsetWidth;
}
}
updateContainerSize();
// Add event listeners for mouse and touch events
divider.addEventListener('mousedown', initDrag);
divider.addEventListener('touchstart', initDrag);
function initDrag(e) {
isDragging = true;
divider.classList.add('active');
updateContainerSize();
const vertical = isVerticalLayout();
if (vertical) {
// For vertical layout
initialLeftSize = leftPanel.offsetHeight;
initialRightSize = rightPanel.offsetHeight;
if (e.type === 'mousedown') {
initialPos = e.clientY;
} else if (e.type === 'touchstart') {
initialPos = e.touches[0].clientY;
}
} else {
// For horizontal layout
initialLeftSize = leftPanel.offsetWidth;
initialRightSize = rightPanel.offsetWidth;
if (e.type === 'mousedown') {
initialPos = e.clientX;
} else if (e.type === 'touchstart') {
initialPos = e.touches[0].clientX;
}
}
// Add event listeners for dragging
document.addEventListener('mousemove', doDrag);
document.addEventListener('touchmove', doDrag);
document.addEventListener('mouseup', stopDrag);
document.addEventListener('touchend', stopDrag);
document.addEventListener('touchcancel', stopDrag);
// Prevent text selection during drag
e.preventDefault();
}
function doDrag(e) {
if (!isDragging) return;
let currentPos = 0;
const vertical = isVerticalLayout();
const minSize = 100; // Minimum panel size
const dividerSize = vertical ? divider.offsetHeight : divider.offsetWidth;
if (vertical) {
// For vertical layout
if (e.type === 'mousemove') {
currentPos = e.clientY;
} else if (e.type === 'touchmove') {
currentPos = e.touches[0].clientY;
}
const offset = currentPos - initialPos;
// Calculate new heights
let newTopHeight = initialLeftSize + offset;
let newBottomHeight = initialRightSize - offset;
// Apply constraints
if (newTopHeight < minSize) {
newTopHeight = minSize;
newBottomHeight = containerSize - minSize - dividerSize;
} else if (newBottomHeight < minSize) {
newBottomHeight = minSize;
newTopHeight = containerSize - minSize - dividerSize;
}
// Update panel heights
const topHeightPercent = (newTopHeight / containerSize) * 100;
const bottomHeightPercent = (newBottomHeight / containerSize) * 100;
leftPanel.style.height = `${topHeightPercent}%`;
rightPanel.style.height = `${bottomHeightPercent}%`;
} else {
// For horizontal layout
if (e.type === 'mousemove') {
currentPos = e.clientX;
} else if (e.type === 'touchmove') {
currentPos = e.touches[0].clientX;
}
const offset = currentPos - initialPos;
// Calculate new widths
let newLeftWidth = initialLeftSize + offset;
let newRightWidth = initialRightSize - offset;
// Apply constraints
if (newLeftWidth < minSize) {
newLeftWidth = minSize;
newRightWidth = containerSize - minSize - dividerSize;
} else if (newRightWidth < minSize) {
newRightWidth = minSize;
newLeftWidth = containerSize - minSize - dividerSize;
}
// Update panel widths
const leftWidthPercent = (newLeftWidth / containerSize) * 100;
const rightWidthPercent = (newRightWidth / containerSize) * 100;
leftPanel.style.width = `${leftWidthPercent}%`;
rightPanel.style.width = `${rightWidthPercent}%`;
}
e.preventDefault();
}
function stopDrag() {
isDragging = false;
divider.classList.remove('active');
// Remove event listeners
document.removeEventListener('mousemove', doDrag);
document.removeEventListener('touchmove', doDrag);
document.removeEventListener('mouseup', stopDrag);
document.removeEventListener('touchend', stopDrag);
document.removeEventListener('touchcancel', stopDrag);
}
// Prevent default drag behavior
divider.addEventListener('dragstart', function (e) {
e.preventDefault();
});
// Handle window resize - improved to actually change layout
let resizeTimeout;
window.addEventListener('resize', function () {
// Debounce resize event
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function () {
const shouldBeVertical = shouldBeVerticalLayout();
const isCurrentlyVertical = isVerticalLayout();
// Only update if layout should change
if (shouldBeVertical !== isCurrentlyVertical) {
// Force layout update
updateLayoutClasses();
}
// Always update container size
updateContainerSize();
}, 250);
});
// Global function to toggle layout manually
window.toggleLayout = function () {
const currentVertical = isVerticalLayout();
updateLayoutClasses(currentVertical ? 'horizontal' : 'vertical');
updateContainerSize();
};
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment