How to design the notification container in CSS? - javascript

I have a task - to make a notification display for some web-application.
The notifications appear in the bottom right corner of the window and disappear after some time.
Or the notifications can be gone by clicking on a notification item.
If there are multiple notifications, they should be stacked.
Each new notification moves the notification stack up.
If the stack of notifications grows larger than the height of the window, then the top older notifications must move to the next stack, which must be displayed to the left of the newer notification stack.
Similarly, if the second stack reaches the height of the window, then the oldest notifications should move to the third stack.
Schematically, it should look like this:
I was able to obtain the correct order of displaying notifications, but I can't get the notification container to be positioned against the right edge of the window.
function notify(msg) {
let element = document.createElement('div');
element.classList.add('notification');
element.innerHTML = msg;
let timeout;
element.onclick = () => element.remove();
element.onmouseenter = () => clearTimeout(timeout);
element.onmouseleave = () => createTimeout();
createTimeout();
let recentElement = container.firstElementChild;
if(recentElement) recentElement.before(element);
else container.appendChild(element);
indicator.innerHTML='Last msg is '+msg;
function createTimeout() {
timeout = setTimeout(() => {
element.remove()
}, 10000);
}
}
let notifyIndex = 0;
x1.onclick = () => {
notify(++notifyIndex);
}
x10.onclick = () => {
for (let i = 0; i < 10; i++)
notify(++notifyIndex);
};
x30.onclick = () => {
for (let i = 0; i < 30; i++)
notify(++notifyIndex);
};
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
}
.action-list button {
width: 4rem;
margin .5rem
}
#container {
position: fixed;
display: flex;
flex-direction: column-reverse;
justify-content: flex-end;
flex-wrap: wrap-reverse;
align-content: flex-end;
align-items: flex-end;
bottom: 0;
right: 30px;
max-height: 100vh;
}
.notification {
transition: all 500ms ease-in-out;
box-shadow: 0 0 1rem #0004;
margin: 0 1rem 1rem 0;
background: #f9f99f;
color: #000;
border: 1px solid #0008;
padding: .2rem 1rem;
cursor: default;
user-select: none;
}
<div id="container"></div>
<div>
<div id="indicator">Last msg is 0</div>
<div class="action-list">
<button id="x1">x1</button>
<button id="x10">x10</button>
<button id="x30">x30</button>
</div>
</div>
I set an indentation of 30 pixels, so that you can see that the notifications go over the edge of the window.
The idea is that stacks of recent notifications should not go over the edge of the window.
What am I doing wrong?

position: fixed;
display: flex;
flex-direction: column-reverse;
justify-content: flex-end;
flex-wrap: wrap-reverse;
align-items: flex-end;
bottom: 0;
right: 30px;
max-height: 100vh;
Try this, let me know if it works!
Tldr: Your Align-content: flex-end was making it grow to the right

Related

Move DIV using CSS Grid

I created a CSS Grid layout with global variables:
#app{
--width-l: 0.5fr;
--width-c: 0.5fr;
}
So I have a bar in the middle of my screen. On the other hand, in JavaScript I have two events that observe if the mouse is pressed (mousedown) and in motion (mousemove) that move the div. It has a problem, the movement is above the mouse position and at the height of the #app. So it works in parts, when I'm near the top, the div#bar doesn't go up anymore, and near the bottom, the same thing happens but at a greater distance.
I'm looking for a solution to make the transition smoothly, using the grid positions.
This is the code I created to try:
var split = document.querySelector(".split");
var app = document.querySelector("#app");
var position = app.getBoundingClientRect();
var isMouseMove = false;
split.addEventListener("mousedown", (e) => {
isMouseMove = true;
this.addEventListener("mouseup", (e) => {
isMouseMove = false;
});
});
split.addEventListener("mousemove", (e) => {
if (isMouseMove) {
let fullSize = app.offsetHeight;
let average = (100 * (e.y - position.top)) / fullSize;
let up = (average / 100).toFixed(4);
let down = (1 - average / 100).toFixed(4);
app.style.setProperty("--width-l", `${up}fr`);
app.style.setProperty("--width-c", `${down}fr`);
}
});
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
header {
background: aquamarine;
height: 50px;
}
footer {
background: aqua;
height: 50px;
}
#app {
--width-l: 0.5fr;
--width-c: 0.5fr;
height: calc(100vh - 100px);
display: grid;
grid-template: "aside up" var(--width-l) "aside split" 50px "aside down" var(
--width-c
) / 100px auto;
}
#app .aside {
grid-area: aside;
background: blue;
}
#app .up {
grid-area: up;
background: yellow;
resize: horizontal;
}
#app .split {
grid-area: split;
background: floralwhite;
display: flex;
justify-content: center;
align-items: center;
font-weight: 900;
color: tomato;
user-select: none;
}
#app .down {
grid-area: down;
background: green;
}
#app .split:active {
cursor: move;
}
<header></header>
<div id="app">
<div class="aside"></div>
<div class="up"></div>
<div class="split"> CLICK AND MOVE </div>
<div class="down"></div>
</div>
<footer></footer>

