How do I make stacking cards with CSS and JS? - javascript

I created the effect I want below:
const container = document.getElementById("container");
const boxes = document.querySelectorAll(".box");
const clearBtn = document.getElementById("clear-button");
const spread = () => {
boxes.forEach((box) => box.classList.remove("selected"));
boxes.forEach((box, i) => {
box.style.left = `${i * 116 + 16}px`;
});
};
const select = (index) => {
spread();
console.log(`selecting ${index}`);
boxes[index].classList.add("selected");
boxes.forEach((box, i) => {
box.style.left = `16px`;
});
};
clearBtn.addEventListener("click", spread, false);
spread();
#container {
display: flex;
}
.box {
position: absolute;
font-size: 64px;
display: flex;
align-items: center;
justify-content: center;
height: 150px;
width: 100px;
top: 5vh;
z-index: 1;
transition: left 1s;
cursor: pointer;
}
.selected {
z-index: 2;
}
.one {
background-color: cyan;
}
.two {
background-color: magenta;
}
.three {
background-color: yellow;
}
button {
position: absolute;
outline: none;
border: none;
background-color: green;
color: white;
padding: 8px;
border-radius: 4px;
top: 185px;
left: 16px;
}
<div id="container">
<div onclick="select(0)" class="box one">1</div>
<div onclick="select(1)" class="box two">2</div>
<div onclick="select(2)" class="box three">3</div>
</div>
<button id="clear-button">Spread</button>
However, the only way I've been able to manage it is with absolute positioning, which I need to avoid so that this component can be manipulated elsewhere on the DOM.
The idea is that I have a collection of "cards." When one is clicked, all of them slide all the way to the left, leaving the selected card on top. Also, it would be ideal to have a way to reverse the process as well.
I wish very badly that I could do this with CSS grid, and it may be possible with Firefox, but that is currently the only browser that supports animating grid-template-columns.
Ultimately this will need to be done in React, but I am happy to see any answers using any other framework! If there are any libraries that might accomplish this, that would be great too, but again, this will be in React at the end of the day, so that may limit what's available.
This is the only way I've found to accomplish this.
Note: This question is asking a very similar question, but without the movement, and the answer seems to be absolute positioning anyway.

Related

