Is there a way to check the bottom of a child div? - javascript

I'm trying to implement an Infinity scroll.
But not a window object, the target is a child div with a scroller.
Is there a way to examine the current height of a child div with JavaScript?
For example, I would like to request an event when the scroll touches at the end.
This is my template code.
<div
style="overflow-y: scroll; height:500px;"
class="scroll-content"
#scroll="onScroll"
>

Here is an example:
var listElm = document.querySelector('#infinite-list');
// Add items.
var nextItem = 1;
var loadMore = function() {
for (var i = 0; i < 10; i++) {
var item = document.createElement('li');
item.innerText = 'Item ' + nextItem++;
listElm.appendChild(item);
}
}
// Detect when scrolled to bottom.
listElm.addEventListener('scroll', function() {
if (listElm.scrollTop + listElm.clientHeight >= listElm.scrollHeight) {
loadMore();
}
});
// Initially load some items.
loadMore();
#infinite-list {
/* We need to limit the height and show a scrollbar */
width: 200px;
height: 100px;
overflow: auto;
/* Optional, only to check that it works with margin/padding */
margin: 30px;
padding: 20px;
border: 10px solid black;
}
/* Optional eye candy below: */
li {
padding: 10px;
list-style-type: none;
}
li:hover {
background: #ccc;
}
<ul id='infinite-list'>
</ul>

The following function returns, whether the user has scrolled to the bottom of a certain element:
function scrollEnd(el) {
return (el.scrollTop + el.offsetHeight >= el.scrollHeight);
}
If you add this to a scroll event listener:
element.addEventListener('scroll', () => {
if (scrollEnd(element)) {
// the user reached the end
}
})
I tried this on a textarea, should work with anything, though.

Related

Collapse an accordion menu

