Overflow is messing up with scrollY value - javascript

I have been trying to read the value of scrollY in my reactapp. But I found that it is being messed by overflow.
Here is the code that I used to read the scrollY:
import React from "react";
import{useEffect, useState} from "react"
export default function Test() {
const [offset, setOffset] = useState(0);
const setScroll = () => {
setOffset(window.scrollY);
};
useEffect(() => {
window.addEventListener("scroll", setScroll);
return () => {
window.removeEventListener("scroll", setScroll);
};
}, []);
return (
<div>
<div style={{color:"red"}}>{offset}</div>
</div>
);
}
Here is my app.jsx >>>
import React, { Component } from 'react';
import { Route } from 'react-router';
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { FetchData } from './components/FetchData';
import { Counter } from './components/Counter';
import abc2 from './img/abc2.jpg'
import './custom.css'
import Resume from './components/resumePage/Resume';
import Test from './components/Test.jsx';
var backGStyle={
backgroundImage:`url(${abc2})`,
minHeight: "100vh",
backgroundSize:'cover',
color:'red'
}
export default class App extends Component {
static displayName = App.name;
render () {
return (
<div className="app" style={backGStyle}>
<div className="mask">
<Layout>
<Route exact path='/' component={Home} />
<Route path='/counter' component={Counter} />
<Route path='/resume' component={Resume} />
<Route path='/test' component={Test} />
</Layout>
</div>
</div>
);
}
}
and my custom.css >>
a {
color: #0366d6;
}
code {
color: #E01A76;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.backgroundBody{
position: relative;
}
.app .mask{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
color:#fff;
background-color:rgba(0, 0, 0, 0.6);
overflow:auto;
}
.abc{
color: aqua;
}
And if I remove the overflow: auto from custome.css .app .mask, I am being able to get the value of scrollY but my background image will be messed up, there will be white space in the background of half of the content like this >>>>

try element.scrollTop since your using divs as masks. ScrollY is only for the scroll position of the window (HTML element). Your question is a little vague, if your trying to align you background images use background-position and background-size. also set your overflow to clip.
https://developer.mozilla.org/en-US/docs/Web/CSS/background-position

Related

React, implementing Dark-Light-Mode with localStrorage