Text in hover change color just where the mouse is [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed last month.
Improve this question
How can I change the color of a text just where the mouse is and not in all the text?like this
I want that the circle that you can see follow the mouse and on hover the text show just the border of the section of the letter that is in hover
I need an advice about which library or function I need to use?
const fx = document.querySelector(".fx");
document.addEventListener("pointermove", e => {
fx.style.top = e.clientY + "px";
fx.style.left = e.clientX + "px";
fx.querySelector("h1").style.transform = `translate(${(e.clientX - fx.getBoundingClientRect().width / 2) * -1}px, ${(e.clientY - fx.getBoundingClientRect().height / 2) * -1}px)`;
})
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
.container {
position: relative;
}
.container h1 {
font-size: 10rem;
color: red;
}
.container .fx {
width: 100px;
height: 100px;
outline: 1px solid blue;
border-radius: 50%;
position: absolute;
top: -100px;
left: -100px;
overflow: hidden;
transform: translate(-50%, -50%);
background-color: white;
}
.container .fx h1 {
font-size: 10rem;
color: transparent;
-webkit-text-stroke: 2px green;
cursor: default;
position: relative;
}
<div class="container">
<h1>abc</h1>
<div class="fx">
<h1>abc</h1>
</div>
</div>
this is just a demo. this will need further logic if you implement this in your project. you will need some two-dimensional knowledge. it is kinda hard to explain in texts. i will not explain css because you might already know them. you will need two tags to hold the text (in demo is h1), one's for display and the other's for the effect (in the fx div). important! you need to the precise position/location in pixel of the container div and the fx background color need to be the same as the parent background color. to build the effect, you need to add mousemove event to the parent of the container (in demo is the document). make the fx div follows the cursor by setting the its top and left property to e.clientY and e.clientX. here is the hard part, when the fx div follows the cursor, the h1 inside of it will follow along as well. the effect works when the fx's h1 stay still in place and stack on top of the container's h1. to do it you need to translate the fx's h1 out of the fx by setting the fx's h1's translate to the cursor position subtraction the container position (fx.querySelector("h1").style.transform = ...). in the demo is easy because the container position is basically x: 0 and y: 0 and i just use e.clientX * -1 and e.clientY * -1. i suggest you should copy and run the demo line by line to understand more.
Update
i've made improvement of the demo and make it more convenient to be implemented. to implement, just copy/paste and change h1's css to your liking.
const glass = document.querySelector(".magnifying-glass"),
container = document.querySelector(".container"),
cInfo = container.getBoundingClientRect();
document.addEventListener("pointermove", e => {
glass.style.opacity = 1;
glass.style.transform = `translate(
${e.clientX - cInfo.x - (glass.getBoundingClientRect().width / 2)}px,
${e.clientY - cInfo.y - (glass.getBoundingClientRect().height / 2)}px
)`;
glass.querySelector("h1").style.transform = `translate(
${(e.clientX - (glass.getBoundingClientRect().width / 2) - cInfo.x) * -1}px,
${(e.clientY - (glass.getBoundingClientRect().height / 2) - cInfo.y) * -1}px
)`;
})
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
display: flex;
height: 100vh;
justify-content: center;
align-items: center;
}
.container {
position: relative;
}
.container .display {
font-size: 10rem;
color: red;
}
.container .magnifying-glass {
width: 100px;
height: 100px;
outline: 1px solid blue;
border-radius: 50%;
position: absolute;
top: 0;
left: 0;
overflow: hidden;
transform: translate(-50%, -50%);
background-color: white;
opacity: 0;
}
.container .magnifying-glass .magnify-fx {
font-size: 10rem;
color: transparent;
-webkit-text-stroke: 2px green;
cursor: default;
}
<div class="container">
<h1 class="display">abc</h1>
<div class="magnifying-glass">
<h1 class="magnify-fx">abc</h1>
</div>
</div>

Navbar change Color using Observer

