Make list items assume height of largest list item [duplicate] - javascript

This question already has answers here:
Equal height rows in CSS Grid Layout
(2 answers)
Closed 4 days ago.
I have a group of list items that are essentially containers with some text content. On mobile screen dimensions, I want the height of the containers to be set automatically according to the text content. However, I want all of the containers to assume the same height. So in this case, each container height would equal the height of the tallest container. The codesandbox link is here.
As you can see in the screenshot below, on a 414 px screen, the third container in the list is taller than the other two. I want all the containers to assume the height of the third one so that they can be the same.
How can I accomplish this? Here is the relevant lines of code:
<div className="App">
<h1>Lender Benefits</h1>
<ul className="list">
{lenderBenefits.map((benefit) => (
<li className="benefit_container">{benefit}</li>
))}
</ul>
</div>
.list {
padding-left: 0;
width: 100%;
}
.benefit_container {
/* height: max-content; */
border-radius: 24px;
margin-bottom: 12px;
padding: 2px 8px;
font-size: 16px;
font-weight: 600;
background-color: #f1e8dc;
display: flex;
align-items: center;
}

The CSS Grid approach (preferred):
const styleForEqualHeightRows = {
display: "grid",
gridAutoRows: "1fr",
gap: "1rem" // <= Optional
}
export default function App() {
return (
<div className="App">
<h1>Lender Benefits</h1>
<ul
className="list"
style={styleForEqualHeightRows}
>
{lenderBenefits.map((benefit) => (
<li
key={benefit}
className="benefit_container"
>
{benefit}
</li>
))}
</ul>
</div>
);
}
Here's one (hacky) approach using JavaScript:
import { useLayoutEffect, useRef, useState } from "react";
import "./styles.css";
const lenderBenefits = [
"No repayment, just reduced margins through credit",
"A cash advance without predatory interest",
"Works with your community to bring them in on your mission"
];
export default function App() {
const list = useRef();
const [style, setStyle] = useState({});
useLayoutEffect(() => {
async function handleResize() {
await setStyle({}); // Break from batch updates...
const lis = list.current.querySelectorAll("li");
const heights = [...lis].map((li) => li.offsetHeight);
const maxHeight = Math.max(...heights);
setStyle({ height: maxHeight + "px" });
}
handleResize();
window.addEventListener("resize", handleResize);
return function cleanUp() {
window.removeEventListener("resize", handleResize);
};
}, []);
return (
<div className="App">
<h1>Lender Benefits</h1>
<ul ref={list} className="list">
{lenderBenefits.map((benefit) => (
<li key={benefit} style={style} className="benefit_container">
{benefit}
</li>
))}
</ul>
</div>
);
}

Related

How do I create an overlay in React?

I am currently trying to create an overlay on an image when hovering. I am able to get a box displayed on screen but it's not placed over the image.
featured.js
const Featured = ({ images }) => {
if (!images || !Array.isArray(images)) return null;
return (
<section className={styles.featuredWrapper} id="work">
{images.map((image) => {
return (
<div className={styles.wrap}>
<GatsbyImage
image={image.gatsbyImageData}
alt="Link to the alt text"
className={styles.featuredImg}
/>
<div className={styles.featuredOverlay}>Test</div>
</div>
);
})}
</section>
);
};
featured.module.css
.featuredImg {
width: 100%;
position: relative;
}
.featuredOverlay {
position: absolute;
background: black;
opacity: 0.5;
width: 100%;
height: 100%;
z-index: 1;
}
Every explanation I see revolves around the use of positions absolute and relative which makes me think my issue is how I am rendering my component. Am I using the position properties on the wrong elements?
import { useState } from "react";
import "./styles.css";
function Home() {
const [showOverlay, setShowOverlay] = useState(false);
return (
<div>
<div className="main-container">
<div className="main-container__grid">
<img
src="https://miro.medium.com/max/2000/1*3SjDVyFY09xZ7NYMO5kj0g.png"
className="test"
alt="Placeholder"
onHover={() => setShowOverlay(true)}
/>
{showOverlay && <div className="targeting-box" />}
</div>
</div>
</div>
);
}
export default Home;