Implementing animations on an array of string values

My goal is to try to animate text. Every example on how to animate text looks at a singular string. I'd like to animate an array of string values, one after the other.
The problem I'm getting is that although I'm creating the elements needed successfully, only the .word3 class is actually being rendered. I'm not sure why this is and after banging my head against the wall, I'm not sure how to fix it.
I've used a lot of stack overflow resources to overcome things like using setTimeout in a for loop which lead to using an iife.. Here is the code that I've settled on for the time being. Using async caused a lot of issues as async needs to be at the top level apparently and I often got the 'unexpected reserved keyword error'.
There must be a simple way to do this?
All help is appreciated, thanks!
const wrapper = document.querySelector(".wrapper");
const buttonCreate = document.querySelector(".create-element");
const buttonAnimate = document.querySelector(".animation");
buttonCreate.addEventListener("click", createElement);
let i = 0;
let j = 0;
let sampleArray = ["Just", "another", "cool", "heading"];
function createElement() {
// debugger
// Create text wrapper in main body and add class
const newDiv = document.createElement("div");
newDiv.classList.add("text-wrapper");
wrapper.insertAdjacentElement("afterbegin", newDiv);
// Modifying text-wrapper html to include p tag with dynamic class
function word() {
sampleArray.map((word, i) => {
newDiv.innerHTML += `<p class="word${i}"></p>`;
let element = document.querySelector(`.word${i}`);
console.log(element);
let j = 0
let interval = setInterval(() => {
element.innerText += word[j];
j++;
if(j === word.length) {
clearInterval(interval)
}
}, 200)
});
};
word();
return;
}
html {
box-sizing: border-box;
}
*,*::before, *::after {
box-sizing: inherit;
}
body {
margin: 0;
}
.wrapper {
width: 100%;
background-color: #333;
color: #FFA;
text-align: center;
height: 100vh;
display: flex;
flex-direction: row;
gap: 10rem;
align-items: center;
justify-content: center;
font-size: 4rem;
position: relative;
}
.text-wrapper {
position: absolute;
top: 0;
width: 100%;
display: flex;
gap: 3rem;
flex-direction: column;
justify-content: center;
justify-content: space-around;
}
.button {
font-size: 3rem;
border-radius: 6px;
background-color: #47cefa;
}
.button:hover {
cursor: pointer;
background-color: #BCEF4D;
}
<section class="wrapper">
<button class="button create-element">Create Element</button>
<button class="button animation">Start Animation</button>
</section>
Instead of setInterval in a loop of the array, just create a recursive function that calls setTimeout and calls itself and increments the array counter until the end of the array.
The lay out of my answer is off because I'm not exactly sure on what your expected layout is
const wrapper = document.querySelector(".text-wrapper");
const buttonAnimate = document.querySelector(".animation");
buttonAnimate.addEventListener("click", animation);
let i = 0;
let sampleArray = ["Just", "another", "cool", "heading"];
function animation() {
if (i < sampleArray.length) {
let el = document.createElement("p");
el.innerHTML = sampleArray[i];
wrapper.appendChild(el);
i++;
setTimeout(animation, 200);
}
}
.wrapper {
width: 100%;
background-color: #333;
color: #FFA;
text-align: center;
height: 100vh;
display: flex;
flex-direction: row;
gap: 10rem;
align-items: center;
justify-content: center;
font-size: 4rem;
position: relative;
}
.text-wrapper {
position: absolute;
top: 0;
width: 100%;
display: flex;
gap: 3rem;
flex-direction: column;
justify-content: center;
justify-content: space-around;
}
.button {
font-size: 3rem;
border-radius: 6px;
background-color: #47cefa;
}
.button:hover {
cursor: pointer;
background-color: #BCEF4D;
}
<section class="wrapper">
<button class="button animation">Start Animation</button>
<div class="text-wrapper"></div>
</section>