I have this simple collapsible menu on www.keokuk.com
I would like for the previous menu to close when you click on the next one.
this is the javascript:
<script>
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function () {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight) {
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
</script>
I worked on a solution on your website.
But it appears you set max-height manually in an other javascript function so you can just do the same thing in the commented line.
document.querySelectorAll('.collapsible').forEach(el => {
el.addEventListener('click', (e) => {
document.querySelectorAll('.collapsible').forEach(e => {
e.classList.remove('active');
e.nextSibling.nextElementSibling.style.maxHeight = "0px";
});
e.target.classList.toggle('active');
e.target.nextSibling.nextElementSibling.style.maxHeight =
`${el.nextSibling.nextElementSibling.scrollHeight}px`;
});
});
Details are commented in example
// Collect all .switch into an array
const switches = [...document.querySelectorAll(".switch")];
// Bind each .switch to the click event
switches.forEach(s => s.addEventListener("click", openClose));
// Event handler passes Event Object as default
function openClose(event) {
// Reference the tag proceeding clicked tag
const content = this.nextElementSibling;
// Get the height of content
let maxHt = content.scrollHeight + 'px';
// Find the index position of clicked tag
let index = switches.indexOf(this);
// The clicked tag will toggle .active class
this.classList.toggle('active');
// Remove .active class from all .switch
switches.forEach((btn, idx) => {
/*
If current index does NOT equal index of
clicked tag...
...remove .active
*/
if (idx != index) {
btn.classList.remove('active');
}
});
/*
If clicked has .active class...
...set style property of max-height using CSS variables
*/
if (this.classList.contains('active')) {
content.style.setProperty('--maxH', maxHt + 'px');
} else {
content.style.setProperty('--maxH', '0px');
}
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box
}
:root {
font: 300 1.5ch/1.2 'Segoe UI';
--maxH: 0px;
}
body {
width: 100%;
min-height: 200%;
padding: 15px;
}
header {
width: max-content;
margin: 10px 0 0;
padding: 5px 10px;
border: 3px ridge black;
border-radius: 4px;
background: #aaa;
cursor: pointer;
}
section {
position: relative;
max-height: var(--maxH);
margin: 0;
padding: 5px 10px;
border: 3px ridge black;
border-radius: 4px;
background: #ddd;
opacity: 0;
pointer-events: none;
}
.active+section {
z-index: 1;
opacity: 1.0;
}
<header class='switch'>Read more...</header>
<section>
<p>Merchandise Morty, your only purpose in life is to buy & consume merchandise and you did it, you went into a store an actual honest to god store and you bought something, you didn't ask questions or raise ethical complaints you just looked into
the bleeding jaws of capitalism and said 'yes daddy please' and I'm so proud of you, I only wish you could have bought more, I love buying things so much Morty. Morty, you know outer space is up right? Are you kidding? I'm hoping I can get to both
of them, Rick! And there's no evidence that a Latino student did it.</p>
</section>
<header class='switch'>Read more...</header>
<section>
<p>Oh, I'm sorry Morty, are you the scientist or are you the kid who wanted to get laid? Why don't you ask the smartest people in the universe, Jerry? Oh yeah you can't. They blew up. Looossseeerrrrr. I am not putting my father in a home! He just came
back into my life, and you want to, grab him and, stuff him under a mattress like last month's Victoria's Secret?!
</p>
</section>

How to change the text color of a transparent header on scroll, depending on the div it overlaps

I have a header with a transparent backgrounsd, and I am trying to get the text of the header to change colour between white and black depending on the background of the div it's overlapping.
So far I have managed to add a class of .color-menu to all the divs where I want the header to be black.
I then have it add a class of .dark-menu to the header when the .color-menu div reaches the top of the page.
The problem is that it only works for the first .colour-menu div. It will change to black when it is in the viewport and back to white for the next div but then when the next .color-menu div gets to the top it doesn't change.
So, it seems like the .each function isn't working but I am not sure how to fix it.
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(".color-menu").offset().top;
var bottom_of_element = $(".color-menu").offset().top + $(".color-menu").outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
UPDATE: I have also tried using $(this) but it really throws off when it changes color.
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(this).offset().top;
var bottom_of_element = $(this).offset().top + $(this).outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
Here is a simplified version of my code as an example:
$(document).ready(function() {
$(".white").addClass("color-menu");
$(".white-bold").addClass("color-menu");
$(".light").addClass("color-menu");
$(".light-bold").addClass("color-menu");
$(".bright").addClass("color-menu");
});
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(".color-menu").offset().top;
var bottom_of_element = $(".color-menu").offset().top + $(".color-menu").outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
.header {
width: 100%;
background: rgba(0,0,0,0);
margin: 0;
padding:10px;
position: fixed;
text-align: center;
}
.header a {
color: white;
font-size: 2rem;
text-transform: uppercase;
}
.dark-menu a{
color: black;
}
.black {
background-color: black;
height: 200px;
}
.white, .white-bold, .light, .light-bold, .bright {
background-color: white;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header">
<a>This is the header</a>
</div>
<div class ="black"></div>
<div class ="white"></div>
<div class ="black"></div>
<div class ="white-bold"></div>
<div class ="black"></div>
<div class ="light"></div>
<div class ="black"></div>
<div class ="light-bold"></div>
<div class ="black"></div>
<div class ="bright"></div>
What is happening in your code is that on scroll, you loop through every color-menu div and add the class if it is the current one... but then the code continues to loop though the remaining elements in the array and removes it again because the page is not in the other div.
I've explained step-by-step the changes you need to get this to work after the example, but first you can see it working here:
Working Example:
$(document).ready(function() {
$(".white").addClass("color-menu");
$(".white-bold").addClass("color-menu");
$(".light").addClass("color-menu");
$(".light-bold").addClass("color-menu");
$(".bright").addClass("color-menu");
$(window).scroll(function() {
var inColorMenu = false; /* initialise var to store if we are in color-menu */
var top_of_screen = $(window).scrollTop(); /* just get this once outside loop */
/* Loop through each color-menu element and check if we are in one */
$('.color-menu').each(function(i) {
var top_of_element = $(this).offset().top;
var bottom_of_element = top_of_element + $(this).outerHeight();
/* if we are in a color-menu element, set our var to true and stop processing */
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
inColorMenu = true;
return false; /* N.B. need to return "false" to break from the "each" loop */
}
});
if (inColorMenu) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
.header {
width: 100%;
background: rgba(0, 0, 0, 0);
margin: 0;
padding: 10px;
position: fixed;
text-align: center;
}
.header a {
color: white;
font-size: 2rem;
text-transform: uppercase;
}
.header.dark-menu a {
color: black;
}
.black {
background-color: black;
height: 200px;
}
.white,
.white-bold,
.light,
.light-bold,
.bright {
background-color: white;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header">
<a>This is the header</a>
</div>
<div class="black"></div>
<div class="white"></div>
<div class="black"></div>
<div class="white-bold"></div>
<div class="black"></div>
<div class="light"></div>
<div class="black"></div>
<div class="light-bold"></div>
<div class="black"></div>
<div class="bright"></div>
How this works:
Declare a variable to record whether we are in a "color-menu" class or not, and initialise this to false, e.g.:
var inColorMenu = false;
When looping through $('.color-menu').each, if we are between the top and bottom of one of divs (which your code is already detecting), then set our variable to true to record this.
We can also return false to break the each loop and stop processing the rest of the elements (it will still work without this, we are just reducing the amount of processing required):
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
inColorMenu = true;
return false; /* N.B. need to return "false" to break from the "each" loop */
}
Finally, after we finish our $('.color-menu').each loop, if inColorMenu is true, we know we are in a color-menu div so we add the dark-menu class to the header, otherwise we remove it:
if (inColorMenu) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
Note: You need to use $(this) when getting the offset().top and outerHeight() so that you are getting the values for the current element in the loop. $(".color-menu") gets the values for an unspecified element with this class so will not work.

Making a dragbar to resize divs inside CSS grids

I have 2 boxes and a vertical div line in one unique container div (code and fiddle below).
I'm using CSS grids to position my elements inside the container
What I'd like to accomplish is to use the vertical line to resize horizontally the two boxes based on the position of the vertical line.
I apologize if the question is noobish, I am new to web development, only used Python before, already tried to google and stackoverflow search but all solutions seem overly complicated and generally require additional libraries, I was looking for something simpler and JS only.
HTML:
<div class="wrapper">
<div class="box a">A</div>
<div class="handler"></div>
<div class="box b">B</div>
</div>
CSS:
body {
margin: 40px;
}
.wrapper {
display: grid;
grid-template-columns: 200px 8px 200px;
grid-gap: 10px;
background-color: #fff;
color: #444;
}
.box {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
resize: both;
}
.handler{
width: 3px;
height: 100%;
padding: 0px 0;
top: 0;
background: red;
draggable: true;
}
https://jsfiddle.net/gv8Lwckh/6/
What you intend to do can be done using CSS flexbox—there is no need to use CSS grid. The bad news is that HTML + CSS is not so smart that declaring resize and draggable will make the layout flexible and adjustable by user interaction. For that, you will have to use JS. The good news is that this is actually not too complicated.
Here is a quick screen grab of output the code below:
However, for you to understand the code I will post below, you will have to familiarize yourself with:
Event binding using .addEventListener. In this case, we will use a combination of mousedown, mouseup and mousemove to determine whether the user is in the middle of dragging the element
CSS flexbox layout
Description of the solution
Initial layout using CSS
Firstly, you will want to layout your boxes using CSS flexbox. We simply declare display: flex on the parent, and then use flex: 1 1 auto (which translates to "let the element grow, let the element shrink, and have equal widths). This layout is only valid at the initial rendering of the page:
.wrapper {
/* Use flexbox */
display: flex;
}
.box {
/* Use box-sizing so that element's outerwidth will match width property */
box-sizing: border-box;
/* Allow box to grow and shrink, and ensure they are all equally sized */
flex: 1 1 auto;
}
Listen to drag interaction
You want to listen to mouse events that might have originated from your .handler element, and you want a global flag that remembers whether the user is dragging or not:
var handler = document.querySelector('.handler');
var isHandlerDragging = false;
Then you can use the following logic to check if the user is dragging or not:
document.addEventListener('mousedown', function(e) {
// If mousedown event is fired from .handler, toggle flag to true
if (e.target === handler) {
isHandlerDragging = true;
}
});
document.addEventListener('mousemove', function(e) {
// Don't do anything if dragging flag is false
if (!isHandlerDragging) {
return false;
}
// Set boxA width properly
// [...more logic here...]
});
document.addEventListener('mouseup', function(e) {
// Turn off dragging flag when user mouse is up
isHandlerDragging = false;
});
Computing the width of box A
All you are left with now is to compute the width of box A (to be inserted in the [...more logic here...] placeholder in the code above), so that it matches that of the movement of the mouse. Flexbox will ensure that box B will fill up the remaining space:
// Get offset
var containerOffsetLeft = wrapper.offsetLeft;
// Get x-coordinate of pointer relative to container
var pointerRelativeXpos = e.clientX - containerOffsetLeft;
// Resize box A
// * 8px is the left/right spacing between .handler and its inner pseudo-element
// * Set flex-grow to 0 to prevent it from growing
boxA.style.width = (pointerRelativeXpos - 8) + 'px';
boxA.style.flexGrow = 0;
Working example
var handler = document.querySelector('.handler');
var wrapper = handler.closest('.wrapper');
var boxA = wrapper.querySelector('.box');
var isHandlerDragging = false;
document.addEventListener('mousedown', function(e) {
// If mousedown event is fired from .handler, toggle flag to true
if (e.target === handler) {
isHandlerDragging = true;
}
});
document.addEventListener('mousemove', function(e) {
// Don't do anything if dragging flag is false
if (!isHandlerDragging) {
return false;
}
// Get offset
var containerOffsetLeft = wrapper.offsetLeft;
// Get x-coordinate of pointer relative to container
var pointerRelativeXpos = e.clientX - containerOffsetLeft;
// Arbitrary minimum width set on box A, otherwise its inner content will collapse to width of 0
var boxAminWidth = 60;
// Resize box A
// * 8px is the left/right spacing between .handler and its inner pseudo-element
// * Set flex-grow to 0 to prevent it from growing
boxA.style.width = (Math.max(boxAminWidth, pointerRelativeXpos - 8)) + 'px';
boxA.style.flexGrow = 0;
});
document.addEventListener('mouseup', function(e) {
// Turn off dragging flag when user mouse is up
isHandlerDragging = false;
});
body {
margin: 40px;
}
.wrapper {
background-color: #fff;
color: #444;
/* Use flexbox */
display: flex;
}
.box {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
/* Use box-sizing so that element's outerwidth will match width property */
box-sizing: border-box;
/* Allow box to grow and shrink, and ensure they are all equally sized */
flex: 1 1 auto;
}
.handler {
width: 20px;
padding: 0;
cursor: ew-resize;
flex: 0 0 auto;
}
.handler::before {
content: '';
display: block;
width: 4px;
height: 100%;
background: red;
margin: 0 auto;
}
<div class="wrapper">
<div class="box">A</div>
<div class="handler"></div>
<div class="box">B</div>
</div>
Here's an example of the drag event handling, but using CSS Grids
The trick is to set the grid-template-columns (or rows) on the grid container rather than than the size of the grid items
let isLeftDragging = false;
let isRightDragging = false;
function ResetColumnSizes() {
// when page resizes return to default col sizes
let page = document.getElementById("pageFrame");
page.style.gridTemplateColumns = "2fr 6px 6fr 6px 2fr";
}
function SetCursor(cursor) {
let page = document.getElementById("page");
page.style.cursor = cursor;
}
function StartLeftDrag() {
// console.log("mouse down");
isLeftDragging = true;
SetCursor("ew-resize");
}
function StartRightDrag() {
// console.log("mouse down");
isRightDragging = true;
SetCursor("ew-resize");
}
function EndDrag() {
// console.log("mouse up");
isLeftDragging = false;
isRightDragging = false;
SetCursor("auto");
}
function OnDrag(event) {
if (isLeftDragging || isRightDragging) {
// console.log("Dragging");
//console.log(event);
let page = document.getElementById("page");
let leftcol = document.getElementById("leftcol");
let rightcol = document.getElementById("rightcol");
let leftColWidth = isLeftDragging ? event.clientX : leftcol.clientWidth;
let rightColWidth = isRightDragging ? page.clientWidth - event.clientX : rightcol.clientWidth;
let dragbarWidth = 6;
let cols = [
leftColWidth,
dragbarWidth,
page.clientWidth - (2 * dragbarWidth) - leftColWidth - rightColWidth,
dragbarWidth,
rightColWidth
];
let newColDefn = cols.map(c => c.toString() + "px").join(" ");
// console.log(newColDefn);
page.style.gridTemplateColumns = newColDefn;
event.preventDefault()
}
}
#page {
height: 100%;
background-color: pink;
display: grid;
grid-template-areas: 'header header header header header' 'leftcol leftdragbar tabs tabs tabs' 'leftcol leftdragbar tabpages rightdragbar rightcol' 'leftcol leftdragbar footer footer footer';
grid-template-rows: min-content 1fr 9fr 1fr;
grid-template-columns: 2fr 6px 6fr 6px 2fr;
}
/*****************************/
#header {
background-color: lightblue;
overflow: auto;
grid-area: header;
}
#leftcol {
background-color: #aaaaaa;
overflow: auto;
grid-area: leftcol;
}
#leftdragbar {
background-color: black;
grid-area: leftdragbar;
cursor: ew-resize;
}
#tabs {
background-color: #cccccc;
overflow: auto;
grid-area: tabs;
}
#tabpages {
background-color: #888888;
overflow: auto;
grid-area: tabpages;
}
#rightdragbar {
background-color: black;
grid-area: rightdragbar;
cursor: ew-resize;
}
#rightcol {
background-color: #aaaaaa;
overflow: auto;
grid-area: rightcol;
}
#footer {
background-color: lightblue;
overflow: auto;
grid-area: footer;
}
<body onresize="ResetColumnSizes()">
<div id="page" onmouseup="EndDrag()" onmousemove="OnDrag(event)">
<div id="header">
Header
</div>
<div id="leftcol">
Left Col
</div>
<div id="leftdragbar" onmousedown="StartLeftDrag()"></div>
<div id="tabs">
Tabs
</div>
<div id="tabpages">
Tab Pages
</div>
<div id="rightdragbar" onmousedown="StartRightDrag()"></div>
<div id="rightcol">
Rightcol
</div>
<div id="footer">
Footer
</div>
</div>
</body>
https://codepen.io/lukerazor/pen/GVBMZK
I changed, so you can add more Horizontal and Vertical slider.
test1.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="test1.css">
<script src= "test1.js" > </script>
</head>
<body>
<div id="page" onmouseup="EndDrag()" onmousemove="OnDrag(event)">
<div id="header">
Header asdlkj flkdfj sdflkksdjf sd;flsdjf sd;flkjsd;fljsd;flsdj;fjsd f;sdlfj;sdlfj
</div>
<div id="leftcol">
Left Col
</div>
<div id="leftdragbar" onmousedown="StartHDrag(1)"></div>
<div id="tabs">
Tabs
</div>
<div id="topdragbar" onmousedown="StartVDrag(2)"></div>
<div id="tabpages">
Tab Pages
</div>
<div id="rightdragbar" onmousedown="StartHDrag(3)"></div>
<div id="rightcol">
Rightcol
</div>
<div id="botdragbar" onmousedown="StartVDrag(4)"></div>
<div id="footer">
Footer
</div>
</div>
<div id= 'status'></div>
</body>
</html>
test1.css
body {
}
#page {
height: 100vh;
background-color: pink;
display: grid;
grid-template-areas:
'header header header header header'
'leftcol leftdragbar tabs tabs tabs'
'leftcol leftdragbar topdragbar topdragbar topdragbar'
'leftcol leftdragbar tabpages rightdragbar rightcol'
'botdragbar botdragbar botdragbar botdragbar botdragbar'
'footer footer footer footer footer';
grid-template-rows: min-content 1fr 6px 9fr 6px 1fr;
grid-template-columns: 2fr 6px 6fr 6px 2fr;
}
/*****************************/
#header {
background-color: lightblue;
overflow: auto;
grid-area: header;
}
#leftcol {
background-color: #aaaaaa;
overflow: auto;
grid-area: leftcol;
}
#leftdragbar {
background-color: black;
grid-area: leftdragbar;
cursor: ew-resize;
}
#topdragbar {
background-color: black;
grid-area: topdragbar;
cursor: ns-resize;
}
#botdragbar {
background-color: black;
grid-area: botdragbar;
cursor: ns-resize;
}
#tabs {
background-color: #cccccc;
overflow: auto;
grid-area: tabs;
}
#tabpages {
background-color: #888888;
overflow: auto;
grid-area: tabpages;
}
#rightdragbar {
background-color: black;
grid-area: rightdragbar;
cursor: ew-resize;
}
#rightcol {
background-color: #aaaaaa;
overflow: auto;
grid-area: rightcol;
}
#footer {
background-color: lightblue;
overflow: auto;
grid-area: footer;
}
test1.js
let isHDragging = false;
let isVDragging = false;
let cols = ['2fr','6px','6fr','6px','2fr']; //grid-template-columns: 2fr 6px 6fr 6px 2fr;
let colns = ['leftcol','','tabpages','','rightcol'];
let Tcols = [];
let rows = ['min-content','1fr','6px','9fr','6px','1fr']; //grid-template-rows: min-content 1fr 6px 9fr 1fr
let rowns = ['header','tabs','','tabpages','','footer'];
let Trows = []
let CLfactor ;
let CRfactor ;
let gWcol = -1;
let gWrow = -1;
function StartHDrag(pWcol) {
isHDragging = true;
SetCursor("ew-resize");
CLfactor = parseFloat(cols[pWcol-1]) / document.getElementById(colns[pWcol-1]).clientWidth;
CRfactor = parseFloat(cols[pWcol+1]) / document.getElementById(colns[pWcol+1]).clientWidth;
Tcols = cols.map(parseFloat);
gWcol = pWcol;
}
function StartVDrag(pRow) {
isVDragging = true;
SetCursor("ns-resize");
CLfactor = parseFloat(rows[pRow-1]) / document.getElementById(rowns[pRow-1]).clientHeight;
CRfactor = parseFloat(rows[pRow+1]) / document.getElementById(rowns[pRow+1]).clientHeight;
Trows = rows.map(parseFloat);
gWrow = pRow;
}
function SetCursor(cursor) {
let page = document.getElementById("page");
page.style.cursor = cursor;
}
function EndDrag() {
isHDragging = false;
isVDragging = false;
SetCursor("auto");
}
function OnDrag(event) {
if(isHDragging) {
Tcols[gWcol-1] += (CLfactor * event.movementX);
Tcols[gWcol+1] -= (CLfactor * event.movementX);
cols[gWcol-1] = Math.max(Tcols[gWcol-1],0.01) + "fr";
cols[gWcol+1] = Math.max(Tcols[gWcol+1],0.01) + "fr";
let newColDefn = cols.join(" ");
page.style.gridTemplateColumns = newColDefn;
} else if (isVDragging) {
Trows[gWrow-1] += (CLfactor * event.movementY);
Trows[gWrow+1] -= (CLfactor * event.movementY);
rows[gWrow-1] = Math.max(Trows[gWrow-1],0.01) + "fr";
rows[gWrow+1] = Math.max(Trows[gWrow+1],0.01) + "fr";
let newRowDefn = rows.join(" ");
page.style.gridTemplateRows = newRowDefn;
document.getElementById("footer").innerHTML = newRowDefn;
}
event.preventDefault()
}
To actually match the question! Making a dragbar to resize divs inside CSS grids.
Here is a possible way, the original OP layout is kept, as well as the CSS, using Grids.
The goal is to capture the original state of the Grid Template Columns, and convert it to floats.
The browser always compute in pixels, and the sum of those columns + the gap, represent the total width of the container element. That sum must always be the same, or the elements will jump!
NB: Calls to .getComputedStyle() are not very efficient, optimisation is likely possible here!
Notice, doing this way using grids and screenX avoid the common jumping bug on mouse down.
Comments are added, this will allow to apply the logic with any number of columns, or rows, good luck.
With the usage of pointer events, it does works from a touch device as well.
let target = document.querySelector("div") // Target container element
let md = false; // Will be true at mouse down
let xorigin; // Click origin X position
let gtcorigin = []; // Origin Grid Template Columns in pixels
const pointerdown = (e) => {
if (e.target.classList[0] === "handler"){ // Filter to target the wanted element
md = true; // Set mouse down
xorigin = e.screenX; // Store the origin X position
// Grid Template Columns, array of pixels as float
gtcorigin = window.getComputedStyle(target)["grid-template-columns"].split(" ").map((a) => +(a.slice(0, -2)));
document.body.style.cursor = "col-resize" // This makes things nice
document.body.style.userSelect = "none" // This makes things nice
}
}
const pointerup = (e) => {
md = false; // Reset bool at mouse up
document.body.style.cursor = "pointer"
document.body.style.userSelect = "unset"
}
const resizer = (e) => {
if (md){ // Mouse is down hover the handler element
let gtc = window.getComputedStyle(target)["grid-template-columns"].split(" ").map((a) => +(a.slice(0, -2))); // Grid Template Columns, array of pixels as float
let xdragdif = xorigin - e.screenX; // Move in pixels since the click
gtc[0] = gtcorigin[0] - xdragdif // First column, if negative, it will grow
gtc[2] = gtcorigin[2] + xdragdif // Third column
gtc = gtc.map((a) => a+"px") // Set back the values in string with "px"
document.querySelector("console").textContent = gtc.join(" ") // !!! This is only for the demo
target.style.gridTemplateColumns = gtc.join(" ") // Apply the new Grid Template Column as inline style.
}
}
// Attach all events on the largest container element. Here the body is used.
document.body.addEventListener("pointerdown", pointerdown, false)
document.body.addEventListener("pointerup", pointerup, false)
document.body.addEventListener("pointermove", resizer, false)
body {
margin: 40px;
overflow-x: hidden
}
.wrapper {
display: grid;
grid-template-columns: 200px 8px 200px;
grid-gap: 10px;
background-color: #fff;
color: #444;
}
.box {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
}
.handler{
width: 3px;
height: 100%;
padding: 0px 0;
top: 0;
background: red;
cursor: col-resize
}
<div class="wrapper">
<div class="box">A</div>
<div class="handler"></div>
<div class="box">B</div>
</div>
<console></console>
No limits are applied here, this can be enhanced with CSS only, using min-width and other similar rules, and the float values can be retrieved to create range sliders and more, this way.

