I want to make a sticky footer that will stick to the bottom if there's not enough content to fill up the whole page. I've searched up ways to do it in CSS, but a lot of them doesn't translate to React/Next since it involves messing with the html and body tag. I'm wondering if there are other ways to do it.
Here is the JSX for my Footer:
<div>
<footer>
<a href={"https://www.instagram.com/linghandmade18/"}>
<i className="fab fa-instagram" />
</a>
</footer>
<h2>Some Text</h2>
</div>
Here is my Layout file for Next.js:
const Layout = (props) => {
return (
<div>
<Navbar />
{props.children}
<Footer />
</div>
);
};
If you don't want to mess with html and body tags then you need a container on which you can apply your style. So first of all create a common container inside your Layout (add container class to parent element), like this:
const Layout = (props) => {
return (
<div class="container">
<Navbar />
{props.children}
<Footer />
</div>
);
};
Now you have a .container class to the div which is parent div of Navbar, Content and Footer. Now add following styles to the container class:
.container {
min-height: 100vh;
position: relative;
}
Because of this your container height will stay at least 100vh (viewport height), it will grow more if content length increases.
And for your footer component, make these changes if the h2 is part of the footer (for better accessibility).
const Footer = (
<footer>
<a href={"https://www.instagram.com/linghandmade18/"}>
<i className="fab fa-instagram" />
</a>
<h2>Some Text</h2>
</footer>
);
For footer styling you can add this style:
footer {
position: absolute;
bottom: 0;
left: 0;
}
This way it will always stay in the bottom position regardless of the content height.
Related
In my Vue application, I have a navigation drawer that contains a sticky header and content underneath it.
The content is quite long and I'd like to display a divider or a border between the header and the content.
I know a border-bottom on the "sticky-header" div would do it but I want to apply a bottom border to the header only when it becomes stuck (fixed at the top) not when the header is in the default form (relative position).
Below is my code-
<v-navigation-drawer
v-if="drawerNodeData"
class="nav-drawer"
width="408px"
dark
color=#212121
v-model="drawerNode"
absolute
temporary
>
<div class="node-drawer">
<div class="sticky_header">
<div class="close-actor-panel">
<span></span> <!-- To stick the close icon to the right -->
<v-btn icon size="0.8em" #click.stop="drawerNode = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</div>
<v-list-item class="title-and-nb-of-news">
<span v-if="this.drawerNodeData" class="node-title">{{
this.drawerNodeData.id.replaceAll("_", " ")
}}</span>
<div class="chip-for-news">
<DrawerNewsModale
:actorName.sync="actorName"
:filteredNews.sync="filteredNews"
></DrawerNewsModale>
</div>
</v-list-item>
</div>
<div class="content"></div>
</div>
</v-navigation-drawer>
Any idea how to detect if element is sticky or not so I can toggle the border accordingly?
The IntersectionObserver interface of the Intersection Observer API
provides a way to asynchronously observe changes in the intersection
of a target element with an ancestor element or with a top-level
document's viewport.
So, A few lines of IntersectionObserver in JavaScript and tricky usage of top: -1px in the CSS could help to achieve this.
The Below code is containing a header and some content. When the page will be scrolled and the header will be stuck at the top, a bottom border will be applied to the header and when the header is not sticky anymore, the border class will be removed.
const el = document.querySelector(".sticky-element")
const observer = new IntersectionObserver(
([e]) => e.target.classList.toggle("threshold-reached", e.intersectionRatio < 1),
{ threshold: [1] }
);
observer.observe(el);
#parent {
height: 2000px;
}
.sticky-element {
position: sticky;
top: -1px;
z-index: 1;
background: yellow;
}
/* styles for when the header is in sticky mode */
.sticky-element.threshold-reached {
border-bottom: 1px solid black;
}
<div id="parent">
<br />
<div class="sticky-element">Header</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
</div>
Important- Read more about how this intersection API works.
Here is a picture of my Title and Image. As you can see, there is space above the Title and the blue line. However, if there is no Title, then the space above the Title is gone and the images are now closer to the blue line, example here. How can I still keep the space above the title, even if there is no title? Basically, I don't want my images to move up closer to the blue line, even if there is no Title, I want that space to remain in place.
I tried applying min-height on the title class, but still there is no space, when there is no header
Here is my HTML:
<div class = "myComponentWrapper">
<div class = "myContainer"
<div class = "Title">
<h2 class = "headTitle"> This my Header</h2>
</div>
</div>
<div class "image">
<img class="myImage" .......>
</div>
</div>
First of all, you need to be careful with your code:
Use kebab-case for CSS class names
Use double space for indentation
Don't add spaces around = when setting an attribute
Now, to solve your problem, I think all you need to do is to reset the margin for the h2 element to make sure that the total height h2 + margin is smaller than the min-height you set for the title container.
Try this and see if it solves your problem:
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="my-component-wrapper">
<div class="my-container">
<div class="title">
<h2 class="head-title">This my Header</h2>
</div>
</div>
<div class="image">
<img
class="my-image"
src="https://via.placeholder.com/800x200"
alt=""
/>
</div>
</div>
</body>
</html>
/* style.css */
body {
margin: 0;
padding: 0;
background-color: aqua;
}
.my-image {
width: 100%;
}
.title {
min-height: 60px;
}
.head-title {
margin: 0;
padding: 10px 0;
}
There could be multiple ways to do it. I am going to describe one that I find simple.
Use conditional margins
You can find out(by inspecting) how much height the header takes plus the gap between the header and the image and then what you can do is apply conditional margin top to the .image class to achieve it.
Assuming the height of header + gap is 40px and the gap itself is 20px, here's what you can do.
/* We give this margin-top by default. This includes header height + gap*/
.image {
margin-top: 40px;
}
/* If there's myContainer div, which i am assuming only comes in when you have the header, we reduce that margin top to 20px to compensate for the header that exists.*/
.myContainer + .image {
margin-top: 20px;
}
You could also give a specific height to your header element and then this calculation would become a little easier for you instead of finding the taken up vertical space by inspecting.
NOTE: you also need to worry about the line-height of the h2 element if you aren't specifying a particular height for it. In that case, you need to add the computed line-height along with the gap between the header and the image and then use that as the margin-top instead of 40px as shown above.
Update:
If the assumptions I made are wrong, then you can't use the sibling selector to apply those rules. In that case you need to either change your markup to make the header and the image as siblings or may be choose a more complicated route and use JavaScript(not preferred).
Semantically, I would like to keep them as siblings if I can(assuming I have control over the markup generation). In that scenario, it would look like this.
<div class = "myContainer"
<div class = "Title">
<h2 class = "headTitle"> This my Header</h2>
</div>
<div class "image">
<img class="myImage" .......>
</div>
</div>
Then I would use the same sibling selector trick to get the thing done.
JavaScript Way
If you do not have control over markup generation, you can use JS to conditionally apply the margins.
// Rough example of what you can do assuming the height of header is 20px.
// If you want to find out the height of header dynamically, use
// ```header.offsetHeight``` and add that with 20px(assumed gap height) and apply that as margin top.
const header = document.querySelector(".headTitle");
const image = document.querySelector(".image");
if(header) {
image.style.marginTop = '20px';
} else {
image.style.marginTop = '40px';
}
Set min-height value for your title that would be equivalent to it's line-height value. In such a way space for your title won't collapse even without text content.
.headTitle {
min-height: 40px;
line-height: 40px; // set these values as you want, they just need to be equal
}
<div class = "myComponentWrapper">
<div class = "myContainer"
<div class = "Title">
<h2 class = "headTitle"> This my Header</h2>
</div>
</div>
<div class "image">
<img class="myImage" .......>
</div>
</div>
In my react app I have a hidden banner that I want to show, when the length of the array reaches to 5. But it looks like that I am trying to get an element before it is rendered. I get the error about getting a style of undefined element.
This function must change css of the banner element and make it visible.
showBanner() {
let banner = document.getElementsByClassName('overlay')[0]
banner.style.cssText = "visibility: visible;opacity: 1;"
}
I want to render my popup component only if the condition is met.
render() {
if (this.props.awarded) {
if (this.props.awarded.length === 5) {
this.showBanner()
return (
<>
<h1 id="awardLabel">5 movies</h1>
<div id="movieList">
{this.props.awarded.map((movie) => {
return (
<div className="awardHolder" key={movie.imdbID}>
<div className="awardImgHolder" >
<img src={movie.Poster} alt={movie.Title}></img>
</div>
<div className="awardMovieInfo">
<p>{movie.Title}</p>
<p>year {movie.Year}</p>
</div>
<div className="withdrawButton" onClick={(e) => this.deleteMovie(e, movie)}> WITHDRAW </div>
</div>
)
})}
</div>
<Popup />
</>
)
} else { ...
This is my banner structure.
<div id="popup1" className="overlay">
<div className="popup">
<h2>Here i am</h2>
<a className="close" href="#">×</a>
<div className="content">
<p>Congratulations. You've nominated 5 movies.</p>
<button onClick={this.closeBanner}>Try again</button>
</div>
</div>
</div>
This is my css for the banner element.
.overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
transition: opacity 500ms;
visibility: hidden;
opacity: 0;
}
How can I dynamically change element's styles using conditions to render that element?
You're trying to access your Popup component before it gets created. In other words, this.showBanner() is called before <Popup /> is rendered.
One solution is to move your popup to a higher-level component
This might be a good use case for React Context, which will allow you to have some global state that your components can tap into without having to pass the banner state through multiple components as props.
If you are going to do this, you might consider not manually updating the styling with querySelectors; instead, you can have React either render or not render the component based on your global banner state.
Your application will be wrapped in <BannerContext.Provider> tags, and then the component that needs to render or not render the banner can use <BannerContext.Consumer> tags to check the current banner state. You can also store a toggle function in the BannerContext so that other parts of the application (and the banner itself) can toggle the BannerContext as needed.
I have the following modal component:
export default function LoginModal(props) {
const { showLogin, hideLogin } = props
if (!showLogin) return null
return (
<div class='overlay'>
<div class='modal'>
<article class='mw5 center bg-white br3 pa3 pa4-ns mv3 ba b--black-10'>
<div class='tc'>
<h1 class='f4'>Firstname Lastname</h1>
<hr class='mw3 bb bw1 b--black-10' />
</div>
<p class='lh-copy measure center f6 black-70'>
test test test test
</p>
</article>
</div>
</div>
)
}
which I am attempting to conditionally render using a state property stored in my redux store. However, when I place it as follows:
<article class='pv6 center ph3 ph5-ns tc br2 bg-washed-green dark-green mb5'>
PAGE CONTENT HERE
</article>
<LoginModal />
It appears below the rest of the screen content rather than above everything else as I had hoped. I am using the following css to try to get this effect, but it doesn't seem to be working:
.overlay {
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: 'rgba(0,0,0,0.3)';
padding: 50;
z-index: 300;
}
.modal {
background-color: '#fff';
border-radius: 5;
max-width: 500;
min-height: 300;
margin: '0 auto';
padding: 30;
}
I need the modal to appear as a login card in the center of the screen with the background dimmed, as is common on many websites. I would also rather not use a UI component library to achieve. Thanks for the help!
I think you are missing a position: fixed in your .overlay styles.
Also, you might want to consider using Portals to render the modal outside the DOM hierarchy of your App, that is, outside the <div id="app"></div> or <div id="root"></div> we commonly use.
To do that, your index.html or equivalent file would have something like this:
<div id="app"></div>
<div id="modal"></div>
And then you need to update your LoginModal like this:
export default function LoginModal(props) {
const { showLogin, hideLogin } = props;
if (!showLogin) return null;
return ReactDOM.createPortal((
<div class='overlay'>
<div class='modal'>
<article class='mw5 center bg-white br3 pa3 pa4-ns mv3 ba b--black-10'>
<div class='tc'>
<h1 class='f4'>Firstname Lastname</h1>
<hr class='mw3 bb bw1 b--black-10' />
</div>
<p class='lh-copy measure center f6 black-70'>
test test test test
</p>
</article>
</div>
</div>
), document.getElementById('modal'));
}
Note that if you try to reuse the code in this example to create a generic Modal component, you would only be able to show one at a time, as they would be rendering inside the same #modal element.
In the Portals documentation you can see an example that creates a new element dynamically so that you can have multiple modals at the same time.
I have a div that is dynamically populated with other divs of various widths, and i need to get the total width of all inside divs here is some code
<div class="inner_wrpr">
<div class="box>
...some content...
</box>
<div class="box>
...some content...
</box>
<div class="box>
...some content...
</box>
<div class="box>
...some content...
</box>
</div>
And here is the JS
<script>
var totalWidth = 0;
$('.box').each(function() {
totalWidth += ($(this).outerWidth(true));
});
$('.inner_wrpr').css({'width': totalWidth});
</script>
But for some reason it only gives me the right width for the box divs which are narrower then the body but anything wider then the body returns the same width as the body
if you could help me with that i would appreciate it.
You have added * { max-width: 100%; } or something like that in .box parent elements. So it doesn't allow widths greater than body's width. Check the screenshots: