I'm building a navbar who changes its background color if the user has scrolled until an ad.
Before, I used the method "window.scrollY" and change the color if its number is over 700.
Unfortunatly this method is bad to be responsive with differents screens.
In my navabr when I click on a title, the website automaticaly scroll until the part.
I used references to do it and I would like to know if you know a way to check if the scroll reached a reference in the top of the screen (to replace the bad color system).
Like ref.current.isReached ? return a boolean.
With that I could change my css in a better way.
I hope I was clear, my english is very bad.
This is my classe with the ref system:
const Home = ({ section }: Props) => {
const sectionRef1: any = useRef(React.createRef());
const sectionRef2: any = useRef(React.createRef());
const scroll = (ref: any) => {
ref.current.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
};
function scrollToSection(section: number) {
if (section === 1) {
scroll(sectionRef1);
}
else if (section === 2) {
scroll(sectionRef2);
}
else if (section === 3) {
//TODO: active button
}
}
useEffect(() => {
scrollToSection(section);
}, [section]);
return (
<div>
<div ref={sectionRef1} />
<Carrousel></Carrousel>
<div ref={sectionRef2} className="margin_top_portrait" />
<Portrait></Portrait>
</div>
);
}
export default Home;
Thanks in advance
useEffect(() => {
window.addEventListener('scroll', onScroll);
return () => {
window.removeEventListener('scroll', onScroll);
};
}, []);
In onScroll function you can find out whatever your want about your element position with ref.current.getBoundingClientRect()
Example of checking if element is in viewport
Related
I'm relatively new to react and am totally lost trying to figure out how to make an Component appear when I press on a button. I've set up the code as such
<Button>GO</Button>
<CalendarIcon id="calendar visibility="hidden"/>
and then useVisibility()
export default function useVisibility() {
const[visibility, setVisibility] = useState("hidden")
useEffect(() => {
function handleVis(){
setVisibility("visible")
}
button.addEventListener("onClick", handleVis)
return () => button.removeEventListener("onClick", handleVis)
}, [])
return visibility
}
My problem is that I don't know how to pass the button into the function so that I can add the event listener. If I am doing this in a totally roundabout way or overcomplicating it please tell me because I am so lost.
Thanks!
What I would do is let each instance where you render a button specify how its click handler should behave as there can be many use cases for a button in a website.
function MyComponent() {
const[isVisible, setIsVisible] = useState(false)
const handleVisibilityToggle = useCallback(
() => setIsVisible(!isVisible),
[isVisible, setIsVisible]
)
...
const visibility = isVisible ? 'unset' : 'hidden'
return (
<>
...
<Button onClick={handleVisibilityToggle}>GO</Button>
<CalendarIcon id="calendar" visibility={visibility}/>
</>
)
}
if you would like to clean up how that code is used and abstract the logic to a visibility hook it would look something like this
function useVisibilityToggle(defaultValue = false) {
const[isVisible, setIsVisible] = useState(defaultValue)
const toggleVisibility = useCallback(
() => setIsVisible(!isVisible),
[isVisible, setIsVisible]
)
const visibility = isVisible ? 'visible' : 'hidden'
return [visibility, toggleVisibility]
}
function MyComponent() {
const [visibility, toggleVisibility] = useVisibilityToggle(false)
return (
<>
...
<Button onClick={toggleVisibility}>GO</Button>
<CalendarIcon id="calendar" visibility={visibility}/>
</>
)
}
Check the first example here: https://reactjs.org/docs/hooks-state.html
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
That should show you how to manipulate the state when the button is clicked. Next you wanted to show/hide something when that button is clicked.
First let's change that useState to a boolean one, so
const [hidden, setHidden] = useState(false);
Then we can change the button so that it uses the previous state to set the new one. You should never use the hidden in the setHidden, but instead pass a function into it, which gets the previous value as parameter.
<button onClick={() => setHidden(prevHidden => !prevHidden)}>
And now we want to use that value, so we can add something below the button:
{ !hidden ? (<p>This text is visible</p>) : (<></>) }
Working example: https://codesandbox.io/s/strange-williamson-wuhnb?file=/src/App.js
Your code looks like you are trying to build a custom hook, that's more advanced stuff, and if you are super new to React, you won't need that right now.
Your main goal is to show CalendarIcon component visible when you click on GO Button.
So you need a state lets say visible variable to control this.
You can update this state onClick of your Button as shown below to true or false, And make visibility="visible" always.
When visible will be true your CalendarIcon will appear.
const [visible, toggleVisibility] = useState(false)
<Button onClick={()=> toggleVisibility(!visible)}>GO</Button>
{visible && <CalendarIcon id="calendar" visibility="visible"/>}
Code:
const Component = () => {
const[divShow, setDivShow] = useState(false);
const toggleDivShow = () => {
setDivShow(!divShow)
}
return (
<div>
<button onClick={toggleDivShow}>
{
divShow && <div>click the button to show me</div>
}
</div>
)
}
now, this is working perfectly and toggle showing the div when the user click the button but, this only hide the div when the user click the button, How to hide the div when the user click anywhere else in the window
I tried to add a click event listener to the window that set divShow to false but unfortunately this didn't work, as this affected the button too and divShow always set to false even when i click the button, this is expected i think because the button is a part of the window
How can i solve this problem??
add divShow to useEffect dependency array, also dont forget to call clean up method in useEffect, as multiple document.addEventListener will cause the browser to hang.
const Component = () => {
const [divShow, setDivShow] = useState(false);
const toggleDivShow = () => {
setDivShow(!divShow);
};
useEffect(() => {
if(divShow){document.addEventListener("click", toggleDivShow)}
return () => document.removeEventListener("click", toggleDivShow); //cleaning the side effect after un-mount
}, [divShow]);
return (
<div>
<button onClick={toggleDivShow}>
{divShow && <div>click the button to show me</div>}
</button>
</div>
);
};
Following is my handleScroll function in which I am trying to add red class if it scroll down to a certain limit else apply blue. However this is only going inside the else statement and also console.log(e.target.scrollTop); its consoling as undefined. Let me know what I am doing wrong here.
Code -
handleScroll(e) {
console.log(e.target.scrollTop);
if (window.screenX > 100) {
this.setState({
navBckgroundColor: `red`
});
} else {
this.setState({
navBckgroundColor: `blue`
});
}
}
Codesandbox - https://codesandbox.io/s/silly-feynman-m6hp1
I would highly recommend adding an extra check to your condition. When you scroll a single-time, you update your component-state multiple times after a certain range (100), which is unnecessary. You only need to update it once.
It will keep updating because you meet the condition inside handleScroll, even though the background has already changed. The sheer amount of updates can cause your app to crash.
Try something like this it will update your component-state as expected and only when necessary: https://codesandbox.io/s/white-architecture-jepyc
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
navBckgroundColor: `blue`,
altered: false
};
}
componentDidMount() {
window.addEventListener("scroll", this.handleScroll);
}
//use arrow function instead so that its "this" keyword points to the component's execution context. Otherwise, "this" will point to the global window object where you cannot use this.setState.
handleScroll = e => {
if (window.pageYOffset > 100 && !this.state.altered) {
this.setState({
navBckgroundColor: `red`,
altered: true
});
} else if(window.pageYOffset < 100 && this.state.altered) {
this.setState({
navBckgroundColor: `blue`,
altered: false
});
}
};
render() {
return (
<div className="App">
<Navbar bckGroundColor={this.state.navBckgroundColor} />
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
}
Use window.scrollY instead of window.screenY and also bind the handleScroll method.
handleScroll = (e) => {
if (window.scrollY > 100) {
this.setState({
navBckgroundColor: `red`
});
} else {
this.setState({
navBckgroundColor: `blue`
});
}
}
Working demo
Please use
handleScroll = e => {
console.log(e.target.scrollTop);
if (window.scrollY > 100) {
this.setState({
navBckgroundColor: `red`
});
} else {
this.setState({
navBckgroundColor: `blue`
});
}
}
Please see the workable code on :
https://codesandbox.io/s/friendly-swirles-bwl06
also your window.screenX will always output the same value, and thus no change to the colors.
I have changed that in the code as well
Please read code first.
After css processing, it seems like memo application's single memo paper.
The goal of the component is to print a 1 when clicked(in real, the goal is to hadding redux store's state).
When i click outside of div component, it works very well. ( it printed '1' )
but when i clicked inner div component(title, date,content), onClick event also proceed ( it printed '')
how can i prevent non-valued print?
My code :
class container extends Component {
handleState = (event) => {
console.log(event.target.id)
}
render(){
return(
<div onClick={handleState} id={value}>
<div>title</div>
<div>date</div>
<div>content</div>
</div>
)
}
}
container.defaultprops = {
value: 1
}
thanks.
You can use currentTarget:
handleState = (event) => {
console.log(event.currentTarget.id)
}
About difference between target and currentTarget:
https://stackoverflow.com/a/10086501/5709697
You can use currentTarget to check if it's the target since you bound the handler to the parent e.g.
handleState = (event) = > {
if (event.target == event.currentTarget) {
console.log(event.target.id)
}
}
How can I add a className when the page scrolls? I have ready many other articles and answers to this (may be a duplicate) but none have helped me understand what is wrong with my code below.
If the code is not the issue I believe that it stems from a perspective wrapper around the app that may disallow the registration of scroll. How can I add the event listener to register scroll on id=container
constructor(props) {
super(props)
this.state = {
isStuck: true,
}
this.handleHeaderStuck = this.handleHeaderStuck.bind(this)
}
componentDidMount () {
window.addEventListener('scroll', this.handleHeaderStuck);
}
componentWillUnmount () {
window.removeEventListener('scroll', this.handleHeaderStuck);
}
handleHeaderStuck() {
if (window.scrollY === 0 && this.state.isStuck === true) {
this.setState({isStuck: false});
}
else if (window.scrollY !== 0 && this.state.isStuck !== true) {
this.setState({isStuck: true});
}
}
render() {
return (
<main className={this.state.isStuck ? 'header-stuck' : ''}>
...
</main>
This screenshot reassures me that the issue is with the registering of onScroll listener
Be sure your component have enough height for scroll. Your code works.
Add some height to main and check it.
main {
height: 2000px;
}
https://jsfiddle.net/69z2wepo/156204/
You code has an issue, onScroll is attached a listener function handleScroll whereas the function is handleHeaderStuck in your case. Change the listener to execute the correct function.
componentDidMount () {
window.addEventListener('scroll', this.handleHeaderStuck);
}
componentWillUnmount () {
window.removeEventListener('scroll', this.handleHeaderStuck);
}