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?
Related
how can i create dialog effect like google keep.i tried to debug the css but without any success.
is there code example out there ?
i see that they using hidden position fixed modal that triggering on click but how they calculate the position.
This is what i could make out of your question(pure JS and CSS).
Below is the code
var example_note = document.getElementsByClassName('example_note')[0];
var close_btn = document.getElementById('close_btn');
example_note.onclick = function() {
document.getElementsByClassName('background_change')[0].style.display = "block";
document.getElementsByClassName('display_block')[0].style.display = "block";
example_note.style.display="none";
}
close_btn.onclick = function() {
document.getElementsByClassName('background_change')[0].style.display = "none";
document.getElementsByClassName('display_block')[0].style.display = "none";
example_note.style.display="block";
}
* {
margin: 0px;
padding: 0px;
font-family: 'arial';
}
.example_note {
position: absolute;
width: 250px;
margin-top: 10%;
margin-left: 15%;
box-shadow: -1px 1px 10px 3px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: -1px 1px 10px 3px rgba(0, 0, 0, 0.2);
-moz-box-shadow: -1px 1px 10px 3px rgba(0, 0, 0, 0.2);
padding: 30px;
border-radius: 15px;
background-color: white;
}
.example_note h1 {
font-size: 23px;
}
.display_block {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 450px;
background-color: white;
padding: 30px;
border-radius: 15px;
transform-origin: 0 25%;
animation: show_block 0.2s 1;
}
#keyframes show_block {
from {
transform: translate(-50%, -50%)scale(0);
}
to {
transform: translate(-50%, -50%)scale(1);
}
}
input[type="text"] {
width: 390px;
padding: 10px;
border: none;
}
input[type="text"]:focus {
outline: none;
}
button {
float: right;
background-color: white;
padding: 10px 20px 10px 20px;
border-radius: 8px;
border: none;
font-size: 17px;
transition: 0.4s;
font-weight: bold;
outline: none;
}
button:hover {
background-color: #E3E3E3;
}
.background_change {
display: none;
height: 100%;
width: 100%;
position: absolute;
background-color: black;
opacity: 0.6;
animation: show_back 0.5s 1;
}
#keyframes show_back {
from {
opacity: 0;
}
to {
opacity: 0.6;
}
}
<div class="example_note">
<h1>Example Note</h1>
</div>
<div class="background_change"></div>
<div class="display_block">
<input type="text" name="title" placeholder="Title" style="font-size: 25px;">
<br>
<input type="text" name="name" value="Example Note" style="font-size: 15px; font-weight: bold;">
<br>
<button id="close_btn">close</button>
</div>
This answer is plain JavaScript and CSS (no libraries), and produces the following effect:
A full working example is contained in the following snippet (best previewed full screen):
function openModal(noteEl, modalEl, modalContainerEl) {
// Compute and apply the transform to deform the modal to cover the note with a transition to make it animate
const transform = computeTransform(noteEl);
modalEl.style.transform = transform;
modalEl.style.transition = 'transform 250ms';
// Setup the modal background animate in too
modalContainerEl.style.backgroundColor = 'transparent';
modalContainerEl.style.transition = 'background-color 250ms';
// Show the modal
modalContainerEl.classList.add('modal-container--open');
// Put the rest in a setTimeout to allow the styles applied above to take
// affect and render before we overwrite them with new ones below
setTimeout(function () {
// Remove the transform to allow the modal to return to it's natural shape and position
modalEl.style.transform = 'none';
modalContainerEl.style.backgroundColor = 'rgba(33, 33, 33, 0.5)';
}, 0)
}
function computeTransform(noteEl) {
// Modal positions here are hardcoded to match styles set in CSS
const modalTop = 150;
const modalLeft = (document.body.offsetWidth / 2) - 300;
const modalWidth = 600;
const modalHeight = 150;
// Get note div's position relative to the viewport
const notePosition = noteEl.getBoundingClientRect();
// Compute a CSS transform that moves the modal to match the note's position
const translateX = notePosition.left - modalLeft;
const translateY = notePosition.top - modalTop;
const scaleX = notePosition.width / modalWidth;
const scaleY = notePosition.height / modalHeight;
return `translateX(${translateX}px) translateY(${translateY}px) scaleX(${scaleX}) scaleY(${scaleY})`;
}
// Handle click events using event delegation
document.addEventListener('click', function (event) {
// Handle click events on note elements (open modal)
if (event.target.className === 'note') {
// Get a reference
const modalContainerEl = document.querySelector('.modal-container');
const modalEl = document.querySelector('.modal');
openModal(event.target, modalEl, modalContainerEl);
}
// Handle click event on modal background element (close modal)
if (event.target.classList.contains('modal-container')) {
event.target.classList.remove('modal-container--open');
}
})
body {
display: flex;
flex-wrap: wrap;
width: 100%;
color: #333;
}
.note {
flex: 0 0 200px;
width: 200px;
height: 200px;
border: 1px solid #CCC;
margin: 12px;
border-radius: 10px;
}
.modal-container {
display: none;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(33, 33, 33, 0.5);
}
.modal-container--open {
display: block;
}
.modal {
position: absolute;
top: 150px;
left: 50%;
margin-left: -300px;
width: 600px;
height: 150px;
transform-origin: top left;
will-change: transform; /* makes the animation run smoother */
background-color: #EEE;
border-radius: 10px;
}
<!DOCTYPE html>
<html>
<head>
<style type="text/css" />
</style>
<script type="text/javascript">
</script>
</head>
<body>
<div class="note">1</div>
<div class="note">2</div>
<div class="note">3</div>
<div class="note">4</div>
<div class="note">5</div>
<div class="note">6</div>
<div class="note">7</div>
<div class="note">8</div>
<div class="note">9</div>
<div class="note">10</div>
<div class="note">11</div>
<div class="note">12</div>
<div class="note">13</div>
<div class="note">14</div>
<div class="note">15</div>
<div class="note">16</div>
<div class="note">17</div>
<div class="note">18</div>
<div class="note">19</div>
<div class="note">20</div>
<div class="modal-container">
<div class="modal">
Modal
</div>
</div>
</body>
</html>
The trick is to apply a CSS transform to the modal in order to deform into the shape/position of the clicked note before showing the modal. We can then remove the transform and use a CSS transition to get it to smoothly animate into it's natural shape/position.
We calculate the transform as follows:
function computeTransform(noteEl) {
// Modal positions here are hardcoded to match styles set in CSS
const modalTop = 150;
const modalLeft = (document.body.offsetWidth / 2) - 300;
const modalWidth = 600;
const modalHeight = 150;
// Get note div's position relative to the viewport
const notePosition = noteEl.getBoundingClientRect();
// Compute a CSS transform that moves the modal to match the note's position
const translateX = notePosition.left - modalLeft;
const translateY = notePosition.top - modalTop;
const scaleX = notePosition.width / modalWidth;
const scaleY = notePosition.height / modalHeight;
return `translateX(${translateX}px) translateY(${translateY}px) scaleX(${scaleX}) scaleY(${scaleY})`;
}
And we apply it as follows:
function openModal(noteEl, modalEl, modalContainerEl) {
// Compute and apply the transform to deform the modal to cover the note with a transition to make it animate
const transform = computeTransform(noteEl);
modalEl.style.transform = transform;
modalEl.style.transition = 'transform 250ms';
// Setup the modal background animate in too
modalContainerEl.style.backgroundColor = 'transparent';
modalContainerEl.style.transition = 'background-color 250ms';
// Show the modal
modalContainerEl.classList.add('modal-container--open');
// Put the rest in a setTimeout to allow the styles applied above to take
// affect and render before we overwrite them with new ones below
setTimeout(function () {
// Remove the transform to allow the modal to return to it's natural shape and position
modalEl.style.transform = 'none';
modalContainerEl.style.backgroundColor = 'rgba(33, 33, 33, 0.5)';
}, 0)
}
Note the setTimeout between applying and removing the transform. This is important as otherwise the transform will never actually be applied.
See the snippet for the full details, but also of note: the transform-origin: top left; style on the modal is important to make the transform computation work.
I did like the example above, wich worked perfectly. I added opacity to de card selected to remove erase the card from the screen when it is clicked
<style type="text/css">
body {
display: flex;
flex-wrap: wrap;
width: 100%;
color: #333;
}
.note {
flex: 0 0 200px;
width: 200px;
height: 200px;
border: 1px solid #CCC;
margin: 12px;
border-radius: 10px;
}
.modal-container {
display: none;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(33, 33, 33, 0.5);
}
.modal-container--open {
display: block;
}
.modal {
position: absolute;
top: 150px;
left: 50%;
margin-left: -300px;
width: 600px;
height: 150px;
transform-origin: top left;
will-change: transform;
/* makes the animation run smoother */
background-color: #EEE;
border-radius: 10px;
}
</style>
<script type="text/javascript">
function openModal(noteEl, modalEl, modalContainerEl) {
// Compute and apply the transform to deform the modal to cover the note with a transition to make it animate
const transform = computeTransform(noteEl);
modalEl.style.transform = transform;
modalEl.style.transition = 'transform 250ms';
// Setup the modal background animate in too
modalContainerEl.style.backgroundColor = 'transparent';
modalContainerEl.style.transition = 'background-color 250ms';
// Show the modal
modalContainerEl.classList.add('modal-container--open');
noteEl.style.opacity = 0;
// Put the rest in a setTimeout to allow the styles applied above to take
// affect and render before we overwrite them with new ones below
setTimeout(function () {
// Remove the transform to allow the modal to return to it's natural shape and position
modalEl.style.transform = 'none';
modalContainerEl.style.backgroundColor = 'rgba(33, 33, 33, 0.5)';
}, 0)
}
function computeTransform(noteEl) {
// Modal positions here are hardcoded to match styles set in CSS
const modalTop = 150;
const modalLeft = (document.body.offsetWidth / 2) - 300;
const modalWidth = 600;
const modalHeight = 150;
// Get note div's position relative to the viewport
const notePosition = noteEl.getBoundingClientRect();
// Compute a CSS transform that moves the modal to match the note's position
const translateX = notePosition.left - modalLeft;
const translateY = notePosition.top - modalTop;
const scaleX = notePosition.width / modalWidth;
const scaleY = notePosition.height / modalHeight;
return `translateX(${translateX}px) translateY(${translateY}px) scaleX(${scaleX}) scaleY(${scaleY})`;
}
// Handle click events using event delegation
let cardSelected;
document.addEventListener('click', function (event) {
// Handle click events on note elements (open modal)
if (event.target.className === 'note') {
// Get a reference
cardSelected = event.target
const modalContainerEl = document.querySelector('.modal-container');
const modalEl = document.querySelector('.modal');
openModal(event.target, modalEl, modalContainerEl);
}
// Handle click event on modal background element (close modal)
if (event.target.classList.contains('modal-container')) {
event.target.classList.remove('modal-container--open');
cardSelected.style.opacity = 1;
}
})
</script>
<div class="note">1</div>
<div class="note">2</div>
<div class="note">3</div>
<div class="note">4</div>
<div class="note">5</div>
<div class="note">6</div>
<div class="note">7</div>
<div class="note">8</div>
<div class="note">9</div>
<div class="note">10</div>
<div class="note">11</div>
<div class="note">12</div>
<div class="note">13</div>
<div class="note">14</div>
<div class="note">15</div>
<div class="note">16</div>
<div class="note">17</div>
<div class="note">18</div>
<div class="note">19</div>
<div class="note">20</div>
<div class="modal-container">
<div class="modal">
Modal
</div>
</div>
To create a modal, you can use a library called Swal. Swal, however does not look like keep's popups, so I've restyled it below.
script links you must reference to:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/#sweetalert2/theme-material-ui/material-ui.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2#10"></script>. You may also wish to visit Google Fonts and pick a nice font-family. Test this pop-up by clicking run this snippet.
const MySwal = Swal.mixin({
//background: "rgb(10,10,10)",
background: "white",
showCloseButton: true,
backdrop: "rgba(0,0,0,0.7)",
showClass: {
popup: "animate__animated animate__fadeInDown med"
},
hideClass: {
popup: "animate__animated animate__fadeOutUp fast"
},
width: "95vw"
});
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.js" integrity="sha512-HBD0cOZJYcymSn0H0CnN3VBhQLdiH8imucm16ZQ792TT2n48u6nmX+T7hZTCwmzIrgMt76x4rHhR7KkZqhIGxA==" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2#10"></script>
<script src="alpha.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>
<link rel="shortcut icon" href="favicon.png" id="iconshort">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/#sweetalert2/theme-material-ui/material-ui.css">
<script src='https://kit.fontawesome.com/a076d05399.js'></script>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght#300&display=swap" rel="stylesheet">
</head>
<body>
<button onclick="MySwal.fire('Title','Content')">LAUNCH POP-UP</button>
</body>
const MySwal = Swal.mixin({
//background: "rgb(10,10,10)",
background: "white",
showCloseButton: true,
backdrop: "rgba(0,0,0,0.7)",
showClass: {
popup: "animate__animated animate__fadeInDown med"
},
hideClass: {
popup: "animate__animated animate__fadeOutUp fast"
},
width: "95vw",
willOpen: function() {
open_audio.play();
},
willClose: function() {
confirm_audio.play();
}
});
I am trying to a make carousel using pure Javascript. I successfully manage to slide the carousel and have created left and right buttons.
I took my slide functions and added them to the button on-click event-listener, but I have problems when I implement the function on my buttons. It does not behave as expected. My code is below, how can I fix this?
const images = document.getElementById('imgs'); //here
const allImages = document.querySelectorAll('#imgs img');
const leftBtn = document.getElementById('left');
const rightBtn = document.getElementById('right');
let index = 0;
function changeSliderPage() {
const dot = [...document.getElementsByClassName('star')];
index++;
if (index > allImages.length - 1) {
index = 0
}
imgs.style.transform = `translateX(${-index * 500}px)`;
dot.forEach((dot, i) => {
if (i === index) {
dot.classList.add('active')
} else {
dot.classList.remove('active')
}
});
};
allImages.forEach(i => {
const elem = document.createElement('div');
elem.classList.add('star');
document.body.appendChild(elem)
});
rightBtn.onclick = () => {
changeSliderPage(index + 1);
}
leftBtn.onclick = () => {
changeSliderPage(index - 1);
}
let x = setInterval(changeSliderPage, 100000);
images.onmouseover = () => {
clearInterval(x)
}
images.onmouseout = () => {
x = setInterval(changeSliderPage, 2000);
}
*{
box-sizing: border-box;
}
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.carousel {
overflow: hidden;
width: 500px;
height: 500px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, .3);
border-radius: 5px;
}
.image-container {
display: flex;
transition: transform 300ms linear;
transform: translateX(0);
}
img {
width:500px;
height: 500px;
object-fit: cover;
}
.star{
cursor: pointer;
height: 15px;
width: 15px;
margin: 0 10px;
border-radius: 50%;
display: inline-block;
transition: background-color 0.6s ease;
background-color: #eeeeee;
}
.star.active{
background-color: red;
}
button{
cursor: pointer;
position: relative;
font-size: 18px;
transition: 0.6s ease;
user-select: none;
height: 50px;
width: 40px;
display: flex;
justify-content: center;
align-items: center;
align-content: center;
top: calc(50% - 25px);
}
button:hover {
background-color: rgba(0,0,0,0.8);
};
button.left {
border-radius: 3px 0 0 3px;
right: 0;
}
button.left {
border-radius: 3px 0 0 3px;
left: 0;
}
<button id="left">❮</button>
<button id="right">❯</button>
<div class="carousel">
<div class="image-container" id="imgs" >
<img src="https://images.unsplash.com/photo-1599736375341-51b0a848f3c7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
<img src="https://images.unsplash.com/photo-1516026672322-bc52d61a55d5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
<img src="https://images.unsplash.com/photo-1573081586928-127ecc7948b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
<img src="https://images.unsplash.com/flagged/photo-1572850005109-f4ac7529bf9f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
</div>
</div>
Logic that I use with carousels:
for example you have 4 images:
[1][2][3][4]
I have an animation for sliding every image, I add 5th image which is same as image no 1:
[1][2][3][4][1]
Imagine cursor which shows what image is currently displayed, Ill mark cursor as ! !
So at begin:
[!1!][2][3][4][1]
Now the slider moves on...
[1][!2!][3][4][1]
etc...
It moves to last image:
[1][2][3][4][!1!]
And now it has to move under the hood from last image to first image, but without any animation so the whole change is not visible by user:
[!1!][2][3][4][5]
This way you can get inifinite carousel, just need to check in javascript if current image is last one and you want to slide right -> no animation. Same if you are on 1st image and want to slide left.
for a bit of fun I decided to make a sliding shelf today. I got it looking and working how I wanted and then decided to put some content in the cards.
I added text to the first card and it moved down almost on to a new line. I can't explain why this happened (though I'm sure there's a simple explanation). Can one of you tell me what I'm missing, please?
Thank you 🙂
function hideTrigger() {
leftTrig.removeAttribute("hidden");
rightTrig.removeAttribute("hidden");
switch (pos) {
case 0:
leftTrig.setAttribute("hidden", "");
break;
case posMax:
rightTrig.setAttribute("hidden", "")
}
}
function moveHelper() {
boxesCont.style.transform = slideHelper(), hideTrigger(), setTimeout(function() {
end = boxCont[posMax].getBoundingClientRect().left <= window.innerWidth ? 1 : 0
}, 300)
}
function slideHelper() {
return "translate(-" + boxSize * pos + "px)"
}
function moveRight() {
pos < posMax && (end ? endHelper() : (pos++, moveHelper()))
}
function moveLeft() {
pos > 0 && (pos--, moveHelper())
}
function moveTo(e) {
e >= 0 && e <= posMax && (pos = e, moveHelper())
}
function endHelper() {
pos++;
let edgeDif = boxSize - boxMargin - (window.innerWidth - boxCont[posMax].getBoundingClientRect().left);
rightTrig.setAttribute("hidden", ""), boxesCont.style.transform = "translate(-" + (boxSize * (pos - 1) + edgeDif) + "px)"
}
var leftTrig = document.querySelector(".directional.left");
var rightTrig = document.querySelector(".directional.right");
var boxesCont = document.querySelector(".shelf .boxes");
var boxCont = boxesCont.querySelectorAll(".box");
var boxStyle = boxCont[0].currentStyle || window.getComputedStyle(boxCont[0]);
var boxMargin = parseFloat(boxStyle.marginLeft);
var boxSize = boxCont[0].offsetWidth + 2 * boxMargin;
var end = 0;
var pos = 0;
var posMax = boxCont.length - 1;
leftTrig.addEventListener("click", function() {
moveLeft()
});
rightTrig.addEventListener("click", function() {
moveRight()
});
moveHelper();
body {
margin: 0;
font-family: Roboto;
text-align: justify;
}
.shelf {
position: relative;
overflow-x: hidden;
font-size: 0;
}
.shelf button.directional {
position: absolute;
transition: opacity 0.3s cubic-bezier(.25, .8, .25, 1);
height: 100%;
width: 30px;
top: 0;
opacity: 0;
border: 0;
border-radius: 0;
background: rgba(55, 71, 79, 0.4);
color: #F5F5F5;
cursor: pointer;
z-index: 9999;
}
.shelf button.directional.left {
left: 0;
}
.shelf button.directional.right {
right: 0;
}
.boxes {
white-space: nowrap;
transition: transform 0.3s cubic-bezier(.25, .8, .25, 1);
}
.box {
display: inline-block;
border-radius: 2px;
height: 200px;
width: 350px;
margin: 0 5px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
font-size: 16px;
}
.box:nth-child(even) {
background: #f44336;
}
.box:nth-child(odd) {
background: #2196F3;
}
.shelf:hover button.directional {
opacity: 1;
}
.shelf:hover button.directional:hover {
background: rgba(55, 71, 79, 0.8);
}
*[hidden] {
display: none;
}
<div class="shelf">
<button class="directional left">‹</button>
<div class="boxes">
<div class="box">test</div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
<button class="directional right">›</button>
</div>
Link to JSFiddle
Add vertical-align: top; to your .box.
This will get your inline block elements to align themselves vertically across their top points.
Problem with inline-block elementsis, most browsers including IE will add 1px around inline and inline-block elements.
https://davidwalsh.name/remove-whitespace-inline-block
https://css-tricks.com/fighting-the-space-between-inline-block-elements/
https://matthewlein.com/articles/inline-block-no-space-font/
https://tylercipriani.com/blog/2012/08/01/display-inline-block-extra-margin/
This happens if you stack your inline-block elements. Meaning, in order to keep your code more readible if you put line breaks in html before inline-block elements, you will get this gap and it will cause unwanted UI.
So basically you need to set font-size:0 to wrapper element then set your font-size back to normal in inline-block elements if you have to keep them in separate lines like
<li>Some content<li>
<li>Some content<li>
<li>Some content<li>
or you need to merge them in single line
<li>Some content<li><li>Some content<li><li>Some content<li>
my name is Daniel and i'm making a drinking game for school, I want to let a div to become visible when the bar is full (so you know when the bar is full and you win the game), but i have no idea how to do this...
Could you help me out?
HTML:
<div class="col-xs-12" style="display: none;" id="hiddenText">
<div id="bar" class="animated bounceInUp">
</div>
</div>
CCS:
#bar {
background-color: #F8F8F8 ;
width: 340px;
height: 24px;
margin-right: auto;
margin-left: auto;
}
#bar > div {
margin-top: 30px;
max-width: 334px;
width: 100%;
height: 16px;
background: #9d3349;
position: relative;
top: 4px;
left: 3px;
transition: width 500ms;
}
JS:
var jumpsize = 2.77, // %
body = $("body");
(container = $("#bar")), (bar = container.children("div")), (topcnt = function(
px
) {
return 100 * px / container.width();
}), (set = function(pcnt) {
bar.css({ width: pcnt + "%" });
});
body
.on("click", ".card1, .card2, .card3, .card4", function() {
set(topcnt(bar.width()) + jumpsize);
});
set(0);
The reason its not working is because u forgot to put the if statement in the function u run on click. So the if statement only runs once. and on first load it will result in false. To fix your code move the if statement in your Body.onclick.
Next time it would be smart to include the full javascript that is relative to the function.
By looking at the online code i was able to find the issue.
Hope this resolves your issues.
~Yannick
When you hit your target you need to remove the CSS styling of Display = none.
W3 schools page here for some helpful info to help you learn some more.
https://www.w3schools.com/jsref/prop_style_display.asp
The line below inserted when you reach your goal to display should make the bar appear.
document.getElementById("hiddenText").style.display = "block";
I'm not sure you want this, but try this:
var jumpsize = 2.77, // %
width = 0,
body = $("body");
(container = $("#bar")), (bar = container.children("div")), (topcnt = function(
px
) {
return 100 * px / container.width();
}), (set = function(pcnt) {
bar.css({ width: pcnt + "%" });
if(pcnt >= 100) {$('#hiddenText').show();}
});
body
.on("click", ".card1, .card2, .card3, .card4", function() {
width += jumpsize;
set(topcnt(width));
});
set(0);
#bar {
background-color: #F8F8F8 ;
width: 340px;
height: 24px;
margin-right: auto;
margin-left: auto;
}
#bar > div {
margin-top: 30px;
max-width: 334px;
width: 100%;
height: 16px;
background: #9d3349;
position: relative;
top: 4px;
left: 3px;
transition: width 500ms;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="col-xs-12" style="display: none;" id="hiddenText">
<div id="bar" class="animated bounceInUp">
<div></div>
</div>
</div>
<button class="card1">click me</button>
You are using jQuery so quicker will be:
$('#hiddenText').show();
Edit:
sooo
if($('#bar').children('div').width() >= 334){
$('#hiddenText').show();
}
As You can see the div with progress bar can have max od 334 px. Check if it has it and if yes then show the text. Put this in that click event
Seems to me like you're overcomplicating things a little bit with the percentage calculations. I would just add a variable for the width of the bar that starts at 0 and increase this with the jumpsize on every click. Once this new variable goes over or equals 100 you show the hidden div.
HTML
<div class="col-xs-12" id="hiddenText">
<div id="bar" class="animated bounceInUp">
<div></div>
</div>
</div>
<button id="button">Click me</button>
<div id="showOnComplete">Show me when the bar is full!</div>
CSS
#bar {
width: 340px;
height: 24px;
padding: 4px 3px;
margin: 30px auto 0;
background-color: #f8f8f8;
}
#bar > div {
position: relative;
float: left;
height: 100%;
width: 0;
max-width: 100%;
background: #9d3349;
transition: width 500ms;
}
#button {
margin: 20px auto;
display: block;
}
#showOnComplete {
width: 400px;
padding: 20px;
margin: 20px auto;
background: blue;
color: #fff;
text-align: center;
display: none;
}
JS
(function($) {
var jumpSize = 20, //increased this for the fiddle, so we don't have to click as often
barWidth = 0,
$bar,
$showOnComplete;
$(function() {
$bar = $("#bar").children("div");
$showOnComplete = $("#showOnComplete");
$(document).on("click", "#button", function() {
barWidth += jumpSize;
$bar.width(barWidth + "%");
if (barWidth >= 100) $showOnComplete.show(); //optionally add a setTimeout of 500 here to account for the final transition of the bar
});
});
})(jQuery);
I've made a fiddle for it here.
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>