How to rotate and scale cards while swiping

As I'm trying to make a carousel that rotates I wonder how to move the cards rotated and it scales from a minimum of 0.8(left and right cards) and a maximum scale of 1(center card) when the user keeps swiping
Scale:
left = 0.8
center = 1
right = 0.8
I'm trying to solve on how to rotate them using transform and z-index properties. The cards will also rotate however I'm still trying to make a formula on how to make a function that makes the cards rotate
Any alternative solutions are accepted The animation is
similar to this carousel from codepen however it doesn't swipe Carousel Rotate
const CONTAINER_FLEX = document.querySelector('.container-flex');
const items = document.querySelectorAll('.item');
let touchStartX = 0;
let touchMoveX = 0;
let count = 0;
let current_translate = 0;
let previous_translate = 0;
CONTAINER_FLEX.addEventListener('touchstart', (event) => {
touchStartX = event.touches[0].pageX;
});
CONTAINER_FLEX.addEventListener('touchmove', (event) => {
touchMoveX = event.touches[0].pageX;
current_translate = previous_translate + (touchMoveX - touchStartX);
console.log(current_translate);
items[1].style.transform = `translateX(${current_translate}px)`;
});
CONTAINER_FLEX.addEventListener('touchend', () => {
current_translate = touchMoveX - touchStartX;
previous_translate = current_translate;
});
*,
::before,
::after {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
font-family: Arial, Helvetica, sans-serif;
line-height: 1.5;
background-color: #131b24;
}
.main-container {
padding: 30px 0;
height: 300px;
width: 900px;
border-top: 1px solid #444;
border-bottom: 1px solid #444;
overflow: hidden;
background-color: white;
}
.container-flex {
height: 100%;
display: flex;
transition: transform 400ms cubic-bezier(0.165, 0.84, 0.44, 1);
}
.item {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
min-width: 300px;
max-width: 300px;
}
.item h1 {
font-size: 40px;
color: white;
}
/* ITEMS */
.item-1 {
background-color: #2c3e50;
transform: translateX(100px);
z-index: 1;
}
.item-2 {
background-color: #3498db;
z-index: 2;
}
.item-3 {
background-color: #1abc9c;
transform: translateX(-100px);
z-index: 1;
}
<div class="main-container">
<div class="container-flex" id="container-flex">
<div class="item item-1">
<h1>1</h1>
</div>
<div class="item item-2">
<h1>2</h1>
</div>
<div class="item item-3">
<h1>3</h1>
</div>
</div>
</div>
Here is a working example https://jsfiddle.net/4ue5sgm9/3/
I wonder how to move the cards when it goes from 0 to 200 however
Have all your cards in an array.
var cards = [ thing1, thing2, thing3];
Use % - modulus operator, it's the secret for cycling back to the beginning
cardIndex = (cardIndex + 1) % cards.length
Copied from the chat. I made it verbose for clarity
scrollLeft() {
nextIndex = items.indexOf(displayed[2])
nextIndex = ++nextIndex % items.length-1
displayed = items[nextIndex]
nextIndex = ++nextIndex % items.length-1;
displayed.push( items[nextIndex] );
nextIndex = ++nextIndex % items.length-1;
displayed.push( items[nextIndex] )
}
P.S. Write and iterator for items array. An iterator stops after the last item. An iterator is why "for (x in thisArray)" works. BUT write the next() function to return "% this.length-1" instead => now you have a never-ending looper. All that code just above goes away.