function in list only works on the first list item in react

I'm looping through a list, every list item has a arrow button that can show a hidden div underneath it. But right now every button triggers the first item in the list.
Im working right now in react, but i cant find the right solution
My JS react file
const CardConference = ({ content }) => {
const showItems = () => {
const items = document.getElementsByClassName("hidden-items")[0];
const arrow = document.getElementsByClassName("arrow-down")[0]
if (items.style.display == "block") {
items.style.display = "none";
arrow.classList.toggle('rotate-arrow')
} else {
items.style.display = "block";
arrow.classList.toggle('rotate-arrow')
}
}
return (
<div className="card card-conferences ">
<h4> {content[0].title} </h4>
<ul>
{content[0].cities.map((city) => (
<div>
<li className="city-name"> {city.name}
<button className="btn button-arrow" onClick={showItems} ><FaAngleDown color="#717171" className="arrow-down" /></button>
</li>
<ul className="hidden-items">
{city.conferenties.map((conf) => (
<li className="hidden-item">{conf} </li>
))}
</ul>
</div>
))}
</ul>
</div >
);
}
export default CardConference;
And this is my css
.arrow-down {
position: absolute;
margin-top: -11px;
margin-left: -6px;
transition: transform 1s;
}
.rotate-arrow {
transform: rotate(180deg);
}
..hidden-items {
display: none;
}
Because you only get getElementsByClassName for first element. You need to pass an index to get exactly the element.
const showItems = (i) => () => {
const items = document.getElementsByClassName("hidden-items")[i];
const arrow = document.getElementsByClassName("arrow-down")[i]
...
cities.map((city, index)
...
onClick={showItems(index)}
...

Focused elements display wrong style

I have element with width 400% and I want to move it to left by using translateX(-(index/4)*100%) when focused index changes.
Changing focused element translateX property with tab keyboard button displays it wrong on middle elements (1,2) even though using same hardcoded styling works as expected. What am I missing here?
const {useState} = React;
const App = () => {
const [curr, setCurr] = useState(0);
const carouselStyles = {
transform: `translateX(${-(curr / 4) * 100}%)`
// uncomment to see that styling works fine with hardcoded values 1,2..
// transform: `translateX(${-(1 / 4) * 100}%)`
};
const handleFocus = (num) => {
if (num !== curr) {
setCurr(num);
}
};
console.log(carouselStyles);
return (
<div>
<div className="carousel" style={carouselStyles}>
<div className="item">
11 very long text
<a href="/111" onFocus={() => handleFocus(0)}>
11111
</a>
</div>
<div className="item">
22 very long text
<a href="/222" onFocus={() => handleFocus(1)}>
22222
</a>
</div>
<div className="item">
33 very long text
<a href="/333" onFocus={() => handleFocus(2)}>
33333
</a>
</div>
<div className="item">
44 very long text
<a href="/444" onFocus={() => handleFocus(3)}>
44444
</a>
</div>
</div>
current: {curr}
</div>
);
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
.carousel {
display: flex;
align-items: center;
width: 400%;
}
.item {
flex: 0 1 100%;
text-align: center;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I needed to prevent the scrolling and in my provided example its enough to add this line into handleFocus function
window.scrollTo(0, 0);
But in my real scenario parent wrapper also had overflow: hidden; which prevented above code from working. So I've used refs
const handleFocus = (num) => {
if (num !== curr) {
setCurr(num);
carouselRef.current.scrollTo(0, 0);
}
};
return (
<div ref={carouselRef}>
<div className="carousel" style={carouselStyles}>
...
</div>
current: {curr}
</div>
);

Tabbed dates for api content

in React i am attempting to add my api content to a series of tabs, so you click on a tab, and it will show any listings in the api matching that date.
my attempt, code of component is below
import context from "./apiContext";
import styled from "styled-components";
const Tab = styled.button`
font-size: 20px;
padding: 10px 60px;
cursor: pointer;
opacity: 0.6;
background: white;
border: 0;
outline: 0;
${({ active }) =>
active &&
`
border-bottom: 2px solid black;
opacity: 1;
`}
`;
const ButtonGroup = styled.div`
display: flex;
`;
const tabs = ["18-08-20", "19-08-20", "20-08-20"];
function Movies() {
const { films } = useContext(context);
console.log(films);
const [active, setActive] = useState(tabs[0]);
return (
<>
<div className="movies">
<div className="title">
<h1>
Movies: <span />
All Releases
</h1>
<div className="tab-menu">
<ButtonGroup>
{tabs.map((tab) => (
<Tab
key={tab.label}
active={active === tab}
onClick={() => setActive(tab)}
>
{tab.label}
</Tab>
))}
</ButtonGroup>
</div>
</div>
<div className="content">
{films
.filter((item) => item.PreShowStartTime === Date.parse({ active }))
.map((item, index) => (
<div class="card" key={index}>
<div class="title">
<span class="title">{item.Title}</span>{" "}
<span>
{new Date(item.PreShowStartTime).toLocaleDateString("en", {
day: "2-digit",
month: "short"
})}
</span>
</div>
</div>
))}
</div>
</div>
</>
);
}
export default Movies;
To view with live API:
https://codesandbox.io/s/strange-hoover-8hik2
On line 24, is my test array of dates, which also should show as the label of the tab.
You are going to have to format the dates returned by the API according to what you have defined in the tabs constant in your code (or vice versa). Currently your API returns example 2020-08-18T14:00:00 but your tabs has format 18-08-20.
So for that, on your filter condition, you can use something like:
new Date(item.PreShowStartTime).toJSON().slice(2, 10) === active
where active is the active date selected on the tab
https://codesandbox.io/s/reverent-yonath-pv8i0?file=/src/Movies.js:1293-1357
As a side note, you were also accessing tab.label to print your tab labels. tabs does not contain a property named label...

Is it possible to pass dynamic props, from one page to another with next.js?

I'm new to Next and have been trying to make a page(index.js) that fetches data(countries) and then displays that data, where each returned element(country) has a button to go to a page(info.js) where that specific countries data will be displayed, was wondering if its possible to pass the props(all country data) to the info.js page? I've tried reading the documentation and watching YT videos but can't seem understand what i'm reading/watching.
index.js:
import Link from 'next/link'
Welcome.getInitialProps = async function (props) {
const res = await fetch('https://restcountries.eu/rest/v2/all')
const data = await res.json()
return {
data: data
}
}
const MyLink = props => {
return (
<p>
<Link href={`/info?name=${props.name}`} >
<a>Learn More</a>
</Link>
</p>
)
}
function Welcome(props) {
return (
<div>
<div className="main-content">
<style jsx>{`
.main-content {
width: 80%;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-gap: 5px;
}
.item {
border: 1px solid black;
text-align: center;
}
.item ul{
padding: 0;
}
.item ul li {
list-style-type: none;
}
`}</style>
{props.data.map(country => (
<div key={country.numericCode} className="item">
<h4>{country.name}</h4>
<p>Region: {country.region}</p>
<p>Population: {country.population}</p>
<MyLink name={country.name} borders={country.borders} currencies={country.currencies}/>
</div>
))}
</div>
</div>
)
}
export default Welcome
info.js:
import { withRouter } from 'next/router'
import Link from 'next/link'
const Info = (props) => {
return (
<div>
<h1>{props.router.query.name}</h1>
<Link href="/">
<a>Home</a>
</Link>
</div>
)
}
export default withRouter(Info)
In MyLink component instead of using Link you can create a normal div (style it like a link) and onClick of that div push it to different page using nextjs router:
//import useRouter
import { useRouter } from 'next/router'
//then call it
const router = useRouter()
const MyLink = props => {
return (
<p onClick={() => {
router.push({
pathname: `/info?name=${props.name}`,
query: { data: //data to pass },
})
}}>
<a>Learn More</a>
</p>
)
}
You can access that data in the location object in the query key
import {useLocation} from ""
const location = useLocation()
const data = location.query

Categories