Hey out there reading,
i'm making a webpage and im relativly new to JavaScript. I want the Navbar to change Color when its in section Two, while in Section One and Three the Navbar should have the same Color. I watched some tutorials and was able with that code to change the color of the navbar so that in section one and section two the navbar has the right color. When trying out the variables sectionTwo and sectionThree (in order to get the navbars Color to switch back to the color in sectionOne when entering sectionThree) on the other hand it didnt change the colors at the right position (like 100px befor the section). I dont know why this problem accures. If someone knows how to fix it, it would mean the world to me :).
const header = document.querySelector("header")
const sectionOne = document.querySelector(".one")
const sectionTwo = document.querySelector(".two")
const sectionThree = document.querySelector(".three")
const sectionOneOptions = {
}
const sectionOneObserver = new IntersectionObserver(function(entries, sectionOneObserver) {
entries.forEach(entry => {
if (entry.isIntersecting) {
header.classList.add("nav-scrolled")
} else {
header.classList.remove("nav-scrolled")
}
});
},
sectionOneOptions);
sectionOneObserver.observe(sectionTwo)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Arial";
}
body {
width: 100vw;
height: 100vh;
margin: 0;
background: #000;
}
header {
--text: #fff;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 8rem;
z-index: 10000;
padding: 0 1rem 5rem;
display: flex;
justify-content: space-between;
align-items: center;
}
header nav {
margin: auto;
display: flex;
justify-content: center;
align-items: center;
}
header nav li {
flex: 0 0 auto;
list-style: none;
margin-left: 1rem;
}
header nav li a {
text-decoration: none;
padding: 6px, 15px;
color: var(--text);
border-radius: 20px;
}
.nav-scrolled {
--text: #000;
}
.one {
position: relative;
width: 100%;
height: 100vh;
padding: 200px 20vw;
display: flex;
}
.two {
height: 100vh;
position: relative;
padding: 100px 20vw;
background: #fff;
}
.three {
position: relative;
padding: 100px 20vw;
color: #fff;
height: 100vh;
}
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<header>
<nav>
<li>one</li>
<li>two</li>
<li>three</li>
</nav>
</header>
<section>
<div class="one" id="one"></div>
</section>
<section>
<div class="two" id="two"></div>
</section>
<section>
<div class="three" id="three"></div>
</section>
<script src="app.js"></script>
</body>
</html>
Let me know if you need some more Code and not only the snippet to fix this.
Hopefully some hero can help me :).
Greetings
Noel
I did research to try and answer this question, so please take the preface that I am not an expert in IntersectionObserver.
With that preface out of the way,
There were many things I changed from your example to create the desired effect, however the core feature that you would have needed to implement in your example is the option for intersection observers, rootMargin. By adding this option you can give the item on the screen a negative top and bottom margin. This is important, because you do not want the observer to fire until the header is about to cross into the observed section, and you dont want the header to change until just before it crosses back into the next section.
The way I chose to emulate this behavior, is by using the rootMargin option to give a negative margin. This makes the actual element observed begin however many pixels after we specify. For example, an element that is 100px in height, with "0px 0px -20px 0px" as the value for rootMargin in the options object, would not trigger the IntersectionObserver until 20px of the element was scrolled into the viewport height.
With this understanding, we can define our goal. We want to preform an action when the observed element is about to touch the header. We can describe this as the viewport height minus the header height in a negative margin to the bottom would adjust the element just enough to trigger the intersection observer as desired. Because the methods of obtaining margins are not exact, I subtract one from the numbers calculated to adjust for small error. If we set both margins to overlap eachother, we will never have the observer fire.
I also decided to use css variables and set the value of the variable depending on whether or not the element observed scrolled into or out of the viewport.
This code is not able to execute properly in a stack snippet because the viewport option that the intersection observer defaults to in the options is not set correctly for the snippet environment. I have not tested this code in environments that resize.
Hopefully that explanation helps you understand this code. Let me know if you have any questions.
relevant html:
<header>
<nav>
<li>one</li>
<li>two</li>
<li>three</li>
</nav>
</header>
<section id="one">
</section>
<section id="two">
</section>
<section id="three">
</section>
<script src="app.js"></script>
relevant css:
:root {
--header-text-color: white;
}
body {
margin: 0;
}
header {
position: fixed;
top: 0;
width: 100%;
}
header nav {
display: flex;
justify-content: center;
align-items: center;
}
header li {
list-style: none;
padding: 1rem;
font-size: 2rem;
}
header a {
color: var(--header-text-color);
text-decoration: none;
}
section {
height: 120vh;
background: black;
}
#two {
background: white;
}
relevant js:
const header = document.querySelector("header");
const sectionTwo = document.querySelector("#two");
const topMargin = header.offsetHeight - 1;
const bottomMargin = window.innerHeight - header.offsetHeight - 1;
const options = {
rootMargin: `-${topMargin}px 0px -${bottomMargin}px 0px`,
}
const observer = new IntersectionObserver(([entry]) => {
const color = entry.isIntersecting ? "black" : "white";
document.documentElement.style.setProperty('--header-text-color', color);
}, options);
observer.observe(sectionTwo);

Display a menu when hovering on the last character in the input in React