Change a forEach() method to a For Loop to use with IntersectionObserver - Javascript

I have the following IntersectionObserver code that is working as a scroll trigger for animations on a site that all works OK.
However, I would like to switch the forEach() method that works as the call for the IntersectionObserver to a for loop, but I can't get this to work.
I'm sure that this can be done, but it's driving me a bit crazy.
The reason for wanting this is because I'm using a polyfill so that IntersectionObserver works in older versions of IE and Edge, but of course the forEach() method doesn't work in these browsers.
I've commented out my attempt at the loop at the bottom of the code.
Any help would be amazing.
Codepen: https://codepen.io/emilychews/pen/xJaZay
window.addEventListener("load", function(){
var iO = "IntersectionObserver" in window; /* true if supported */
var box = document.querySelectorAll('.box');
if (iO) {
const config = {
root: null, // sets the framing element to the viewport
rootMargin: '0px',
threshold: .5
};
let observer = new IntersectionObserver(function(entries) {
entries.forEach(function(item) {
if (item.intersectionRatio > .5) {
item.target.classList.add("active");
} else {
item.target.classList.remove("active");
}
});
}, config);
box.forEach(function(item){
observer.observe(item);
});
// for (i = 0; i < box.length; i++) {
// observer[i].observe(item);
// }
} // end of if(iO)
}); // end of load event
body {
font-family: arial;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 280vh;
}
.box {
position: relative;
margin: 1rem 0;
width: 100px;
height: 100px;
background: blue;
opacity: 1;
transition: .5s all;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
}
#box1{
margin-bottom: 100px;
}
.active {
background: red;
opacity: 1;
}
<div id="box1" class="box">Box 1</div>
<div id="box2" class="box">Box 2</div>
You have one observer but you're applying an index on it. As you're iterating over the boxes, your index accessor should be on box.
for (i = 0; i < box.length; i++) {
observer.observe(box[i]);
}
This should work, it's untested though.

Select Multiple Elements with Query Selector Alll - JavaScript

I have an intersectionObserver on a box that changes color when it comes into the viewport which all works fine.
I'm trying to apply this to multiple boxes though, and when I change the the getElementsById to querySelectorAll (line 13 of the JS) it doesn't play ball.
Does anyone know what I'm doing wrong here? I don't think the problem is with the intersectionObserver, I think it's with the selector. It's driving me mad.
Codepen: https://codepen.io/emilychews/pen/RMWRPZ
window.addEventListener("load", function(){
var iO = "IntersectionObserver" in window; /* true if supported */
if (iO) {
const config = {
root: null, // sets the framing element to the viewport
rootMargin: '400px 0px 0px 0px', // should remove the animation 400px after leaving the viewport
threshold: .5
};
const box = document.getElementById('box1');
// const box = document.querySelectorAll('.box');
let observer = new IntersectionObserver(function(entries) {
entries.forEach(function(item) {
if (item.intersectionRatio > .5) {
item.target.classList.add("active");
} else {
item.target.classList.remove("active");
}
});
}, config);
observer.observe(box);
} // end of if(iO)
}); // end of load event
body {
font-family: arial;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 250vh;
}
.box {
margin: 1rem;
width: 100px;
height: 100px;
background: blue;
opacity: 1;
transition: .5s all;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
}
.active {
background: red;
opacity: 1;
}
<div id="box1" class="box">Box 1</div>
<div id="box2" class="box">Box 2</div>
querySelectorAll returns an array of nodes, getElementById returns a single dom object. observer.observe needs a dom object as a parameter, so a solution to that could be
const box = document.getElementsByClassName('box');
box.forEach(function(item){
observer.observe(item);
});

Categories

Resources