Stick div at top not working properly : javascript

I going to create a scroll and stick div which has to stick on the top of the page but while scrolling down the div next to stickdiv automatically stick to the div before to sticky div
var left = document.getElementsByClassName("stickdiv");
for (var i = 0; i < left.length; i++) {
var stop = (left[0].offsetTop);
window.onscroll = function(e) {
var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
// left.offsetTop;
if (scrollTop >= stop) {
// get array item by index
left[0].classList.add('stick'); //adding a class name
} else {
// get array item by index
left[0].classList.remove('stick');
}
}
}
.stickdiv {
height: 50vh!important;
width: 100vh!important;
background-color: green!important;
}
.stick {
position: fixed;
top: 0;
margin: 0 0
}
#right {
float: right;
width: 100px;
height: 1000px;
background: red;
}
.des {
height: 300px;
width: 100%;
background-color: #000;
}
<div class="des"></div>
<div class="stickdiv"></div>
<div id="right"></div>
Example : green color div is the sticky div but after scrollingdown , red is also going to stick , I've tried position absolute in css but not working how to fix it
Here is the code to make green sticky when scrolling.
$ = document.querySelectorAll.bind(document);
// how far is the green div from the top of the page?
var initStickyTop = $(".stickdiv")[0].getBoundingClientRect().top + pageYOffset;
// clone the green div
var clone = $(".stickdiv")[0].cloneNode(true);
// hide it first
clone.style.display = "none";
// add it to dom
document.body.appendChild(clone);
addEventListener("scroll",stick=function() {
// if user scroll past the sticky div
if (initStickyTop < pageYOffset) {
// hide the green div but the div still take up the same space as before so scroll position is not changed
$(".stickdiv")[0].style.opacity = "0";
// make the clone sticky
clone.classList.add('stick');
// show the clone
clone.style.opacity="1";
clone.style.display = "block";
} else {
// make the clone not sticky anymore
clone.classList.remove("stick");
// hide it
clone.style.display = "none";
// show the green div
$(".stickdiv")[0].style.opacity="1";
};
});
// when resize, recalculate the position of the green div
addEventListener("resize", function() {
initStickyTop = $(".stickdiv")[0].getBoundingClientRect().top + pageYOffset;
stick();
});
.stickdiv {
height: 50vh!important;
width: 100vh!important;
background-color: green!important;
}
.stick {
position: fixed;
top: 0;
margin: 0 0
}
#right {
float: right;
width: 100px;
height: 1000px;
background: red;
}
.des {
height: 300px;
width: 100%;
background-color: #000;
}
<div class="des"></div>
<div class="stickdiv"></div>
<div id="right"></div>
JS FIDDLE
you might want to remove the stickdiv class and add it accordingly
if (scrollTop >= stop) {
// get array item by index
left[0].classList.add('stick'); //adding a class name
left[0].classList.remove('stickdiv');
} else {
// get array item by index
left[0].classList.remove('stick');
left[0].classList.add('stickdiv');
}

