Is there any way to fit a slide Nav in splitter? - javascript

I am trying to fit a slide nav on the right side of the splitter, which is able to split when dragging the separator based on the width of the right side. https://jsfiddle.net/74bewsdu/
I tried modifying the width to auto as well as the z-index, it still doesn't work well.
index
<div class="splitter">
<div id="first">
<iframe src="{{ route('child') }}" style="width:100%; height:100%" frameBorder="0">
Your browser isn't compatible
</iframe>
</div>
<div id="separator"></div>
<div id="second">
<div id="mySidenav" class="sidenav"></div>
<div style="font-size:30px;cursor:pointer" onclick="openNav()">☰ open</div>
</div>
</div>
</div>
javascript
function openNav() {
document.getElementById("mySidenav").style.width = "250px";
}
css
.sidenav {
height: 100%;
width: 0;
position: fixed;
z-index: 1;
top: 0;
right: 0;
background-color: #111;
overflow-x: hidden;
transition: 0.5s;
padding-top: 60px;
}

You could get the offsetWidth of the right side div (#second) plus the offsetWidth of the separator, and set the sliding nav width to that value. If you don't want to completely overlap the menu button, you could subtract it by the width of the menu button (or some arbitrary constant).
Example (see comments in lines between /* added */ and /* end added */ areas in the snippet below). Update: I also added code to check to see if the slider nav is open. Now, if you drag the slider when the side nav is open, it should be dynamically resized.
let navIsOpen = false; // variable to keep track of if the slider nav is open
function openNav() {
/* added */
// calculate width of right side div + separator
let width = second.offsetWidth + separator.offsetWidth;
// uncomment below line to subtract a value so the slider doesn't obscure the menu button/text
//width = width - 95;
document.getElementById("mySidenav").style.width = width + "px";
navIsOpen = true; // slider nav is open
/* end added */
}
// 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;
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
};
first.style.pointerEvents = 'none';
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";
}
/* added */
if (navIsOpen) { // check if slider nav is open, if so resize
openNav();
}
/* end added */
}
}
dragElement(document.getElementById("separator"), "H");
.splitter {
width: 100%;
height: 300px;
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 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;
}
.sidenav {
height: 100%;
width: 0;
position: fixed;
z-index: 1;
top: 0;
right: 0;
background-color: #111;
overflow-x: hidden;
transition: 0.5s;
padding-top: 60px;
}
<div class="splitter">
<div id="first">
<iframe src="{{ route('child') }}" style="width:100%; height:100%" frameBorder="0">
Your browser isn't compatible
</iframe>
</div>
<div id="separator"></div>
<div id="second">
<div id="mySidenav" class="sidenav"></div>
<div style="font-size:30px;cursor:pointer" onclick="openNav()">☰ open</div>
</div>
</div>

Related

How can I split the resizable panel vertically using JavaScript

