How to scroll 'onClick' component that is hidden before onClick event - javascript

When I click on the image I want that it is automatically scrolled to the component which is then displayed. I tried with anchor tags, but it's not working (I believe due to the fact that the component is hidden and at the same time when it is shown it should be scrolled to it ) , useRef - I get the error 'not defined' (I believe same reason as above).
Component is displyed onClick, but it does't scroll to the view-port of the user. Pls help, I'm out of the ideas :/
const WebContent = () => {
const [hidden, setHidden] = useState(false)
return (
<div>
<img onClick={() => setHidden(true)} src={first}/>
<div>
{hidden && <MyComponent/>}
</div>
</div>
)}

Your intuition is probably right that MyComponent is not yet mounted when you try to scroll to it. A simple way to do this would be to have MyComponent scroll itself into view when it mounts, if that's the behavior you're looking for.
const MyComponent = () => {
const ref = React.useRef(null);
useEffect(() => {
if (ref.current) ref.current.scrollIntoView();
}, [ref]);
return (
<div ref={ref}>
NOW YOU SEE ME
</div>
);
};
export default MyComponent;

One (hacky?) idea is add the ref to the surrounding div of the hidden content:
this.scrollHere = React.useRef(null);
...
return (
<div style={{ minHeight: 1 }} ref={this.scrollHere}>
{hidden && <div>My Hidden Component</div>}
</div>
)
Then you can run a function onClick, which sets hidden to true (which by the way is kinda irritating. Maybe just use "shown" as a quick improvement) and also lets the ref scrollIntoView:
const showAndScroll = () => {
setHidden(true);
this.scrollHere.current.scrollIntoView({
behavior: "smooth"
});
};
The minHeight has to be placed on the div since it is at height of 0 first and this messes with the scroll function (it scrolls below the hidden content).
See working example here.

Related

How to change/update className of a "nested" grandparent from a button, inside of a child component in react : SOLVED :

As the title says, I have a homepage which uses a map() function to map through an arrayList which holds the information about songs, like title, artist, songURL and img.
When I map() through it I will have many "songs" like you would have with blog posts. many elements which is an article which shares the same classname.
Within this map() function I assign a component to each object mapped which is called , this component renders waveforms etc.. the audiovisualizer component also have a button inside for Play/Pause features. and this is the button I want to interact with the Homepage.
When this button is clicked, I want to change the classname for this specific element.
Through useState and callback functions, I got it almost working, but instead of changing the classname for the specific elements that is related to the button clicked, ALL of the "elements/songs" where changed, and not the one targeted.
I will show you the code down under, so you can see the "Tree" and get an understanding of the code.
My result, would be that the button that is attached to each "song" element, would update that specific parent/grandparent element sit sits inside of. but I can't really seem to wrap my head around the logic.
Im completely new to react, this is my first project ever, so go easy on me.
There is also pictures to see what what the code is doing. My goal is to make the specific play/pause object to have an CSS class that will pull up that song and have a position so you can't scroll while its playing. I want to add an pseudo-element to change background color so you can only see that specific card from the list of cards.. from the picture you can see 2 songs. when I click the play/pause I want that card to get an absolute background that will cover the other song.. Problem is that when I try to onClick={} the button that is nested, I can't reach the event.target... and if I do, all of the are changed, not the specific parent element that holds the button.
EDIT - SOLVED
solved the entire problem by referencing the button using useRef. then I used that element like this let buttonRef = buttonRef.current.closest("my wanted parent by classname").classname, and then I changed that manually.
CODE:
Home.js
** this is where the songs/cards are made swith the map() function // be aware, I removed unrelated code for easier view.. **
const Home = (props) => {
const [songs, setSongs] = useState([]);
return (
<div className='home_wrapper'>
<>
{loading ?
<ClipLoader color="#36d7b7" />
:
<div className='homepage_container'>
<h1 className='homeTitleSongs'>Songs</h1>
<button className='logOutButton' onClick={handleLogout}>Logout</button>
{/* // Map through our song library and display all items on homepage */}
{ songs.map((data) => {
return (
// I WANT TP TARGET THE <Article> element´s classname and edit this when the button from inside <AudioVisualizer> component gets clicked.
// Problem is that this is a map function, so when i did get to change it, it changed ALL of the elements from the map function, not the one specific
// where the button is attached to.
<article key={data.id} className='card'>
<div className='card_content'>
<img className='card_image' src={data.image} alt=""/>
<div className='song_info'>
<h2>{data.title}</h2>
<h4>{data.artist}</h4>
</div>
<div className='audio_wrapper'>
<AudioVisualizer audio={data.audio}/>
</div>
</div>
</article>
)
})}
</div>
}
</>
</div>
)
}
export default Home
audioVisualizer component::
const AudioVisualizer = (props) => {
const [isPlaying, setIsPlaying] = useState(false)
const [volume, setVolume] = useState(0.5);
const playButton = faCirclePlay;
const pauseButton = faCirclePause;
const audioRef = useRef(null);
const audioTrackRef= useRef(undefined);
// Create audio waveform object, and load song from database..
useEffect(()=>{
if (audioRef.current){
audioTrackRef.current = wavesurfer.create({
container: audioRef.current,
progressColor: "#13AEA2",
waveColor: "red",
cursorColor: "OrangeRed",
preload: true,
backend: "WebAudio", // originally = "MediaElement"
barWidth: 2,
barHeight: 1, // the height of the wave
fillParent: true,
hideScrollbar: true,
responsive: true,
});
audioTrackRef.current.load(props.audio);
}
}, [])
// Handle play pause button
const handlePlayPause = (e) => {
// Get a view of what the "click" registers:
// if playing == pause
if ( ! isPlaying ) {
console.log("not playing.. Start playing");
audioTrackRef.current.play()
setIsPlaying(isClicked => true)
return
}
else {
console.log("Is playing.. will pause")
audioTrackRef.current.pause()
setIsPlaying(isClicked => false);
return
}
};
return (
<>
<div className='audio' ref={audioRef}>
</div>
<div className='audioKnobs'>
// This is the button i click for play pause a song inside a specific "song" card. and this card is the one i want to update the classname of..
<button className="playpausewrapper" onClick={handlePlayPause}>
<FontAwesomeIcon className={ isPlaying ? 'playButton activeButton' : 'playButton notActiveButton'} icon={ isPlaying ? pauseButton : playButton} />
</button>
<input type="range" className='VolumeSlider onPhoneRemoveVolumeSlider' id="volume" name="volume" min="0.01" max="1" step=".025" onChange={onVolumeChange} defaultValue={volume}/>
</div>
</>
)
}
export default AudioVisualizer;