I'm trying to use use-local-storage to achieve a theme changer in React.
App component:
import './App.css';
import React from 'react';
import { Navbar, SearchBar, Header, Main, Chart, Map } from './components';
import { Routes, Route, BrowserRouter } from 'react-router-dom';
import useLocalStorage from 'use-local-storage';
function App() {
// a function that toggles between darkmode and lightmode in css
const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const [theme, setTheme] = useLocalStorage('theme', defaultDark ? 'dark' : 'light');
const switchTheme = () => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
}
console.log(theme)
return (
<BrowserRouter>
<div className='App' data-theme={theme} >
<Header />
<SearchBar />
<Navbar switchTheme={switchTheme} />
<Routes>
<Route path="/" element={<Main />} />
<Route path="/map" element={<Map />} />
<Route path="/chart" element={<Chart />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
Navbar component:
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faMapLocationDot, faChartLine, faHouseUser } from '#fortawesome/free-solid-svg-icons'
import React from 'react'
import { Link } from 'react-router-dom'
const Navbar = ({switchTheme}) => {
return (
<nav className='nav'>
<button onClick={switchTheme}>Toggle</button>
<Link to='/'>
<FontAwesomeIcon icon={faHouseUser} size='4x' color='blue' />
<br></br>
Home
</Link>
<Link to='/map'>
<FontAwesomeIcon icon={faMapLocationDot} size='4x' />
<br></br>
Map</Link>
<Link to='/chart'>
<FontAwesomeIcon icon={faChartLine} size='4x' color='red' />
<br></br>
Chart</Link>
</nav>
)
}
export default Navbar
CSS:
*, *::after, *::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/****************** VARIABLES ******************/
:root {
--background-color:coral;
}
[data-theme="light"] {
--background-color:red;
}
[data-theme="dark"] {
--background-color:yellow;
}
body {
background-color:var(--background-color);
font-family: 'Roboto', sans-serif;
font-size: 16px;
color: #333;
line-height: 1.5;
margin: 2vmin;
}
.App {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/********************** SearchBar **********************/
form {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
max-width: 500px;
margin: 0 auto;
}
form > svg {
margin-left: -20px;
}
input {
font-size:inherit;
border-radius: 1vmin;
border: .5px solid #ccc;
padding: .5rem;
}
input:focus {
border-color: #333;
}
nav {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 0.5rem;
background-color: yellow;
border-bottom: 1px solid #eaeaea;
width: 10vw;
height: 50vh;
border: 3px dotted purple;
align-self: flex-start;
}
a {
text-decoration: none;
}
/* a:active {
/* do sth with selected Link
} */
I am getting the correct values from console.log(theme) in App.js but I can't change the background colour of the whole app.
Any ideas to solve this issue ?
You are having a cascading issue, as you are setting your theme on body, and trying to change it later through the App component. Add the data-theme on the body itself or on html, which comes before, not on something that comes after.
Adding this useEffect in App.js just before your return would work:
useEffect(() => {
document.documentElement.setAttribute("data-theme", theme);
}, [theme]);
Find the full example as well as a CodeSandbox below:
import './App.css';
import React, {useEffect} from 'react';
import { Navbar, SearchBar, Header, Main, Chart, Map } from './components';
import { Routes, Route, BrowserRouter } from 'react-router-dom';
import useLocalStorage from 'use-local-storage';
function App() {
// a function that toggles between darkmode and lightmode in css
const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const [theme, setTheme] = useLocalStorage('theme', defaultDark ? 'dark' : 'light');
const switchTheme = () => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
}
useEffect(() => {
document.documentElement.setAttribute("data-theme", theme);
}, [theme]);
return (
<BrowserRouter>
<div className='App'>
<Header />
<SearchBar />
<Navbar switchTheme={switchTheme} />
<Routes>
<Route path="/" element={<Main />} />
<Route path="/map" element={<Map />} />
<Route path="/chart" element={<Chart />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;

Side Drawer Not Closing

I have a side-drawer nav component that opens when a hamburger icon is clicked (mobile view).
When the component is open, it shows nav links. The issue is that the side-drawer stays open even when a link is a clicked and a new page loads. What is the best way to close the side nav when a navlink gets clicked? Edit: added componentDidUpdate to app.js, side drawer still won't close
//side-drawer.jsx
import React from 'react'
import './side-drawer.css'
import { Link } from 'react-router-dom';
const SideDrawer = props => {
let drawerClasses = 'side-drawer';
if (props.show){
drawerClasses = 'side-drawer open';
}
return(
<nav className={drawerClasses}>
<div className="side-nav">
<Link className="side-items" to="/about">ABOUT</Link>
<Link className="side-items" to="/contact">CONTACT US</Link>
</div>
</nav>
);
};
export default SideDrawer;
//app.js
class App extends React.Component {
state = {
sideDrawerOpen: false
};
drawerToggleClickHandler = () => {
this.setState((prevState) => {
return {sideDrawerOpen: !prevState.sideDrawerOpen};
});
};
componentDidUpdate(prevProps) {
const { location } = this.props;
if (location !== prevProps.location && this.state.sideDrawerOpen) {
this.setState({ sideDrawerOpen: false });
}
}
render(){
return (
<div style={{height: '100%'}}>
<Header drawerClickHandler={this.drawerToggleClickHandler}/>
<SideDrawer show={this.state.sideDrawerOpen}/>
<Switch>
<Route exact path='/' component={Homepage}/>
<Route exact path='/about' component={About}/>
<Route exact path='/contact' component={Contact}/>
</Switch>
<Footer/>
</div>
);
}
}
//.side-drawer.css
.side-drawer {
height: 100%;
background: linen;
box-shadow: 1px 0px 7px rgba(0, 0, 0, 0.5);
position: fixed;
top: 0;
left: 0;
width: 70%;
max-width: 400px;
z-index: 200;
transform: translateX(-100%);
transition: transform 0.3s ease-out;
}
.side-drawer.open{
transform: translateX(0);
}
.side-nav{
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.side-items {
margin: 0.5rem auto;
}
.side-items:hover{
text-decoration-line: overline underline;
color: #99bbff;
}
#media (min-width: 769px){
.side-drawer-open{
display: none;
}
}
Well if you're conditionally applying classnames to the SideDrawer component you can do this instead...
<nav className={props.show ? 'side-drawer open' : 'side-drawer'}>
Or you could conditionally show the component in the App component by...
return (
<div style={{height: '100%'}}>
<Header drawerClickHandler={this.drawerToggleClickHandler}/>
{
this.state.sideDrawerOpen && <SideDrawer />
}
<Switch>
<Route exact path='/' component={Homepage}/>
<Route exact path='/about' component={About}/>
<Route exact path='/contact' component={Contact}/>
</Switch>
<Footer/>
</div>
);
Here is the complete solution..
You need a specific handler for closing the sideDrawer and some css changes.
sideDrawer.js
import React from "react";
import style from "./side-drawer.module.css";
import { Link } from "react-router-dom";
const SideDrawer = (props) => {
let drawerClasses = [style.sideDrawer, style.Close];
if (props.show) {
drawerClasses = [style.sideDrawer, style.Open];
}
return (
<div className={drawerClasses.join(" ")}>
<div className={style.sideNav}>
<Link
to="/about"
// className={style.Items}
onClick={props.closed}
style={{ textDecoration: "none", color: "black" }}
>
ABOUT
</Link>
<Link
to="/contact"
// className={style.Items}
onClick={props.closed}
style={{ textDecoration: "none", color: "black" }}
>
CONTACT US
</Link>
</div>
</div>
);
};
export default SideDrawer;
App.js
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
import SideDrawer from "./sideDrawer";
import { Homepage, About, Contact } from "./Components";
import Header from "./Header";
class App extends Component {
state = {
sideDrawerOpen: false
};
drawerToggleClickHandler = () => {
this.setState((prevState) => {
return { sideDrawerOpen: !prevState.sideDrawerOpen };
});
};
drawerClosedHandler = () => {
this.setState({ sideDrawerOpen: false });
};
render() {
return (
<div style={{ height: "100%" }}>
<Header drawerClickHandler={this.drawerToggleClickHandler} />
<SideDrawer
show={this.state.sideDrawerOpen}
closed={this.drawerClosedHandler}
/>
<Switch>
<Route exact path="/" component={Homepage} />
<Route exact path="/about" component={About} />
<Route exact path="/contact" component={Contact} />
</Switch>
{/* <footer /> */}
</div>
);
}
}
export default App;
side-drawer.module.css
module.css because of using join function in sideDrawer.js
.sideDrawer {
position: fixed;
width: 700px;
max-width: 80%;
height: 100%;
left: 0;
top: 0;
z-index: 600;
background-color: white;
box-sizing: border-box;
transition: transform 0.3s ease-out;
background-color: yellow;
}
#media (min-width: 900px) {
.sideDrawer {
display: none;
}
}
.sideNav a{
border: 1px solid black;
margin: 200px 30px ;
}
.Open {
transform: translateX(0);
}
.Close {
transform: translateX(-100%);
}
full source code

Expand div in a Class component

I'm following this react-flip-toolkit tutorial in order to animate a<div> expansion in a component:
This is the tutorial code:
import React, { useState } from 'react'
import { Flipper, Flipped } from 'react-flip-toolkit'
const AnimatedSquare = () => {
const [fullScreen, setFullScreen] = useState(false)
const toggleFullScreen = () => setFullScreen(prevState => !prevState)
return (
<Flipper flipKey={fullScreen}>
<Flipped flipId="square">
<div
className={fullScreen ? 'full-screen-square' : 'square'}
onClick={toggleFullScreen}
/>
</Flipped>
</Flipper>
)
}
My project however, unlike the functional Component example above, uses Class components, like so:
class Field extends Component {
constructor(props) {
super(props);
this.state = {
players:[],
};
}
getPlayersByPosition = (players, position) => {
return players.filter((player) => player.position === position);
};
render() {
const { players } = this.props;
if(players){
return (
<div className="back">
<div className="field-wrapper" >
<Output output={this.props.strategy} />
// this is the target div I want to expand
<div className="row">
{this.getPlayersByPosition(players, 5).map((player,i) => (
<Position key={i} >{player.name}</Position>
))}
</div>
</div>
</div>
);
}else{
return null}
}
}
export default Field;
How can I declare AnimatedSquare() in my Class component and encapsulate my target <div> above within <Flipper/> and <Flipped/>?
I've converted the example to a class based component for you. You should be able to work the rest out from this example:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { Flipped, Flipper } from "react-flip-toolkit";
import "./styles.css";
class AnimatedSquare extends Component {
state = {
fullScreen: false
};
toggleFullScreen() {
this.setState({ fullScreen: !this.state.fullScreen });
}
render() {
const { fullScreen } = this.state;
return (
<Flipper flipKey={fullScreen}>
<Flipped flipId="square">
<div
className={fullScreen ? "full-screen-square" : "square"}
onClick={this.toggleFullScreen.bind(this)}
/>
</Flipped>
</Flipper>
);
}
}
ReactDOM.render(<AnimatedSquare />, document.querySelector("#root"));
* {
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.square {
width: 5rem;
height: 5rem;
cursor: pointer;
background-image: linear-gradient(
45deg,
rgb(121, 113, 234),
rgb(97, 71, 182)
);
}
.full-screen-square {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
background-image: linear-gradient(
45deg,
rgb(121, 113, 234),
rgb(97, 71, 182)
);
}

React Context - "this" is undefined

I am using React Context in order to manage a global state.
So I have defined my Context with its Provider and its Consumer.
I have my videoplaying-context.js
import React from "react";
import { createContext } from 'react';
// set the defaults
const VideoContext = React.createContext({
videoPlaying: false,
setPlayingVideo: () => {}
});
export default VideoContext;
In my _app.js I have:
import App from 'next/app'
import { PageTransition } from 'next-page-transitions'
import VideoContext from '../components/videoplaying-context'
class MyApp extends App {
setPlayingVideo = videoPlaying => {
this.setState({ videoPlaying });
};
state = {
videoPlaying: false,
setPlayingVideo: this.setPlayingVideo
}
render() {
console.log('new _app.js defalt page');
const { Component, pageProps, router, state } = this.props
return (
<React.Fragment>
<VideoContext.Provider value={this.state}>
<PageTransition timeout={300} classNames="page-transition">
<Component {...pageProps} key={router.route} />
</PageTransition>
</VideoContext.Provider>
</React.Fragment>
)
}
}
export default MyApp
and then in one of my file I have put the Consumer:
import Layout from "../components/Layout";
import ReactPlayer from 'react-player
import VideoContext from '../components/videoplaying-context'
class Video extends React.Component {
constructor(props) {
super(props);
this.triggerVideo = this.triggerVideo.bind(this);
}
triggerVideo(event) {
console.log("click");
/* doing other stuff here... */
}
render() {
return (
<VideoContext.Consumer>
{context => (
<Layout>
<h1>Videos</h1>
<div>
<div className="group" id="process-video">
<div
className="poster-image"
onClick={() => {
this.triggerVideo.bind(this);
context.setPlayingVideo(true);
}}
/>
<ReactPlayer
url="https://vimeo.com/169599296"
width="640px"
height="640px"
config={{
vimeo: {
playerOptions: {
thumbnail_url: "http://placehold.it/640x640.jpg",
thumbnail_width: 640,
thumbnail_height: 640
}
}
}}
/>
</div>
</div>
<style jsx global>{`
.group {
position: relative;
height: 0;
overflow: hidden;
height: 640px;
width: 640px;
}
.poster-image {
background: url("http://placehold.it/640x640.jpg") center center;
background-size: cover;
bottom: 0;
left: 0;
opacity: 1;
position: absolute;
right: 0;
top: 0;
z-index: 10;
height: 640px;
width: 640px;
transition: all 0.4s ease-in;
}
.poster-image + div {
position: absolute;
top: 0;
left: 0;
width: 640px;
height: 640px;
}
.poster-image.video--fadeout {
opacity: 0;
}
`}</style>
</Layout>
)}
</VideoContext.Consumer>
);
}
}
export default Video;
So, the function "context.setPlayingVideo(true)" is working fine and it's correctly setting the global state "videoPlaying" to true, but, after the introduction of the Context, "this.triggerVideo.bind(this);" is not working anymore because "this" is undefined.
I tried removing it and other stuff but I'm really stuck and I don't know hot to fix it.
Thanks everyone!
On this line you are not calling the method triggerVideo
onClick={() => { this.triggerVideo.bind(this); context.setPlayingVideo(true); }}
Change to:
onClick={() => { this.triggerVideo(); context.setPlayingVideo(true); }}
or to:
onClick={() => { this.triggerVideo.bind(this)(); context.setPlayingVideo(true); }}

Material-ui drawer width issue

I'm facing an issue with material-ui drawer. I've changed the width of the drawer container which causes a a problem . The drawer remains a little inside the page and visible but I don't want to make it visible on the page while I haven't clicked the button. It might be having an issue with the transform attribute now.
So I changed it to transform: translate(350px, 0px) but then I'm getting another issue, that is if I am clicking the button the drawer is not showing up. Any help on this thing ??
I have got the solution and edited the code.
I've created a Demo here => Have a look
Also shared the code below:
index.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import Drawer from 'material-ui/Drawer';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
class App extends Component {
constructor() {
super();
this.state = {
openDrawer: false
};
}
toggleDrawer() {
this.setState({
openDrawer: !this.state.openDrawer
});
}
render() {
return (
<MuiThemeProvider>
<div>
<button onClick={this.toggleDrawer.bind(this)}> Toggle Drawer</button>
<Drawer
open={this.state.openDrawer}
containerClassName={!this.state.openDrawer? "hide-drawer": "show-drawer" }
openSecondary={true}
docked={true}
>
<div className="drawer-title-div">
<h4 className="drawer-title-text">It's my drawer</h4>
</div>
</Drawer>
</div>
</MuiThemeProvider>
);
}
}
render(<App />, document.getElementById('root'));
style.css
h1, p {
font-family: Lato;
}
.show-drawer {
top: 47px !important;
text-align: left !important;
width: 80% !important;
transform: translate(0%, 0px) !important;
}
.hide-drawer {
top: 47px !important;
text-align: left !important;
width: 80% !important;
transform: translate(100%, 0px) !important;
}
/* .drawer-side-drawer:focus {
top: 47px !important;
text-align: left !important;
width: 350px !important;
transform: translate(0px, 0px) !important;
} */
.drawer-title-div {
display: inline-block;
width: 100%;
background: #F2F8FB;
box-shadow: 0 1px 3px 0 rgba(0,0,0,0.24);
}
.drawer-title-text {
display: inline-block;
margin-left: 16px;
margin-top: 16px;
margin-bottom: 16px;
color: #484848;
font-family: Muli;
font-size: 16px;
font-weight: 600;
}
For mui version 5, you have to use the PaperProps prop like so:
<Drawer
PaperProps={{
sx: { width: "90%" },
}}
>{...Child elements here}</Drawer>
you can simply add this to index.css
.MuiDrawer-paper {
width: 60% !important;
}
#media (max-width: 1200px) {
.MuiDrawer-paper {
width: 100% !important;
}
}
Just add PaperProps={{ style: { width: '25%' } }} to your MUI Drawer.
Most Probably it will work for everyone.
You can try adding a toggle class and you can get rid of the transform.
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import Drawer from 'material-ui/Drawer';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
class App extends Component {
constructor() {
super();
this.state = {
openDrawer: false
};
}
toggleDrawer() {
this.setState({
openDrawer: !this.state.openDrawer
});
}
render() {
return (
<MuiThemeProvider>
<div>
<button onClick={this.toggleDrawer.bind(this)}> Toggle Drawer</button>
<Drawer containerClassName={!this.state.openDrawer ? "hide-drawer": "show-drawer"}
open={this.state.openDrawer}
openSecondary={true}
docked={true}
>
<div className="drawer-title-div">
<h4 className="drawer-title-text">It's my drawer</h4>
</div>
</Drawer>
</div>
</MuiThemeProvider>
);
}
}
render(<App />, document.getElementById('root'));
You can use window.innerWidth as width: 100%:
<Drawer ...>
<div style={{width: window.innerWidth * 0.25}}>
...
</div>
</Drawer>
One way to solve this issue is by getting the parent width:
const parentRef = useRef<HTMLDivElement>(null);
<Box
ref={parentRef}
>
<Drawer
PaperProps={{
sx: {
width: parentRef?.current?.clientWidth || 0,
},
}}
// .... etc
</Drawer>
</Box>
One way to solve this issue is by getting the parent width:
const parentRef = useRef<HTMLDivElement>(null);
<Box
ref={parentRef}
>
<Drawer
PaperProps={{
sx: {
width: parentRef?.current?.clientWidth || 0,
},
}}
>
// content goes here
</Drawer>
</Box>
Drawer-Material-UI If you look at the link.. you will find Drawer properties..
width (union: string number) [default : null] The width of the Drawer in pixels or
percentage in string format ex. 50% to fill half of the window or 100%
and so on. Defaults to using the values from theme.
so just update the tag with width and you are good to go,
<Drawer width="50%"></Drawer>
Check it here..
The drawer width is not matching the theme drawer width which was causing the problem.. not the transform CSS attribute.
Just a different approach ^^
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import Drawer from 'material-ui/Drawer';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import Responsive from 'react-responsive-decorator'; // This decorator allows using the library as a decorator.
#Responsive
class App extends Component {
constructor() {
super();
this.state = {
openDrawer: false,
width:350
};
}
// Update for kinda media query thing
componentDidMount() {
this.props.media({ minWidth: 768 }, () => {
this.setState({
width: 350
});
});
this.props.media({ maxWidth: 768 }, () => {
this.setState({
width: 150
});
});
}
toggleDrawer() {
this.setState({
openDrawer: !this.state.openDrawer
});
}
render() {
return (
<MuiThemeProvider>
<div>
<button onClick={this.toggleDrawer.bind(this)}> Toggle Drawer</button>
<Drawer width={this.state.width} //<- Update
open={this.state.openDrawer}
containerClassName="drawer-side-drawer"
openSecondary={true}
docked={true}
>
<div className="drawer-title-div">
<h4 className="drawer-title-text">It's my drawer</h4>
</div>
</Drawer>
</div>
</MuiThemeProvider>
);
}
}
render(<App />, document.getElementById('root'));
I had the same problem.
you just have to add the PaperProps to your drawer

Categories