How to move a carousel item to the middle when it's clicked in jquery

How can I make the carousel center the item I've clicked to the middle? I've looked everywhere for an answer but they're not straight answers... Can someone help me in this, please?
This is what I've done so far: http://jsfiddle.net/sp9Jv/
HTML:
<div id="wrapper">
<div id="carousel">
prev
next
<div class="viewport">
<ul>
<li>Un</li>
<li>Deux</li>
<li>Trois</li>
<li>Quatre</li>
<li>Cinq</li>
<li>Six</li>
<li>Sept</li>
<li>Huit</li>
</ul>
</div>
<!-- viewport -->
</div>
<!-- carousel -->
</div>
<!-- wrapper -->
JavaScript:
var carousel = $('#carousel'),
prev = carousel.find('.prev'),
next = carousel.find('.next'),
viewport = carousel.find('.viewport'),
item = viewport.find('li'),
itemWidth = item.outerWidth(true),
itemNum = item.length,
itemList = viewport.find('ul');
itemList.width(itemWidth * itemNum);
var moveCarousel = function(dir) {
itemList.animate({ left: '-=' + (itemWidth * dir) + 'px' }, 400);
};
//prev
prev.on('click', function(e) {
e.preventDefault();
moveCarousel(-1);
});
//next
next.on('click', function(e) {
e.preventDefault();
moveCarousel(1);
});
//carousel item
item.on('click', 'a', function(e) {
var self = $(this),
selfIndex = self.index(),
distance = itemList.width() / 2,
selfPos = self.position(),
selfPosLeft = selfPos.left,
viewportPosLeft = viewport.position().left;
e.preventDefault();
//move item to middle, but it doesn't work...
if (selfPosLeft > Math.floor(viewport.width())/3) {
itemList.animate({ left: '-' + Math.floor(viewport.width())/3 + 'px' }, 400);
}
if (selfPosLeft < Math.floor(viewport.width())/3) {
itemList.animate({ left: Math.floor(viewport.width())/3 + 'px' }, 400);
}
});
CSS:
#wrapper {
width: 500px;
margin: 20px auto;
}
#carousel {
position: relative;
}
.viewport {
width: 260px;
border: 1px solid #6e6e6e;
height: 80px;
overflow: hidden;
position: relative;
margin-left: 100px;
}
.prev, .next {
position: absolute;
}
.prev {
top: 20px;
left: 0;
}
.next {
top: 20px;
right: 0;
}
.viewport ul {
position: absolute;
}
.viewport li {
float: left;
margin-right: 10px;
}
.viewport li a {
display: block;
width: 80px;
height: 80px;
background: #ddd;
}
While you have prepared all the information needed about all items, you can calculate the value of the left based on the clicked item.
Here is my modification:
and I've bound the click action of carousel items with this function and passed the clicked item using the self keyword.
var itemClicked=function(item){
var itemIndex=$(item).index(),
newLeft=(itemIndex*itemWidth*-1)+Math.floor(($(viewport).width()/itemWidth)/2)*itemWidth;
$(itemList).animate({left:newLeft+"px"},400);
};
You can check it working on this url: http://jsfiddle.net/rUZHg/3/
I assume that this should work despite of the number of viewed elements while it calculates the padding between the left 0 and the left of the center element.
Alright, it's ugly, I hope it gives you some ideas.
I created a global currentItem that tracks what's in the center. Every time the carousel moves this is updated.
The very useful variable I found was selfPosLeft which told me what was being clicked. I should add that 90 was the multiple I got from clicking around. Must be linked to your CSS and I don't know how to find this number dynamically.
Please try it :) http://jsfiddle.net/sp9Jv/4/
Well, I'm picturing that when you have more than 3 items you can change the code to compute the difference between the current item and the selfPosLeft of the clicked one, I'll leave that to you :) Like this, seems to work. http://jsfiddle.net/sp9Jv/5/

Categories