I'm trying to get a div to render until the bottom of the screen/page (and no further than that).
I'm able to do that successfully as long as this div is the only thing in the entire page. However, it's overflowing the page whenever I have anything above it (in this example, the text Some Header).
https://codesandbox.io/s/keen-bird-phx1f?file=/src/App.js
The React code:
import "./styles.css";
import React, { Fragment } from "react";
export default function App(props) {
return (
<Fragment>
<div>Some Header</div>
<div className="test1">
<div className="dashbar">Dash</div>
<div className="other-content">Other</div>
</div>
</Fragment>
);
}
The CSS code:
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.test1 {
display: flex;
height: 100vh;
border: 10px solid green;
}
.dashbar {
flex: 1 0 10%;
background: dodgerblue;
}
.other-content {
flex: 1 0 90%;
background: papayawhip;
}
How can I make it so that this div will stop at the bottom of the page and not overflow like this?
If I remove <div>Some Header</div>, then it behaves as expected:
.test1 has a height of 100vh, meaning you are forcing it to be exactly as tall as the viewport. But it has a header above it! So, the header necessarily pushes it down and it overflows the screen. You need a bit more of Flexbox trickery to get it right, especially flex-grow:1.
* {
box-sizing: border-box;
}
body {
margin: 0;
}
#root {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.test1 {
display: flex;
flex-grow: 1;
border: 10px solid green;
}
.dashbar {
flex: 1 0 10%;
background: dodgerblue;
}
.other-content {
flex: 1 0 90%;
background: papayawhip;
}
<body>
<div id="root">
<div>
Some Header<br>
with a couple<br>
of lines in it
</div>
<div class="test1">
<div class="dashbar">Dash</div>
<div class="other-content">Other</div>
</div>
</div>
</body>
It's because "test1" has height as 100vh and there is a static header text which has some height. Try this to fix:
* {
box-sizing: border-box;
line-height: 15px;
}
.test1 {
display: flex;
height: calc(100vh - 15px);
border: 10px solid green;
}
--- Dynamic solution:
https://codesandbox.io/s/stupefied-agnesi-x496f?file=/src/App.js
Related
I am running into an issue with getting SimpleLightbox not loading for me until I refresh the page on my Gatsby site. I have a demo up where if you go from the "inspirations" page (https://effulgent-liger-a1d01e.netlify.app/inspiration) then click on an individual inspiration you will see by clicking on the photo in the gallery that it just opens the photo url and not the lightbox. Then if you refresh the page and click an image in the gallery it then loads the lightbox fine. I assume this is part of my Gatsby learning process, but stumped on this. Thanks for any help.
Here is my individual "inspiration" post file code.
import React from 'react'
import styled from 'styled-components'
import { GatsbyImage } from 'gatsby-plugin-image'
import SimpleLightbox from 'simple-lightbox'
import 'simple-lightbox/dist/simpleLightbox.min.css'
import Seo from "../components/seo"
import CTAOne from '../components/ctaOne'
const HeroSection = styled.section`
height: 650px;
position: relative;
#media(min-width: 768px) {
height: 825px;
}
.hero-image .gatsby-image-wrapper {
width: 100%;
height: 650px;
object-fit: cover;
object-position: center;
#media(min-width: 768px) {
height: 825px;
}
}
.hero-content {
position: absolute;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
z-index: 1;
text-align: center;
width: calc(100% - 20px);
max-width: 1160px;
padding: 15px 0;
background-color: var(--white-trans);
.hero-border-top,
.hero-border-bottom {
height: 18px;
width: 100%;
border: 6px solid var(--white-trans);
}
.hero-border-top {
margin-top: -33px;
}
.hero-border-bottom {
margin-bottom: -33px;
}
h1 {
margin-bottom: 0;
}
p {
font-size: 21px;
line-height: 1.2;
width: 100%;
max-width: 900px;
margin: 25px auto 21px auto;
padding-left: 10px;
padding-right: 10px;
#media(min-width: 768px) {
font-size: 23px;
}
}
}
`
const InspirationPostContainer = styled.section`
margin-bottom: 90px;
.inspiration-desc {
max-width: 850px;
margin: 90px auto 85px auto;
text-align: center;
}
.inspiration-grid {
column-count: 1;
column-gap: 55px;
#media(min-width: 640px) {
column-count: 2;
}
#media(min-width: 992px) {
column-count: 3;
}
}
.item {
margin: 0 0 40px;
display: grid;
grid-template-rows: 1fr auto;
break-inside: avoid;
}
.item > img {
grid-row: 1 / -1;
grid-column: 1;
}
`
function InspirationPost({ pageContext }) {
const { inspiration } = pageContext
if (typeof window !== `undefined`) {
new SimpleLightbox({elements: '.inspiration-grid a'})
}
return (
<>
<HeroSection>
<div className='hero-image'>
<GatsbyImage image={inspiration.inspiration.projectFeaturedPhoto.gatsbyImage} alt={inspiration.inspiration.projectFeaturedPhoto.altText} />
</div>
<div className="hero-content">
<div className='hero-border-top'></div>
<h1>{inspiration.inspiration.projectTitle}</h1>
<div className='hero-border-bottom'></div>
</div>
</HeroSection>
<InspirationPostContainer>
<div className="container">
<div className='inspiration-desc'>{inspiration.inspiration.projectDescription}</div>
<h2>Project Gallery</h2>
<div className='inspiration-grid'>
{inspiration.inspiration.projectGallery.map((node) => (
<a href={`${node.mediaItemUrl}`} className='item' key={node.id}>
<GatsbyImage image={node.gatsbyImage} alt={node.altText} />
</a>
))}
</div>
</div>
</InspirationPostContainer>
<CTAOne />
</>
)
}
export default InspirationPost
The issue seems to be related to the SimpleLightbox module. Have you tried something like this?
useEffect(()=>{
if (typeof window !== `undefined`) {
new SimpleLightbox({elements: '.inspiration-grid a'})
}
},[])
It looks like the library is not loaded at the first initial render or the elements are not ready at the same time that the lightbox is requesting them, but when hydration occurs, everything works as it should, so loading it at the right time or forcing a reload should work.
I can't figure out why I'm getting this little bit of green when the window is an odd number of pixels wide. I think it has something to do with sub-pixel rendering, but I'm just not sure where the green is coming from. It's just the 2nd div too which is weird.
I have some script that is animating the BG of this div. I'm sure this is part of the issue, but I can't figure out why it's only happening to my 2nd div.
I tried to manually set the width of this div, but I was hoping it would be responsive and scale with the window size.
let currentStage = 1
function performAction(selectedStage) {
currentStage = selectedStage
let stages = document.body.getElementsByClassName('stage-flow-item')
let stageLines = document.body.getElementsByClassName('stage-flow-line')
console.log("selectedStage: " + selectedStage)
for (let stage of stages) {
if (stage.id > currentStage) {
stage.classList.remove('completed')
stage.classList.add('active')
} else {
stage.classList.remove('active')
stage.classList.add('completed')
}
}
for (let stageLine of stageLines) {
if (stageLine.id > currentStage) {
stageLine.classList.remove('lineCompleted')
stageLine.classList.add('lineActive')
} else {
stageLine.classList.remove('lineActive')
stageLine.classList.add('lineCompleted')
}
}
}
.stage-flow-container {
display: flex;
justify-content: space-between;
align-items: center;
height: 70px;
padding: 0 30px;
}
.stage-flow-item {
width: 70px;
height: 70px;
min-width: 70px;
border-radius: 50%;
background-color: #ddd;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
color: #fff;
cursor: pointer;
}
.stage-flow-item.active {
background-color: #ddd;
}
.stage-flow-item.completed {
background-color: #6ab04c;
}
.stage-flow-line {
width: calc(100vw);
height: 6px;
background-color: #ddd;
/* default color */
background: linear-gradient(to left, #ddd 50%, #6ab04c 50%) right;
position: relative;
background-size: 200%;
transition: .5s ease-out;
}
.stage-flow-line.lineCompleted {
background-position: left;
background-color: #6ab04c;
}
.stage-flow-line.lineActive {
background-position: right;
background-color: #ddd;
}
<div class="stage-flow-container">
<div id=1 class="stage-flow-item" onclick="performAction(1)">1</div>
<div id=1 class="stage-flow-line"></div>
<div id=2 class="stage-flow-item" onclick="performAction(2)">2</div>
<div id=2 class="stage-flow-line"></div>
<div id=3 class="stage-flow-item" onclick="performAction(3)">3</div>
</div>
I'm not sure if this is on the right track, but I'd eliminate the odd 100vw width on the connectors and instead make them flex. I'd then remove the 200% background size multiplier. By setting the gradient points to 100% the problem is gone. I really don't know if this covers your use case, though.
I converted from background gradient to a pseudo-element solution for the color transition. I think it's simpler. You'd probably have to use CSS animations (as opposed to simple transitions) to make it work otherwise. Of course, you could apply the same principle to the stage items as well, implementing a delay to crate a consistent animation across the item and the line.
Note that duplicated ID values are invalid in HTML. They must be unique. I've refactored to use data attributes instead and an event listener instead of inline JavaScript.
const stageEls = document.querySelectorAll('.stage-flow-item')
const lineEls = document.querySelectorAll('.stage-flow-line')
let currentStage = 1
stageEls.forEach(el => {
el.addEventListener('click', () => {
performAction(el.dataset.stage)
})
})
function performAction(selectedStage) {
currentStage = selectedStage
for (let el of stageEls) {
if (el.dataset.stage > currentStage) {
el.classList.remove('completed')
el.classList.add('active')
} else {
el.classList.remove('active')
el.classList.add('completed')
}
}
for (let el of lineEls) {
if (el.dataset.stage > currentStage) {
el.classList.remove('lineCompleted')
el.classList.add('lineActive')
} else {
el.classList.remove('lineActive')
el.classList.add('lineCompleted')
}
}
}
.stage-flow-container {
display: flex;
align-items: center;
height: 70px;
padding: 0 30px;
}
.stage-flow-item {
width: 70px;
height: 70px;
min-width: 70px;
border-radius: 50%;
background-color: #ddd;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
color: #fff;
cursor: pointer;
}
.stage-flow-item.active {
background-color: #ddd;
}
.stage-flow-item.completed {
background-color: #6ab04c;
}
.stage-flow-line {
flex: 1;
height: 6px;
background: #ddd;
position: relative;
}
.stage-flow-line::after {
position: absolute;
content: '';
top: 0;
left: 0;
width: 0;
height: 100%;
background: #6ab04c;
transition: all 0.5s ease-out;
}
.stage-flow-line.lineCompleted::after {
width: 100%;
}
<div class="stage-flow-container">
<div data-stage=1 class="stage-flow-item">1</div>
<div data-stage=1 class="stage-flow-line"></div>
<div data-stage=2 class="stage-flow-item">2</div>
<div data-stage=2 class="stage-flow-line"></div>
<div data-stage=3 class="stage-flow-item">3</div>
</div>
I'm trying to make it so that when you click on the button with the class .booking__button, the block scrolls up under the header. Position should not change, only scroll. This is done so that the search results of the booking module, which, would be visible to the user. I found the code that does the scrolling, but it works with the exact number, now 100px, but you understand that this distance will be different for everyone, depending on the height of the screen.
document.querySelector('.booking__button').addEventListener('click', () => {
window.scrollTo(0, 100);
});
body {
margin: 0;
}
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 60px;
background: #002164;
}
.hero {
min-height: calc(100vh - 100px);
background: #fff;
}
.booking__module {
display: flex;
justify-content: center;
align-items: center;
background: #BC0B3C;
}
.booking__search {
height: 600px;
background: #ccc;
}
.booking__button {
height: 20px;
margin: 40px;
}
.others {
height: 200vh;
}
<header class="header"></header>
<main class="hero"></main>
<section class="booking">
<div class="booking__module">
<button class="booking__button">booking</button>
</div>
<div class="booking__search"></div>
</section>
<section class="others"></section>
One approach is below, with explanatory comments in the code. Note that while I changed the background-color of the <header> element, that's simply to visualise the functionality and is not at all required:
// we pass a reference to the Event Object ('evt') to the function:
document.querySelector('.booking__button').addEventListener('click', (evt) => {
// we retrieve the closest ancestor <section> element of the element
// to which the event-handler is bound, and retrieve the 'top' property
// of its bounding-client rect:
let {top} = evt.currentTarget.closest('section').getBoundingClientRect();
// we then scroll to that value:
window.scrollTo(0, top);
});
body {
margin: 0;
}
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 60px;
/*background: #002164;*/
background-color: hsl(200deg 70% 70% / 0.4);
}
.hero {
min-height: calc(100vh - 100px);
background: #fff;
}
.booking__module {
display: flex;
justify-content: center;
align-items: center;
background: #BC0B3C;
}
.booking__search {
height: 600px;
background: #ccc;
}
.booking__button {
height: 20px;
margin: 40px;
}
.others {
height: 200vh;
}
<header class="header"></header>
<main class="hero"></main>
<section class="booking">
<div class="booking__module">
<button class="booking__button">booking</button>
</div>
<div class="booking__search"></div>
</section>
<section class="others"></section>
JS Fiddle demo.
References:
Element.closest().
Element.getBoundingClientRect().
Event.
Event.currentTarget.
EventTarget.addEventListener().
Window.scrollTo.
Basically I have two divs setup up and im trying to use flex box to easily size them so they both equally take up half the screen vertically using react. For some reason they are turning out looking like this.
Iv'e tried reinstalling npm but that didn't work at all, and i'm pretty sure my syntax is just fine.
This is my App.css
.wrapper,html,body {
height: 100%;
margin: 0;
}
.wrapper {
display: flex;
flex-direction: column;
}
.nav {
background-color: red;
flex: 1;
}
.main {
background-color: blue;
flex: 1;
}
This is my App.js
import {Row, Col } from "react-bootstrap";
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import './App.css'
class App extends Component {
render() {
return (
<div className = 'wrapper'>
<div className = 'nav'>
<text>hey</text>
</div>
<div className = 'main'>
<text>hey</text>
</div>
</div>
);
}
}
export default App;
They should work if you remove flex-direction: column; - you actually want flex-direction: row;, which is the default.
You're missing the flex: 1 prop inside the .wrapper css definition. The definition should look like this:
.wrapper {
display: flex;
flex:1;
flex-direction: column;
}
With flex: 1 you're telling the wrapper to use all the available space. Otherwise, it just use what it needs no matter the height.
Here's the entire CSS:
.wrapper,
html,
body {
height: 100%;
margin: 0;
}
.wrapper {
display: flex;
flex: 1;
flex-direction: column;
}
.nav {
background-color: red;
flex: 1;
}
.main {
background-color: blue;
flex: 1;
}
See the result:
Anyway, I totally recommend using Bootstrap v4 which is flexbox based by default.
Your code is all fine, just need add flex:1 to wrapper class.
you can read more about flex here : https://css-tricks.com/snippets/css/a-guide-to-flexbox/
.wrapper,
html,
body {
height: 100%;
margin: 0;
}
.wrapper {
display: flex;
flex-direction: column;
flex: 1;
}
.nav {
background-color: red;
flex: 1;
}
.main {
background-color: blue;
flex: 1;
}
<div class='wrapper'>
<div class='nav'>
<text>hey</text>
</div>
<div class='main'>
<text>hey</text>
</div>
</div>
I am trying to keep a seo friendly and semantic structure for my DOM, without repeating whole elements to display them in various positions.
My layout is based on display: flex items. I try to achieve the following:
Important things to know:
I do not want to show/hide divs based on the window width (to avoid unnecessary duplicates)
None of the divs has a known or fixed height
On desktops the divs should be vertical centered, while the right column builds a tag-team (behaves like one single div)
The layout needs to support at least IE11+
Is there a css only solution to achieve this?
If not, it would be easy to cut out the green div and paste its content into the pink one using javascript. But I do have concerns about the performance and "flickering" using this, although resizing the browser makes it more complicated. Do I make this needlessly complicated?
Here is fiddle showing a working solution but with javascript:
CODEPEN DEMO
In general, you can't do this with Flexbox alone, though there might be a compromise based on each given case.
With Flexbox alone, using fixed height, you can accomplish this
* {
box-sizing: border-box;
}
body, html {
margin: 0;
}
.flex {
width: 90%;
margin: 5vh auto;
height: 90vh;
background: rgba(0, 0, 0, 0.05);
display: flex;
flex-flow: column wrap;
}
.flex div {
flex: 1;
width: 50%;
}
.flex div:nth-child(2) {
order: -1;
}
.flex::before {
content: '';
height: 100%;
}
#media (max-width:768px) {
.flex div {
width: auto;
}
.flex::before {
display: none;
}
.flex div:nth-child(2) {
order: 0;
}
}
/* styling */
.flex-child {
color: white;
font-size: 2em;
font-weight: bold;
}
.flex-child:nth-child(1) {
background: #e6007e;
}
.flex-child:nth-child(2) {
background: #f4997c;
}
.flex-child:nth-child(3) {
background: #86c06b;
}
<div class="flex">
<div class="flex-child">
<div>Top/Right</div>
</div>
<div class="flex-child">
<div>Center/Left</div>
</div>
<div class="flex-child">
<div>Bottom/Right</div>
</div>
</div>
In this case, where no fixed height is allowed, you can combine Flexbox and float.
By set up it for mobile using Flexbox where you add the center item first in the markup and then, with order, move it between the top and bottom.
With a media query you then simply make the flex container a block element and use float to position the left to the left and the right to the right.
* {
box-sizing: border-box;
}
body, html {
margin: 0;
}
.flex {
max-width: 1024px;
width: 90%;
margin: 5vh auto;
height: 90vh;
background: rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
}
.flex-child {
color: white;
font-size: 2em;
font-weight: bold;
padding: 5%;
flex-basis: 33.333%;
display: flex;
align-items: center;
}
.flex-child:nth-child(1) {
background: #e6007e;
order: 1;
}
.flex-child:nth-child(2) {
background: #f4997c;
}
.flex-child:nth-child(3) {
background: #86c06b;
order: 2;
}
#media (min-width: 768px) {
.flex {
display: block;
}
.flex-child {
width: 50%;
}
.flex-child:nth-child(1) {
float: left;
height: 100%;
}
.flex-child:nth-child(2),
.flex-child:nth-child(3) {
float: right;
height: 50%;
}
}
<div class="flex">
<div class="flex-child">
<div>Center/Left</div>
</div>
<div class="flex-child">
<div>Top/Right</div>
</div>
<div class="flex-child">
<div>Bottom/Right</div>
</div>
</div>
Update
Here is another version combining Flexbox with position: absolute, which also vertically center the items in desktop mode
Updated, added a script to control so the absolute positioned element won't get bigger than the right items, and if so, adjust the flex containers height.
Note, the script is by no means optimized, it is only there to show how a fix in certain situations
(function() {
window.addEventListener("resize", resizeThrottler, false);
var fp = document.querySelector('.flex');
var fi = fp.querySelector('.flex-child:nth-child(1)');
var resizeTimeout;
function resizeThrottler() {
// ignore resize events as long as an actualResizeHandler execution is in the queue
if ( !resizeTimeout ) {
resizeTimeout = setTimeout(function() {
resizeTimeout = null;
actualResizeHandler();
// The actualResizeHandler will execute at a rate of 15fps
}, 66);
}
}
function actualResizeHandler() {
// handle the resize event
if (fp.offsetHeight <= fi.offsetHeight) {
fp.style.cssText = 'height: '+fi.offsetHeight+'px';
} else {
fp.style.cssText = 'height: auto';
}
}
window.addEventListener('load', function() {
actualResizeHandler();
})
}());
* {
box-sizing: border-box;
}
body, html {
margin: 0;
}
.flex {
position: relative;
max-width: 1024px;
width: 90%;
margin: 5vh auto;
height: 90vh;
background: rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
}
.flex-child {
color: white;
font-size: 2em;
font-weight: bold;
padding: 5%;
}
.flex-child:nth-child(1) {
order: 1;
}
.flex-child:nth-child(3) {
order: 2;
}
.flex-child:nth-child(1) div {
background: #e6007e;
}
.flex-child:nth-child(2) div {
background: #f4997c;
}
.flex-child:nth-child(3) div {
background: #86c06b;
}
#media (min-width: 768px) {
.flex {
justify-content: center;
}
.flex-child {
width: 50%;
}
.flex-child:nth-child(1) {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.flex-child:nth-child(n+2) {
margin-left: 50%;
}
}
<div class="flex">
<div class="flex-child">
<div>Center/Left<br>with more<br>content<br>than any<br>of the<br>other items<br>other items<br>other items<br>other items<br>other items</div>
</div>
<div class="flex-child">
<div>Top/Right<br>with more<br>content</div>
</div>
<div class="flex-child">
<div>Bottom/Right<br>with more</div>
</div>
</div>
With script one can also reorder/move items between elements.
Stack snippet
You can also combine this with a media query, and use it to do the actual re-order of the elements
$( document ).ready(function() {
$(window).resize(function() {
if ($( window ).width() < 600 ) {
$(".one").insertBefore("#b");
} else {
$(".one").insertBefore(".two");
}
});
});
.outer, #flex, #flex2 {
display: flex;
flex-direction: column;
}
#a {
order: 4;
background: #ccc;
}
#b {
order: 1;
background: #aaa;
}
#c {
order: 3;
background: #d33;
}
.one {
order: 2;
background: #aaa;
}
.two {
order: 5;
background: #aaa;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer">
<div id="flex">
<div id="a">A</div>
<div id="b">B</div>
<div id="c">C</div>
</div>
<div id="flex2">
<div class="one">Show me 2nd</div>
<div class="two">Show me 5th</div>
</div>
</div>
Update 2 (answered at another question but later moved here)
If we talk about smaller items, like a header or smaller menus, one can do what many website platform providers like "squarespace", "weebly", "wordpress", etc does. Their templates holds different markup structures, where an item sometimes exist twice, one visible for desktop, another for mobile.
Also, being so small, there will be less to nothing when it comes to performance (and personally I don't see anymore issue with this than having duplicate CSS rules, one for each screen size, and happily do this instead of introducing script).
Fiddle demo
Stack snippet
.container {
display: flex;
}
.container > div {
width: 50%;
}
.container div:nth-child(-n+2) {
border: dashed;
padding: 10px;
}
.container > div:nth-child(1) {
display: none; /* hide outer "Flower" */
}
#media (max-width:768px) {
.container {
flex-direction: column;
}
.container div {
width: auto;
}
.container div:nth-child(1) {
display: block; /* show outer "Flower" */
}
.container div:nth-child(3) div:nth-child(1) {
display: none; /* hide inner "Flower" */
}
}
<div class="container">
<div>Flower</div>
<div>Tree</div>
<div>
<div>Flower</div>
<div>Bee</div>
</div>
</div>