First click on Nav Menu Button Giving Error and working fine after that ReactJs

I am trying to make a hide/show navbar in ReactJS.
But on the first click I am getting this error and its working fine after the first click.
Note: Its twice and i have no idea why
Here is my code and its only working (after first error) when
setMenu(!menu);
is before
nav.classList.toggle("nav-open");
otherwise the error just keeps coming.
export default function Navbar() {
const [menu, setMenu] = useState(true);
const nav = document.querySelector("nav");
const openNav = () => {
setMenu(!menu);
nav.classList.toggle("nav-open");
};
return (
<nav id="nav-wrapper">
<header className="nav-header">
<div
className="arrow-btn"
onClick={() => {
openNav();
}}
>
{menu ? (
<Icon.HiChevronDoubleLeft size={20} className="arrows" />
) : (
<Icon.HiChevronDoubleRight size={20} className="arrows" />
)}
</div>
<img src={Profiledp} alt="." />
<p className="name">Naman Pokhriyal</p>
</header>
</nav>
This isn't the right way of doing this in React in the first place. You're already using the menu state value to determine if the menu is open is not, so why not continue to use the menu state value to determine if the menu is open or not? Something like this:
const openNav = () => {
setMenu(!menu);
};
return (
<nav id="nav-wrapper" className={menu ? "nav-open" : ""}>
// etc.
);
Basically any time you're trying to directly manipulate the DOM in React, take a step back and try to find a way to manage that via state instead. Direct DOM manipulation in React almost always leads to bugs unless you really know what you're doing under the hood in the framework.

Trigger animated div each time is called with React / Next.js

I'm new with React and Next.js, I'm creating a timeline, the timeline has colored bars which if pressed, the page shows a div below with info about each bar.
I managed to make the content below appear and disappear with useState hook so the content of each bar doesn't stack, I'm using an animated tag "Section" and only the first time I press any bar, the content is animated, the rest appears statically, I'm wondering if I can use something like the useEffect hook to maybe re-render each content div so the animation appears every time you click each bar, also to erase the last loaded div so doesn't stack on browser memory, I hope I explained myself I started with React 2 days ago, and thank you for your time.
Example reduced code:
//useState hook
const [content, setContent] = useState(null);
//Timeline section
<div>
<Bar1 onClick={() => setContent(BarContent_1)}/>
<Bar2 onClick={() => setContent(BarContent_2)}/>
</div>
//Content display
<div>
{content}
</div>
//Content of each bar, (<Section> div has the animation)
const BarContent_1 = () => {
return (
<Section>
Content of bar 1
</Section>
)
}
const BarContent_2 = () => {
return (
<Section>
Content of bar 2
</Section>
)
}
you can use useState for that to toggle classList on element here is example below what I do, in your project I don't know what you do but I will come up with card example first as your default value set state to false in card component and when you will click the button toggle that boolean false to true
like that () => setState(!state) it will always set the value of opposite state
when you will change state component always re-renders and checks the state, and you will make like that on more info div, if that state is true show more-info active that means your div will be displayed
and if it is false it will be dissapeared
const Card1 = () => {
const [showMore, setShowMore] = useState(false)
return (
<div className="card">
<div>Daniel</div>
<button onClick={() => setShowMore(!showMore)}>Show More</button>
<div class={showMore ? "more-info active": "more-info"}>This is more info Daniel</div>
</div>
)
}
also here is my css what I do
.more-info{
opacity: 0;
transition: 0.5s ease;
}
.active{
opacity: 1
}
in start thats good to make stuff like that but I would recommend you to use array objects for that to .map() components and make toggling animations on that, good luck
also quickly I made working example on sandbox
#callmenikk gave me an idea to use a conditional to render my styled div every time the condition met.
Solution:
//useState hook
const [content, setContent] = useState(null);
//Conditional function 'Refresh'
const Refresh = ({children}) => {
const active = content != setContent
if (active) {
return (
<Section>
{children}
</Section>
)
}
}
//Timeline section
<div>
<Bar1 onClick={() => setContent(BarContent_1)}/>
<Bar2 onClick={() => setContent(BarContent_2)}/>
</div>
//Content display and wrapping the content in my Refresh function
<Refresh>
{content}
</Refresh>
//Content of each bar, no need to wrap content in my styled (Section) tag since my Refresh button will
const BarContent_1 = () => {
return (
<div>
Content of bar 1
</div>
)
}
const BarContent_2 = () => {
return (
<div>
Content of bar 2
</div>
)
}

Click on a button that is moved when an input loses focus is not registered

I encountered a weird problem while creating a form with React (it is most likely not specific to React however). The form consists of an input, an error hint underneath it and a button, all vertically stacked. The input is focused on component mount, and the error hint might appear when the user clicks outside of the input. When the input is focused and the user tries to click the button, it loses the focus which may cause the hint to appear. The hint pushes the button downwards which prevents the button's click event from registering, resulting in a bad user experience.
Is there a way force the button to get clicked before the input loses focus?
I have replicated this situation here: https://jsfiddle.net/tacticalteapot/b40hLv3c/4/
Code from the fiddle:
function App() {
const ref = React.useRef(null);
const [showLabel, setShowLabel] = React.useState(false);
const [clicked, setClicked] = React.useState(false);
React.useEffect(() => {
ref.current.focus();
}, []);
return (
<div>
<input ref={ref} onBlur={() => setShowLabel(true)} />
<div>
{showLabel && <label>a label appeared that moved the button</label>}
</div>
<div>
<button onClick={() => setClicked(true)}>
CLICK ME
</button>
</div>
{clicked && <h6 style={{color: 'red'}}>CLICKED</h6>}
</div>)
}
This is happening because of the order of events. onBlur fires first which immediately shows the label. The <label> is rendered directly "on top" of the button. Your click event never registers. To test this, try setting height for your label or even wrap it in another container with a height. Alternatively, add padding to your button such that it's not fully covered by the <label> - it'll register.
Obviously there are better styling methods for this.
function App() {
const ref = React.useRef(null);
const [showLabel, setShowLabel] = React.useState(false);
const [clicked, setClicked] = React.useState(false);
React.useEffect(() => {
ref.current.focus();
}, []);
handleClick = () => {
setClicked(true);
}
return (
<div>
<input ref={ref} onBlur={() => setShowLabel(true)} />
<div className="test">
{showLabel && <label>a label appeared that moved the button</label>}
</div>
<div>
<button style={{padding: "30px"}} onClick={() => handleClick()}>
CLICK ME
</button>
</div>
{clicked && <h6 style={{color: 'red'}}>CLICKED</h6>}
</div>)
}
ReactDOM.render(<App/>, document.getElementById('app'));
I don't think there is a problem with focus. Try using Tab, button gets focused correctly.
Problem is with the UX itself and how the page is pushed down by validation messages.
It would be the best to change the UX slightly to allow the space for validation errors without pushing any elements below:
https://jsfiddle.net/eucj04np/5/
Also, remember to add aria-describedby that points to the id of the element with error validation message. This will help assistive technologies to read out the error content once element is focused.

How can I add styling to an element in React?

In normal JavaScript you can grab an element by its id and add a style to it.
For example:
var element = document.getElementById("myElement");
element.style.backgroundColor = "#f5f5f5";
My question is how you can do this in react. Is it even possible to add this style?
In react im using onChange in a function outside the render(). I looked at the React DOM for styling and tried but since styling is in different function it will tell me how the variable is undefined.
this is my code:
ChangeImage() {
var imgStyles = {
backgroundColor: '#000',
padding: 5,
}
}
render() {
return (
<div className="class">
<div className="img-surround">
<img
src={this.state.file}
id="img"
style={imgStyles}/>
</div>
Everything is working except styles and I even tried putting in different functions
If you want to render the element with the style you can return the element like this in a react functional component:
return <div style={{backgroundColor: "#f5f5f5"}}></div>
If you want the element to only have that style in a certain condition you can use the useState hook in a react functional component:
const [myState, setMyState] = useState(false);
return <div style={myState && {backgroundColor: "f5f5f5"}}></div>
And you should change myState's value using setMyState however you like. For example:
const [myState, setMyState] = useState(false);
return <div onClick={() => myState ? setMyState(true) : setMyState(false)} style={myState && {backgroundColor: "f5f5f5"}}></div>
In this example whenever you click on the div the style is added or removed by case

Categories

Resources