How can I split the resizable panel vertically using current code HTML,CSS and JavaScript. Right now this code is working for horizontal resizable split. What are the changes I would have to do in this current code.
How can I split the resizable panel vertically using current code HTML,CSS and JavaScript. Right now this code is working for horizontal resizable split. What are the changes I would have to do in this current code.
// 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");
.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;
}
<body>
<div class="splitter">
<div id="first"></div>
<div id="separator"></div>
<div id="second"></div>
</div>
</body>
I would do it like this. You will need another SVG image to design properly the separator because the previous one was created for horizontal purpose.
// 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,
firstHeight: first.offsetHeight,
secondHeight: second.offsetHeight
};
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 === "V") // Vertical
{
// Prevent negative-sized elements
delta.x = Math.min(Math.max(delta.y, -md.firstHeight),
md.secondHeight);
element.style.top = md.offsetTop + delta.x + "px";
first.style.height = (md.firstHeight + delta.x) + "px";
second.style.height = (md.secondHeight - delta.x) + "px";
}
}
}
dragElement(document.getElementById("separator"), "V");
.splitter {
width: 100%;
height: 100px;
}
#separator {
cursor: col-resize;
background-color: #aaa;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='3'><path d='M2 30 0 M5 0 v30 M8 0 v30' fill='none' stroke='black'/></svg>");
background-repeat: no-repeat;
background-position: center;
width: 100%;
height: 5px;
/* 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: 100%;
height: 100%;
min-width: 10px;
}
#second {
background-color: #eee;
width: 100%;
height: 100%;
min-width: 10px;
}
<body>
<div class="splitter">
<div id="first"></div>
<div id="separator"></div>
<div id="second"></div>
</div>
</body>

Change width proportions of two blocks with a slider

I'm trying to design a component in which you could change the width proportions of two blocks by moving a slider left and right:
codpen and demo:
.outer {
display: flex;
flex-direction: row;
}
.block {
height: 200px;
width: -webkit-calc(50% - 5px);
width: -moz-calc(50% - 5px);
width: calc(50% - 5px);
}
.block-1 {
background-color: red;
}
.block-2 {
background-color: green;
}
.slider {
line-height: 100%;
width: 10px;
background-color: #dee2e6;
border: none;
cursor: e-resize;
}
<div id="app">
<div class="outer">
<div class="block block-1">
Block 1
</div>
<div class="slider">
S<br>l<br>i<br>d<br>e<br>r
</div>
<div class="block block-2">
Block 2
</div>
</div>
</div>
I have tried using draggable-vue-directive and change the width of blocks based on slider position.
However, it didn't work really well, since draggable-vue-directive set the slider to position:fixed which in turn messed up the block alignment.
How can I make the .slider block horizontally draggable without setting position:fixed?
How to correctly resize Block1 and Block2 when slider moves?
Note: I'm not using jQuery
You can adjust your flexbox along with resize - the downside is that the slider its not very customizeable:
add resize: horizontal to one of the flex items
add flex: 1 to the other flex item (so that this flex item will adjust automatically in response to the changing width of the other flex item as it is resized)
See demo below:
.outer {
display: flex;
flex-direction: row;
}
.block {
height: 100px;
width: 50%; /* 50% would suffice*/
}
.block-1 {
background-color: red;
resize: horizontal; /* resize horizontal */
overflow: hidden; /* resize works for overflow other than visible */
}
.block-2 {
background-color: green;
flex: 1; /* adjust automatically */
}
<div id="app">
<div class="outer">
<div class="block block-1">
Block 1
</div>
<div class="block block-2">
Block 2
</div>
</div>
</div>
So we'll use vanilla JS instead of the resize solution above:
use a mousedown listener that registers a mousemove listener that updates the block-1 width (and reset the mouseup event)
also consider min-width: 0 to override min-width: auto of the block-2 element
See demo below:
let block = document.querySelector(".block-1"),
slider = document.querySelector(".slider");
slider.onmousedown = function dragMouseDown(e) {
let dragX = e.clientX;
document.onmousemove = function onMouseMove(e) {
block.style.width = block.offsetWidth + e.clientX - dragX + "px";
dragX = e.clientX;
}
// remove mouse-move listener on mouse-up
document.onmouseup = () => document.onmousemove = document.onmouseup = null;
}
.outer {
display: flex;
flex-direction: row;
}
.block {
height: 100px;
width: 50%; /* 50% would suffice*/
}
.block-1 {
background-color: red;
}
.block-2 {
background-color: green;
flex: 1; /* adjust automatically */
min-width: 0; /* allow flexing beyond auto width */
overflow: hidden; /* hide overflow on small width */
}
.slider {
line-height: 100%;
width: 10px;
background-color: #dee2e6;
border: none;
cursor: col-resize;
user-select: none; /* disable selection */
text-align: center;
}
<div id="app">
<div class="outer">
<div class="block block-1">
Block 1
</div>
<div class="slider">
S<br>l<br>i<br>d<br>e<br>r
</div>
<div class="block block-2">
Block 2
</div>
</div>
</div>
Solution
You can adapt the above into Vue easily without using any custom Vue plugins for this - the changes are:
#mousedown listener on slider that triggers the slider
use of refs to update the width of block-1
See demo below:
new Vue({
el: '#app',
data: {
block1W: '50%'
},
methods: {
drag: function(e) {
let dragX = e.clientX;
let block = this.$refs.block1;
document.onmousemove = function onMouseMove(e) {
block.style.width = block.offsetWidth + e.clientX - dragX + "px";
dragX = e.clientX;
}
// remove mouse-move listener on mouse-up
document.onmouseup = () => document.onmousemove = document.onmouseup = null;
}
}
});
.outer {
display: flex;
flex-direction: row;
}
.block {
height: 100px;
width: 50%; /* 50% would suffice*/
}
.block-1 {
background-color: red;
}
.block-2 {
background-color: green;
flex: 1; /* adjust automatically */
min-width: 0; /* allow flexing beyond auto width */
overflow: hidden; /* hide overflow on small width */
}
.slider {
line-height: 100%;
width: 10px;
background-color: #dee2e6;
border: none;
cursor: col-resize;
user-select: none; /* disable selection */
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="outer">
<div class="block block-1" ref="block1" :style="{'width': block1W}">
Block 1
</div>
<div class="slider" #mousedown="drag">
S<br>l<br>i<br>d<br>e<br>r
</div>
<div class="block block-2">
Block 2
</div>
</div>
</div>

Image Comparison Slider Containers Won't Flex Wrap

Here's a link to my codepen: https://codepen.io/Bryandbronstein/pen/NLVQjB
So this is a strange issue and I'm at the end of my rope with this one. I've been learning a bit more about CSS and Javascript, and decided to try out an image comparison slider I found on W3C's website. It works perfectly as a single element, however I want to have a full gallery of these. Yet no matter what I try, they don't seem to want to obey any of the flex rules I set for their parent container. You'll notice in the codepen that one comparison container is hidden behind another. Any ideas?
function initComparisons() {
var x, i;
/*find all elements with an "overlay" class:*/
x = document.getElementsByClassName("img-comp-overlay");
for (i = 0; i < x.length; i++) {
/*once for each "overlay" element:
pass the "overlay" element as a parameter when executing the compareImages function:*/
compareImages(x[i]);
}
function compareImages(img) {
var slider, img, clicked = 0, w, h;
/*get the width and height of the img element*/
w = img.offsetWidth;
h = img.offsetHeight;
/*set the width of the img element to 50%:*/
img.style.width = (w / 2) + "px";
/*create slider:*/
slider = document.createElement("DIV");
slider.setAttribute("class", "img-comp-slider");
/*insert slider*/
img.parentElement.insertBefore(slider, img);
/*position the slider in the middle:*/
slider.style.top = (h / 2) - (slider.offsetHeight / 2) + "px";
slider.style.left = (w / 2) - (slider.offsetWidth / 2) + "px";
/*execute a function when the mouse button is pressed:*/
slider.addEventListener("mousedown", slideReady);
/*and another function when the mouse button is released:*/
window.addEventListener("mouseup", slideFinish);
/*or touched (for touch screens:*/
slider.addEventListener("touchstart", slideReady);
/*and released (for touch screens:*/
window.addEventListener("touchstop", slideFinish);
function slideReady(e) {
/*prevent any other actions that may occur when moving over the image:*/
e.preventDefault();
/*the slider is now clicked and ready to move:*/
clicked = 1;
slider.style.border = "0";
/*execute a function when the slider is moved:*/
window.addEventListener("mousemove", slideMove);
window.addEventListener("touchmove", slideMove);
}
function slideFinish() {
/*the slider is no longer clicked:*/
clicked = 0;
slider.style.border = "3px solid white";
}
function slideMove(e) {
var pos;
/*if the slider is no longer clicked, exit this function:*/
if (clicked == 0) return false;
/*get the cursor's x position:*/
pos = getCursorPos(e)
/*prevent the slider from being positioned outside the image:*/
if (pos < 0) pos = 0;
if (pos > w) pos = w;
/*execute a function that will resize the overlay image according to the cursor:*/
slide(pos);
}
function getCursorPos(e) {
var a, x = 0;
e = e || window.event;
/*get the x positions of the image:*/
a = img.getBoundingClientRect();
/*calculate the cursor's x coordinate, relative to the image:*/
x = e.pageX - a.left;
/*consider any page scrolling:*/
x = x - window.pageXOffset;
return x;
}
function slide(x) {
/*resize the image:*/
img.style.width = x + "px";
/*position the slider:*/
slider.style.left = img.offsetWidth - (slider.offsetWidth / 2) + "px";
}
}
}
html, body {
background-color: #333333;
margin: 0;
padding: 0;
}
* {box-sizing: border-box;}
.gallery_text {
color: white;
font-family: Abel, Helvetica, sans-serif;
font-size: 1.7rem;
text-align: center;
line-height: 1.8em;
}
.row{
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.img-comp-container{
position: relative;
flex: 50%;
}
.img-comp-overlay{
border-right: 2px solid rgba(51,51,51, .5) ;
}
.img-comp-img {
position: absolute;
width: auto;
height: auto;
overflow: hidden;
}
.img-comp-img img {
vertical-align: middle;
}
.img-comp-slider {
position: absolute;
z-index: 9;
cursor: ew-resize;
width: 40px;
height: 40px;
transform: rotate(136deg);
background-color: #333333;
opacity: .8;
border-radius: 10%;
border: 3px solid white;
}
<body onload="initComparisons();">
<div class="row">
<div class="img-comp-container" >
<div class="img-comp-img">
<img src="https://images.pexels.com/photos/162389/lost-places-old-decay-ruin-162389.jpeg?cs=srgb&dl=black-and-white-dark-building-162389.jpg&fm=jpg" width="500" height="450">
</div>
<div class="seperator"></div>
<div class="img-comp-img img-comp-overlay">
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSslfAcWKXuMxBpzcJC5ZUyFqMOb2Jtd12x4kBUGG9mTe3KeMJz" width="500" height="450">
</div>
</div>
<div class="img-comp-container">
<div class="img-comp-img">
<img src="https://images.pexels.com/photos/162389/lost-places-old-decay-ruin-162389.jpeg?cs=srgb&dl=black-and-white-dark-building-162389.jpg&fm=jpg" width="500" height="450">
</div>
<div class="seperator"></div>
<div class="img-comp-img img-comp-overlay">
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSslfAcWKXuMxBpzcJC5ZUyFqMOb2Jtd12x4kBUGG9mTe3KeMJz" width="500" height="450">
</div>
</div>
<div class="img-comp-container">
<div class="img-comp-img">
<img src="https://images.pexels.com/photos/162389/lost-places-old-decay-ruin-162389.jpeg?cs=srgb&dl=black-and-white-dark-building-162389.jpg&fm=jpg" width="500" height="450">
</div>
<div class="seperator"></div>
<div class="img-comp-img img-comp-overlay">
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSslfAcWKXuMxBpzcJC5ZUyFqMOb2Jtd12x4kBUGG9mTe3KeMJz" width="500" height="450">
</div>
</div>
</div>
</body>
The images won't wrap because all children of .img-comp-container were absolutely positioned. This collapsed all the flex elements so that the flex wrap wouldn't work.
Either make one of more of the children of .img-comp-container relative or static, or set a width and height to .img-comp-container.
Here are the changes I made to the CSS:
.img-comp-container{
position: relative;
flex: 50%;
}
.img-comp-img {
position: relative;
width: auto;
height: auto;
overflow: hidden;
}
.img-comp-overlay {
border-right: 2px solid rgba(51, 51, 51, 0.5);
position: absolute;
top: 0;
left: 0;
}
See this codepen fork of your pen for a working solution.

How to add class on specific div when scroll?

I am playing around and practice my JS (beginner). I have created stacking panels and i hit a wall. I just can not target and add class of shadow to the moving element (only when one panel is above other, class should add).
For now i have this:
const boxes = Array.from(document.querySelectorAll(".box"));
const wrapper = document.querySelector(".wrapper");
const leftMargin = 60; //px
function scrollWrap(e) {
let scrollCoord = wrapper.scrollLeft; // horizontal scroll value
boxes.forEach((box, index) => {
let leftMarginStop = index * leftMargin;
const boxCoord = box.getBoundingClientRect();
let leftCoordPanel = boxCoord.left;
let flag = false;
//console.log({scrollCoord, leftMarginStop, leftCoordPanel, box});
if (boxCoord.left <= leftMarginStop) { // if left side of pannel is less than margin 60, 120, 180,...
//console.log("STAHP!!");
box.style.position = "sticky";
box.style.left = `${leftMarginStop}px`; // sets the left to 60, 120, 180,...
flag = true;
if (flag) {
box.classList.add("shadow");
console.log(this) //how to target each panel rather than wrapper?
} else {
box.classList.remove("shadow");
}
} else {
box.style.position = "static";
box.style.left = 0;
flag = false;
}
});
}
wrapper.addEventListener("scroll", scrollWrap);
body {
margin: 0;
padding: 0;
}
.wrapper, .box1, .box2, .box3, .box4, .box5, .box6, .box7, .box8 {
position: sticky;
height: 750px;
z-index: 1;
}
.wrapper {
width: 1442px;
border-right: 1px solid #f2f2f2;
border-bottom: 1px solid #f2f2f2;
display: flex;
overflow: scroll;
}
.wrapper .box1 {
min-width: 357px;
height: 750px;
background-color: #1a1a1a;
}
.wrapper .box2 {
min-width: 357px;
height: 750px;
background-color: #333;
}
.wrapper .box3 {
min-width: 702px;
height: 750px;
background-color: #4d4d4d;
}
.wrapper .box4 {
min-width: 630px;
height: 750px;
background-color: #666;
}
.wrapper .box5 {
min-width: 630px;
height: 750px;
background-color: #808080;
}
.wrapper .box6 {
min-width: 357px;
height: 750px;
background-color: #999;
}
.wrapper .box7 {
min-width: 630px;
height: 750px;
background-color: #b3b3b3;
}
.wrapper .box8 {
min-width: 630px;
height: 750px;
background-color: #ccc;
}
.shadow {
box-shadow: -4px 0px 40px rgba(0, 0, 0, 0.2);
}
<div class="wrapper">
<div class="box box1"></div>
<div class="box box2"></div>
<div class="box box3"></div>
<div class="box box4"></div>
<div class="box box5"></div>
<div class="box box6"></div>
<div class="box box7"></div>
<div class="box box8"></div>
</div>
If anyone can help me out, please show me code with explanation, so i can see and know what and how.
I try to do it with some flag or without, but every time the class is added when element reaches the end (left padding). I want to add class when element is on top of other element. If it is not, remove class. Also, how to make this so it works on mouse scroll wheel? I was testing this with apple magic mouse, but on scroll wheel it does not work.
Oh and please, if you see something very wrong please let me know, i am beginner and would like to learn something from this post.
For now i have managed to update my code. It works when i scroll and shadow is also applying to the panel. There is still something i wonder.
When i add shadow class i created transition. It works perfect, but when i remove shadow class it just disappears, no transition back. Why is that? I want shadow to appear and disappear in same way
How to track every single panel if it has reached the left margin, and than apply eventListener with mouse enter and mouse leave on it? So when i hover to "closed" panel, i get that item and if i hover on NOT "closed" panel i will not get item. I was trying with console.log "this" but it returned every panel i mouse entered it
How to target every panel, so i can later say, when 4th panel reaches left margin, the margin of stacked elements change?
My updated code:
const boxes = Array.from(document.querySelectorAll(".box"));
const wrapper = document.querySelector(".wrapper");
const leftMargin = 60; //px
function scrollWrap(e) {
let scrollCoord = wrapper.scrollLeft; // horizontal scroll value
boxes.forEach((box, index) => {
let leftMarginStop = (index) * leftMargin; // calculation for left margin stop (60, 120, 180,...)
const boxCoord = box.getBoundingClientRect();
const leftSideOfCurrent = boxCoord.left; // coordinarion of left side of panel
const rightSideOfCurrent = boxCoord.right; // coordinarion of right side of panel
const leftSideOfNextItem = box.nextElementSibling.getBoundingClientRect().left; // coordinarion of left side of NEXT panel
box.style.position = "sticky";
box.style.left = `${leftMarginStop}px`;
// controll shadow of first element
scrollCoord > 0 ? boxes[1].classList.add("shadow") : boxes[1].classList.remove("shadow");
// controll shadow of all 0+ elements
if (leftSideOfCurrent <= leftMarginStop) { // if left side of pannel is less than margin 60, 120, 180,...
box.nextElementSibling.classList.add("shadow");
}
// controll removal of shadow of all 0+ elements
if (leftSideOfNextItem === rightSideOfCurrent) {
box.nextElementSibling.classList.remove("shadow");
}
// when panel 5 reach left margin, left margin change from 60 to 30 to all panels
if (boxes[4] && leftSideOfCurrent < leftMarginStop) {
console.log("helo");
}
});
}
wrapper.addEventListener("scroll", scrollWrap);
body {
margin: 0;
padding: 0;
}
.wrapper, .box1, .box2, .box3, .box4, .box5, .box6, .box7, .box8 {
position: sticky;
height: 750px;
z-index: 1;
}
.wrapper {
width: 1442px;
border-right: 1px solid #f2f2f2;
border-bottom: 1px solid #f2f2f2;
display: flex;
overflow: scroll;
}
.wrapper .box0 {
min-width: 357px;
background-color: #1a1a1a;
}
.wrapper .box1 {
min-width: 357px;
background-color: #333;
}
.wrapper .box2 {
min-width: 702px;
background-color: #4d4d4d;
}
.wrapper .box3 {
min-width: 630px;
background-color: #666;
}
.wrapper .box4 {
min-width: 630px;
background-color: #808080;
}
.wrapper .box5 {
min-width: 357px;
background-color: #999;
}
.wrapper .box6 {
min-width: 630px;
background-color: #b3b3b3;
}
.wrapper .box7 {
min-width: 630px;
background-color: #ccc;
}
.shadow {
box-shadow: -4px 0px 40px rgba(0, 0, 0, 1.2);
-webkit-transition: box-shadow 0.2s cubic-bezier(0.4,-0.01, 0, 0.98);
-moz-transition: box-shadow 0.2s cubic-bezier(0.4,-0.01, 0, 0.98);
-o-transition: box-shadow 0.2s cubic-bezier(0.4,-0.01, 0, 0.98);
transition: box-shadow 0.2s cubic-bezier(0.4,-0.01, 0, 0.98);
}
<div class="wrapper">
<div class="box box0"></div>
<div class="box box1"></div>
<div class="box box2"></div>
<div class="box box3"></div>
<div class="box box4"></div>
<div class="box box5"></div>
<div class="box box6"></div>
<div class="box box7"></div>
</div>
Tips for your code:
In your CSS code, the wrapper element has a width property. This
causes the page and the wrapper element to have a scrollbars. So,
remove it.
You do not need to use javascript to add stickiness to the
boxes. The only CSS will do this for you. You need JavaScript only
for add the left property and shadow to the boxes.
Don’t try to use overflow: auto|scroll|hidden on the parent element
of a position:sticky element. It completely breaks the stickiness.
overflow: visible is fine. See the code snippet below.
In your JavaScript code, the flag variant value, always is true, So
the shadow class can't remove from elements.
Other notes:
If you’re wanting to use position:absolute on an element inside of
a sticky element you have to be careful. If your app is running in
an older browser that doesn’t support position:sticky, then that
sticky element won’t act like a relative positioned element. So
the absolute positioned element will skip it and look up the DOM
tree until it finds the next non-static element (absolute / relative / fixed position), defaulting to the html element if none
found. In other words, your absolute positioned element is going
to be in a way different place on the screen than you expected it to
be.
position: sticky; is supported in a lot of browsers, but not yet
in Edge. IE doesn’t matter at this point. There are many
polyfills out there if you absolutely have to have this behavior,
but they all use JavaScript. A better option is to design your app
so that sticky position is a slick addition, but the app still
functions without it.
Example:
const boxes = Array.from( document.querySelectorAll( '.box' ) ),
scroller = document.querySelector( '.scroller' ),
leftMargin = 30,
length = boxes.length - 1;
function scrollWrap() {
boxes.forEach( function( box, index ) {
let leftMarginStop = index * leftMargin;
const boxCoord = box.getBoundingClientRect();
let leftCoordPanel = boxCoord.left;
if ( leftCoordPanel <= leftMarginStop ) {
box.style.left = leftMarginStop + 'px';
if ( index < length ) boxes[ index + 1 ].classList.add( 'shadow' )
if ( index == 0 && boxes[ 1 ].getBoundingClientRect().left == box.offsetWidth ) boxes[ 1 ].classList.remove( 'shadow' )
} else {
box.style.left = 0;
if ( index < length ) boxes[ index + 1 ].classList.remove( 'shadow' )
}
} );
}
scroller.addEventListener( 'scroll', scrollWrap )
html,
body {
margin: 0;
height: 100%
}
.scroller {
height: 100%;
display: flex;
align-items: center;
overflow-x: auto;
overflow-y: hidden
}
.wrapper {
height: 100%;
display: flex
}
.box {
min-width: 630px;
height: 100%;
position: -webkit-sticky;
position: sticky;
left: 0 /* <-- become sticky once touching left edge */
}
.box1 {
min-width: 357px;
background: #1a1a1a url(https://cdn.pixabay.com/photo/2015/12/10/16/09/seamless-pattern-1086662__340.jpg)
}
.box2 {
min-width: 357px;
background: #333 url(https://cdn.pixabay.com/photo/2015/12/18/23/46/template-1099298__340.png)
}
.box3 {
min-width: 702px;
background: #4d4d4d url(https://cdn.pixabay.com/photo/2014/07/28/16/00/pattern-403769__340.png)
}
.box4 {
background: #666 url(https://cdn.pixabay.com/photo/2017/06/10/03/14/damask-2388884__340.png)
}
.box5 {
background: #808080 url(https://cdn.pixabay.com/photo/2015/12/12/17/34/seamless-pattern-1089797__340.png)
}
.box6 {
min-width: 357px;
background: #999 url(https://cdn.pixabay.com/photo/2016/03/06/16/23/background-1240686__340.png)
}
.box7 {
background: #b3b3b3 url(https://cdn.pixabay.com/photo/2018/04/24/05/00/backdrop-3346304__340.png)
}
.box8 {
background: #ccc url(https://cdn.pixabay.com/photo/2016/04/01/09/03/floral-1299131__340.png)
}
.shadow {
box-shadow: -10px 0px 40px rgba(0, 0, 0, 1);
}
<div class="scroller">
<div class="wrapper">
<div class="box box1"></div>
<div class="box box2"></div>
<div class="box box3"></div>
<div class="box box4"></div>
<div class="box box5"></div>
<div class="box box6"></div>
<div class="box box7"></div>
<div class="box box8"></div>
</div>
</div>
About your updated code:
the following line in your jS code:
const leftSideOfNextItem = box.nextElementSibling.getBoundingClientRect().left;
causes an error. Because, when index become 8, box.nextElementSibling can't retrieve any element. So you can change it to this:
const leftSideOfNextItem = ( index < boxes.length - 1 ) ? box.nextElementSibling.getBoundingClientRect().left : 0;
Also at the end of your JS code, the following code snippet:
if (index[4] && leftSideOfCurrent < leftMarginStop) {
console.log("helo");
}
must change to:
// when panel 5 reach left margin, left margin change from 60 to 30 to all panels
if (index == 4 && leftSideOfCurrent <= leftMarginStop) {
console.log("helo");
leftMargin = 30
}
Also you must change const leftMargin = 60; to var leftMargin = 60;
Updated code:
const boxes = Array.from(document.querySelectorAll(".box"));
const wrapper = document.querySelector(".wrapper");
var leftMargin = 60; //px
function scrollWrap(e) {
let scrollCoord = wrapper.scrollLeft; // horizontal scroll value
boxes.forEach((box, index) => {
let leftMarginStop = (index) * leftMargin; // calculation for left margin stop (60, 120, 180,...)
const boxCoord = box.getBoundingClientRect();
const leftSideOfCurrent = boxCoord.left; // coordinarion of left side of panel
const rightSideOfCurrent = boxCoord.right; // coordinarion of right side of panel
const leftSideOfNextItem = ( index < boxes.length - 1 ) ? box.nextElementSibling.getBoundingClientRect().left: 0; // coordinarion of left side of NEXT panel
box.style.position = "sticky";
box.style.left = `${leftMarginStop}px`;
// controll shadow of first element
scrollCoord > 0 ? boxes[1].classList.add("shadow") : boxes[1].classList.remove("shadow");
// controll shadow of all 0+ elements
if (leftSideOfCurrent <= leftMarginStop) { // if left side of pannel is less than margin 60, 120, 180,...
box.nextElementSibling.classList.add("shadow");
}
// controll removal of shadow of all 0+ elements
if (leftSideOfNextItem === rightSideOfCurrent) {
box.nextElementSibling.classList.remove("shadow");
}
// when panel 5 reach left margin, left margin change from 60 to 30 to all panels
if (index == 4 && leftSideOfCurrent <= leftMarginStop) {
console.log("helo");
leftMargin = 30
}
});
}
wrapper.addEventListener("scroll", scrollWrap);
body {
margin: 0;
padding: 0;
}
.wrapper, .box0, .box1, .box2, .box3, .box4, .box5, .box6, .box7 {
position: sticky;
height: 750px;
z-index: 1;
}
.wrapper {
width: 1442px;
border-right: 1px solid #f2f2f2;
border-bottom: 1px solid #f2f2f2;
display: flex;
overflow: scroll;
}
.wrapper .box0 {
min-width: 357px;
background-color: #1a1a1a;
}
.wrapper .box1 {
min-width: 357px;
background-color: #333;
}
.wrapper .box2 {
min-width: 702px;
background-color: #4d4d4d;
}
.wrapper .box3 {
min-width: 630px;
background-color: #666;
}
.wrapper .box4 {
min-width: 630px;
background-color: #808080;
}
.wrapper .box5 {
min-width: 357px;
background-color: #999;
}
.wrapper .box6 {
min-width: 630px;
background-color: #b3b3b3;
}
.wrapper .box7 {
min-width: 630px;
background-color: #ccc;
}
.shadow {
box-shadow: -4px 0px 40px rgba(0, 0, 0, 1.2);
-webkit-transition: box-shadow 0.2s cubic-bezier(0.4,-0.01, 0, 0.98);
-moz-transition: box-shadow 0.2s cubic-bezier(0.4,-0.01, 0, 0.98);
-o-transition: box-shadow 0.2s cubic-bezier(0.4,-0.01, 0, 0.98);
transition: box-shadow 0.2s cubic-bezier(0.4,-0.01, 0, 0.98);
}
/* To make the console visible. */
.box0 {
height: 700px;
)
<div class="wrapper">
<div class="box box0"></div>
<div class="box box1"></div>
<div class="box box2"></div>
<div class="box box3"></div>
<div class="box box4"></div>
<div class="box box5"></div>
<div class="box box6"></div>
<div class="box box7"></div>
</div>
This was helpful. I know for error but could not fix it. I have set let leftMargin = 60; i use let instead var.
I am working on the margins, if the 5th panel reaches the left margin the margin of all panels get 30px. I did it also for reverse:
if (index > 4 && leftSideOfCurrent <= leftMarginStop) {
leftMargin = 30;
} else if (index < 5 && leftSideOfCurrent > leftMarginStop) {
leftMargin = 60;
}
But now when 5th panel reaches left margin, the panels less than 5 get EACH transitioned seperately instead of all, but when the 5th element is no longer in left margin, all gets transitioned at same time. Why is it like that? I do not understand. Can you explain please?

Fixing the right button of a vanilla Javascript carousel

Against all reason, I'm trying to create a vanilla JavaScript carousel.
I am having two problems:
1. The images move left at widths of -680px as they should but when I tried to create the same function for the right button, the left value goes to 1370px making the picture off the screen.
2. I would like for it to slide left rather jump left (same for right), I managed to get it to do this but it doesn't work on the first slide, only from the second slide.
Here is the HTML code just for the carousel:
<div id = "container">
<div id = "carousel">
<div class = "slide"><img class = "slideImage" class = "active" src = "sithCover.png"></div>
<div class = "slide"><img class = "slideImage" src = "darthVader.png"></div>
<div class = "slide"><img class = "slideImage" src = "darthSidious.png"></div>
<div class = "slide"><img class = "slideImage" src = "kyloRen.png"></div>
</div>
</div>
<div id = "left" class = "button"></div>
<div id = "right" class = "button"></div>
Here is the CSS code:
#container {
position: absolute;
top: 200px;
left: 100px;
width: 680px;
height: 360px;
white-space: nowrap;
overflow:hidden;
}
#carousel {
position: absolute;
width: 2740px;
height: 360px;
white-space: nowrap;
overflow: hidden;
transition: left 300ms linear;
}
.slide {
display: inline-block;
height: 360px;
width: 680px;
padding: 0;
margin: 0;
transition: left 300ms linear;
}
.slideImage {
position:relative;
height: 360px;
width: 680px;
float: left;
}
.button {
position: absolute;
top: 340px;
height: 60px;
width: 60px;
border-bottom: 12px solid red;
}
#left {
left: 115px;
border-left: 12px solid red;
transform: rotate(45deg);
}
#right {
left: 693px;
border-right: 12px solid red;
transform: rotate(-45deg);
}
Here is the JavaScript:
var carousel = document.querySelector('#carousel');
var firstVal = 0;
document.querySelector('#left').addEventListener("click", moveLeft);
function moveLeft (){
firstVal +=685;
carousel.style.left = "-"+firstVal+"px";
};
document.querySelector('#right').addEventListener("click", moveRight);
function moveRight() {
firstVal +=685;
carousel.style.left = "+"+firstVal+"px";
};
Here is a JSFiddle so that you can see what I mean:
"https://jsfiddle.net/way81/8to1kkyj/"
I appreciate your time in reading my question and any help would be much appreciated.
Ofcourse it goes from -685px on left click and then to +1370pxthe next right click; You are always adding 685 to your firstVal variable.
firstVal = 0
//firstVal is worth 0
moveLeft()
//firstVal is now worth 685
moveRight()
//firstVal is now worth 1370.
The problem is that when you apply the firstVal to your CSS thing in the javascript, you create a string to get your negative value (where you apply the "-" sign infront of firstVal)
Instead, write them like this
function moveLeft (){
firstVal -=685; //note we now subtract, the "-" should appear when the number becomes negative
carousel.style.left = firstVal + "px";
};
function moveRight() {
firstVal +=685;
carousel.style.left = firstVal + "px";
};
var left = document.getElementById("left");
left.addEventListener("click", moveLeft, false);
var right = document.getElementById("right");
right.addEventListener("click", moveRight, false);
var carousel = document.getElementById("carousel");
var images = document.getElementsByTagName("img");
var position = 0;
var interval = 685;
var minPos = ("-" + interval) * images.length;
var maxPos = interval * images.length;
//slide image to the left side <--
function moveRight() {
if (position > (minPos + interval)) {
position -= interval;
carousel.style.left = position + "px";
}
if (position === (minPos + interval)) {
right.style.display = "none";
}
left.style.display = "block";
}
//slide image to the right side -->
function moveLeft() {
if (position < (maxPos - interval) && position < 0) {
position += interval;
carousel.style.left = position + "px";
}
if (position === 0) {
left.style.display = "none";
}
right.style.display = "block";
}
#container {
position: absolute;
top: 200px;
left: 100px;
width: 680px;
height: 360px;
white-space: nowrap;
overflow: hidden;
}
#carousel {
position: absolute;
width: 2740px;
height: 360px;
white-space: nowrap;
overflow: hidden;
transition: left 300ms linear;
}
.slide {
display: inline-block;
height: 360px;
width: 680px;
padding: 0;
margin: 0;
transition: left 300ms linear;
}
.slideImage {
position: relative;
height: 360px;
width: 680px;
float: left;
}
.button {
position: absolute;
top: 340px;
height: 60px;
width: 60px;
border-bottom: 12px solid red;
}
#left {
left: 115px;
border-left: 12px solid red;
transform: rotate(45deg);
display: none;
}
#right {
left: 693px;
border-right: 12px solid red;
transform: rotate(-45deg);
}
<div id="container">
<div id="carousel">
<div class="slide">
<img class="slideImage" class="active" src="sithCover.png" alt="slide1">
</div>
<div class="slide">
<img class="slideImage" src="darthVader.png" alt="slide2">
</div>
<div class="slide">
<img class="slideImage" src="darthSidious.png" alt="slide3">
</div>
<div class="slide">
<img class="slideImage" src="kyloRen.png" alt="slide4">
</div>
</div>
</div>
<div id="left" class="button"></div>
<div id="right" class="button"></div>

Categories