I am currently working on a form using React-Bootstrap that has a body that takes a message. This message shall include some tags selected from a dropdown menu. The thing is, this dropdown is to be displayed when the user enters an & and then, the user chooses an option from the menu that will replace the ampersand. I handled almost everything except the part of displayed the menu. How can I always show the menu at the last character entered by the user and also, how can I display it when hovering on this character. For example,
"Hi Dear, today we are going to work on &[The menu has to be displayed here when hovering on the ampersand]"
To be honest, I am quite confused and I just need to figure out how I should start. If there could be anything that helps, I'd be very grateful. Thanks in advance.
Regards.
Here is something i came up with.
To achieve this, first thing i thought of was to detect the caret position inside input or textarea.
I found this library and it worked perfectly.
https://github.com/bubkoo/get-cursor-position
Next task was to figure out how to place a menu inside input or textarea. For that i simply made a parent div and i set input 100% width and height of it & then we can place using any html tag inside that div using position: absolute and it will work like we adding stuff inside input tag.
The menu was added with top & left i got using that library. Rest of the code is basic, you can simple figure that out just checking it below.
Here is the code:
const menu = document.querySelector('.menu')
function showMenu(element) {
let lastChar = element.value[element.value.length - 1],
caret = getCaretCoordinates(element, element.selectionEnd);
if (lastChar === "&") {
menu.style.display = 'block';
menu.style.left = caret.left + 20 + 'px'
menu.style.top = caret.top + 20 + 'px'
} else {
menu.style.display = 'none'
}
}
function copyText(text) {
const input = document.querySelector('#input')
input.value += " " + text
menu.style.display = 'none'
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: Arial, Helvetica, sans-serif;
transition: all 0.3s ease;
}
body {
height: 100vh;
width: 100vw;
display: grid;
place-content: center;
background-color: blueviolet;
}
.parent {
position: relative;
width: 500px;
height: 120px;
}
#input {
background-color: white;
width: 100%;
height: 100%;
border: none;
border-radius: 10px;
padding: 20px;
}
#input:focus {
border: 0;
outline: none;
}
.menu {
display: none;
position: absolute;
background-color: black;
color: white;
width: 100px;
}
.menu li {
text-align: center;
cursor: pointer;
height: 60px;
list-style: none;
display: grid;
place-content: center;
padding: 10px;
}
li:hover {
background: white;
;
color: black;
border: 1px solid black
}
<div class="parent">
<textarea id="input" oninput="showMenu(this)"></textarea>
<div class="menu">
<li onclick='copyText(this.innerText)'>Mango</li>
<li onclick='copyText(this.innerText)'>Banana</li>
<li onclick='copyText(this.innerText)'>Apple</li>
<li onclick='copyText(this.innerText)'>Orange</li>
<li onclick='copyText(this.innerText)'>Grapes</li>
</div>
</div>
<script src="https://rawgit.com/component/textarea-caret-position/master/index.js"></script>

Sticky header implementation with IntersectionObserver

I'm doing sticky header implementation - I dont want to use position: sticky; because I dont see good polyfill for it so I'm trying to use IntersectionObserver API instead. The problem is - I dont really like the behaviour it provides as its entries could be emitted with random delay. This solution works fine if you scroll gradually, but if you scroll faster the header goes up and only after delayed response it gets fixed class and gets positioned with significant jump especially on heavy pages. Any ways to achieve better response or maybe IntersectionObserver is not good for my purposes?
const header = document.querySelector('.header');
const headerPositionNotifier = document.createElement('div');
headerPositionNotifier.classList.add('header-position-notifier')
header.parentNode.insertBefore(headerPositionNotifier, header);
const options = {
threshold: [0]
};
const callback = (entries) => {
entries.forEach(({
intersectionRatio
}) => {
if (intersectionRatio == 0) {
header.classList.add('fixed')
} else {
header.classList.remove('fixed')
}
})
}
const io = new IntersectionObserver(callback, options);
io.observe(headerPositionNotifier);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
text-align: center;
font-family: Arial;
color: white;
letter-spacing: 10px;
}
.banner {
background: orange;
padding: 20px;
}
.parent {
background: mediumvioletred;
position: relative;
}
.header {
background: salmon;
padding: 40px 0;
}
.content {
background: #444;
padding: 40px 0;
height: 2000px;
}
.fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
}
.header-position-notifier {
height: 1px;
}
<div class="banner">
BANNER
</div>
<div class="parent">
<div class="header">
HEADER
</div>
<div class="content"></div>
</div>
If you can't use CSS for this, than you out of luck. The delay just comes from the delay from scrolling events which you can't make go away. As you said, on heavy pages it gets worse. Even on different browser it's more or less worse. I would prefer to combine css position sticky and as fallback the intersection observer.

Adding non-centered text shifts centered elements [duplicate]

This question already has answers here:
How to prevent scrollbar from repositioning web page?
(26 answers)
Closed 4 years ago.
After putting a centered header, I add a non-centered output with JS. After the output is produced, the header shifts a bit left. What can be done to tackle this problem?
let output = [];
function spit() {
for (let i = 0; i < 100; i++) {
output.push(i);
}
document.getElementById("output").innerHTML =
output.join("<br>");
}
.header {
background-color: lightgray;
border: none;
color: black;
padding: 17px 25px;
display: block;
text-align: center;
font-size: 36px;
width: 100%;
margin-left: auto;
margin-right: auto;
}
<h2 id="dictName" class="header">
Testing Page
</h2>
<button style="font-size:20pt;height:35pt" onclick="spit()">
Press me!
</button>
<p id="output">
</p>
One crazy solution might be to set you body height to the view port height, that way you start off with a scroll, avoiding the shift when the button gets pressed.
let output = [];
function spit() {
for (let i = 0; i < 100; i++) {
output.push(i);
}
document.getElementById("output").innerHTML =
output.join("<br>");
}
body {
min-height: 100vh;
}
.header {
background-color: lightgray;
border: none;
color: black;
padding: 17px 25px;
display: block;
text-align: center;
font-size: 36px;
width: 100%;
margin-left: auto;
margin-right: auto;
}
<h2 id="dictName" class="header">
Testing Page
</h2>
<button style="font-size:20pt;height:35pt" onclick="spit()">
Press me!
</button>
<p id="output">
</p>
I added a universal { margin:0; padding:0;} to your css code. The code did seem to be centered but I think the margin of -50 (that's being created by the auto margin ) is throwing off the look.
let output = [];
function spit() {
for (let i = 0; i < 100; i++) {
output.push(i);
}
document.getElementById("output").innerHTML =
output.join("<br>");
}
* {
margin: 0px; padding:0px;
}
button {
/*margin-left:15px;*/
margin-top:7px;
font-size: 20pt;
height: 35pt;
}
.header {
background-color: lightgray;
/* border: 15px solid white;*/ /*use the commented props if you still want the "indented" effect" */
color: black;
padding: 17px 25px;
display: block;
text-align: center;
font-size: 36px;
width: 100%;
margin-right:auto;
margin-left:auto;
}
<h2 id="dictName" class="header">
Testing Page
</h2>
<button onclick="spit()">
Press me!
</button>
<p id="output">
</p>
IF you don't want the h1 to shift due to the scrollbar, you would have to calculate, using css calc() (and maybe some other things too), 50vw - (widthOfH1/2). This works because the vw unit (viewport width) is not affected by the scrollbar.
One way for the scrollbar to not affect the centering of the h1 would be to use JQuery.
$(#dictName).style.marginLeft = 'calc(50vw -'+(this.width/2)+'px)';
I haven't tested this so I'm not 100% sure if it will work, but please tell me if it does or doesn't. You may need to rerun this code when the button is pressed.
After some googling it seems I found the easiest way to solve this problem here, on SO:
How to prevent scrollbar from repositioning web page?
html {
overflow-y: scroll;
}
Probably, the question should be closed as duplicate, but I don't have enough reputation to do it.

Categories

Resources