I wanted to hide menu slider onclicking a body in reactjs. how can i do that in function using react.js.
document.querySelector('.open-menu').onclick = function(){
html.classList.add('active');
};
document.querySelector('.close-menu').onclick = function(){
html.classList.remove('active');
};
html.onclick = function(event){
if (event.target === html){
html.classList.remove('active');
}
};
I want this same functionality in react js.
Check the code below.
import React, { useState } from 'react';
const SomeComponent = () => {
const [isMenuOpen, showMenu] = useState(false)
const toggleMenu = () => showMenu(!isMenuOpen)
return (
<>
{isMenuOpen && <MenuCompoment />}
<div onClick={toggleMenu}><App /></div>
</>
)
}
This is a stripped down version of code I've used before.
UseEffect on mounting of the Menu adds an event listener on the document for the click event.
When a click happens it uses closest to look up the parent tree of elements for an id (note the '#')
If it finds one, then the click happened on the menu otherwise it happened on any other element so close.
When the menu is disposed the return function of useEffect is called and removes the event handler.
import React, {useState, useEffect} from 'react';
const Page = () => {
const [toggle, setToggle] = useState(false);
return <div>
<button type="button" onClick={e => setToggle(!toggle)}>Toggle</button>
{ toggle && <Menu show={toggle} hide={() => setToggle(false)}/>}
</div>
}
const Menu = ({show, hide}) => {
useEffect(() => {
document.addEventListener("click", listen);
return () => {
document.removeEventListener("click", listen);
}
}, []);
const listen = e => {
if (!e.target.closest("#menu")) {
hide();
}
}
return <div className="menu" id="menu">
<span>I'm a menu</span>
</div>;
}
i think setting onclick event on the menuItems like this will Work
onClick={()=> setOpen(!open)}
export function SidebarMenu({open, setOpen}) {
return (
<div open={open}>
<Link to="#" title="Home" onClick={()=> setOpen(!open)} >Home</Link>
</div>
)
}
Probably too late for answer but since I saw it in active feed, I will try my best to answer it.
I can see that if your menu is open, you want to hide it if clicked anywhere else. I have used useRef to store the menu node and I compare it to the document whenever its open, if it is, I close the menu
Codesandbox link
Related
Currently I'm doing this to close my topbar menu but it isn't what I need because it only closes when the menu icon is clicked.
It needs to close when I click anywhere on the website. Is there an easy and efficient way to achieve this with react?
Navbar.js
const handleToggle = () => {
setActive(!isActive);
};
<div className="account" onClick={handleToggle}><img src={davatar} className="avatar" alt="accountmenu" width="40" height="40" /></div>
There is a package called react-cool-onclickoutside. You can use that to solve this issue
OR you can u can create a custom useOutsideHook refer here
import { useState, useEffect, useRef } from "react";
// Usage
function App() {
// Create a ref that we add to the element for which we want to detect outside clicks
const ref = useRef();
// State for our modal
const [isModalOpen, setModalOpen] = useState(false);
// Call hook passing in the ref and a function to call on outside click
useOnClickOutside(ref, () => setModalOpen(false));
return (
<div>
{isModalOpen ? (
<div ref={ref}>
Hey, I'm a modal. Click anywhere outside of me to close.
</div>
) : (
<button onClick={() => setModalOpen(true)}>Open Modal</button>
)}
</div>
);
}
// Hook
function useOnClickOutside(ref, handler) {
useEffect(
() => {
const listener = (event) => {
// Do nothing if clicking ref's element or descendent elements
if (!ref.current || ref.current.contains(event.target)) {
return;
}
handler(event);
};
document.addEventListener("mousedown", listener);
document.addEventListener("touchstart", listener);
return () => {
document.removeEventListener("mousedown", listener);
document.removeEventListener("touchstart", listener);
};
},
// Add ref and handler to effect dependencies
// It's worth noting that because passed in handler is a new ...
// ... function on every render that will cause this effect ...
// ... callback/cleanup to run every render. It's not a big deal ...
// ... but to optimize you can wrap handler in useCallback before ...
// ... passing it into this hook.
[ref, handler]
);
}
Doing my best to hide this sidebar when a user clicks anywhere outside of the focused area.
Currently, when I click outside, the sidebar remains open. If I click in an area within the sidebar that isnt a link, the sidebar closes. Just trying to recreate that effect elsewhere in the app.
Have no idea to implement this in React/JS
Thank you for any help you can send my way.
import React, { useState } from 'react';
import * as FaIcons from 'react-icons/fa';
import * as AiIcons from 'react-icons/ai';
import { Link } from 'react-router-dom';
import { NavbarData } from './NavbarData';
import '../styles/Navbar.css';
import { IconContext } from 'react-icons';
import onClickOutsideHOC from "react-onclickoutside"; //<-- Should I be using this?
function Navbar() {
const [sidebar, setSidebar] = useState(false);
const showSidebar = () => setSidebar(!sidebar);
const hideSidebar = () => setSidebar(onClickOutsideHOC()); //<-- Should I be using this?
return (
<>
<IconContext.Provider value={{ color: '#fff' }}>
<div className='navbar'>
<Link to='#' className='menu-bars'>
<FaIcons.FaBars onClick={showSidebar} />
</Link>
</div>
<nav className={sidebar ? 'nav-menu active' : 'nav-menu'}>
<ul className='nav-menu-items' onClick={showSidebar}>
<li className='navbar-toggle'>
<Link to='#' className='menu-bars'>
<AiIcons.AiOutlineClose />
</Link>
</li>
{NavbarData.map((item, index) => {
return (
<li key={index} className={item.cName}>
<Link to={item.path}>
{item.icon}
<span>{item.title}</span>
</Link>
</li>
);
})}
</ul>
</nav>
//Commented Out My Attempt Below
{/* <nav>
<nav className={sidebar ? 'nav-menu inactive' : 'nav-menu'}>
<ul className='nav-menu-items' onClick={hideSidebar}>
<li className='navbar-toggle'>
<Link to='#' className='menu-bars'>
<AiIcons.AiOutlineClose />
</Link>
</li>
{NavbarData.map((item, index) => {
return (
<li key={index} className={item.cName}>
<Link to={item.path}>
{item.icon}
<span>{item.title}</span>
</Link>
</li>
);
})}
</ul>
</nav>
</nav>*/}
</IconContext.Provider>
</>
);
}
export default Navbar;
For this, you can create a custom hook that takes the element you want as a parameter. I created a sandbox for you.
https://codesandbox.io/s/outside-click-hook-uc8bo
We send the ref of the element to the custom hook we created.
const boxRef = useRef(null);
// boxOutsideClick will be true on outside click
const boxOutsideClick = OutsideClick(boxRef);
<div ref={boxRef} className="box">
<h1>Click outside of me</h1>
</div>
And our hook will look like this:
export default function OutsideClick(ref) {
const [isClicked, setIsClicked] = useState();
useEffect(() => {
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
setIsClicked(true);
} else {
setIsClicked(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [ref]);
return isClicked;
}
OnMouseDown, we check if the clicked place is in the selected element and update the isClicked value and return.
I got a hack,
add another empty div with the CSS position absolute before the sidebar and give the sidebar the same position absolute and style as you want.
Let the first div have a full view-width and view-hight.
The sidebar can have whatever size you want but it will be on higher in z-index then the first div/sibling.
Add an onClick function on the first div to close the sidebar because when someone clicks outside of the sidebar, it will be a click on the first div.
To close a side navigation bar when the user clicks outside of it in a React application, you can add an event listener to the 'document' object that listens for 'mousedown' events. When the user clicks outside the side navigation bar, the event listener will trigger a function that closes the side navigation bar.
Here's some sample code to achieve this:
import React, { useEffect, useRef } from 'react';
function App() {
const sideNavRef = useRef(null);
useEffect(() => {
// Add event listener to the document object
document.addEventListener('mousedown', handleClickOutside);
// Remove event listener when the component unmounts
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
function handleClickOutside(event) {
if (sideNavRef.current && !sideNavRef.current.contains(event.target)) {
// Clicked outside the side navigation bar, close it
// Implement your close side navigation bar logic here
}
}
return (
<div>
<div ref={sideNavRef}>
{/* Your side navigation bar content goes here */}
</div>
<div>
{/* Other content of your application goes here */}
</div>
</div>
);
}
Explanation:
First, we define a sideNavRef using the useRef hook. This reference will be used to check if the click happened inside or outside of the side navigation bar.
We use the useEffect hook to add an event listener to the document object when the component mounts. The useEffect hook's cleanup function is used to remove the event listener when the component unmounts.
The handleClickOutside function checks if the click happened outside of the side navigation bar using the contains method on the sideNavRef reference. If the click happened outside of the side navigation bar, you can implement your close side navigation bar logic in this function.
Finally, we wrap the side navigation bar content inside a div with the ref attribute set to sideNavRef. The other content of your application goes into a separate div.
So here is the problem which I can't seem to solve. I have an app component, inside of App I have rendered the Show Component. Show component has toggle functionality as well as a outside Click Logic. In the Show component I have a Button which removes an item based on his Id, problem is that When I click on the button Remove. It removes the item but it also closes the Show Component, I don't want that, I want when I press on button it removes the item but does not close the component. Thanks
App.js
const App =()=>{
const[isShowlOpen, setIsShowOpen]=React.useState(false)
const Show = useRef(null)
function openShow(){
setIsShowOpen(true)
}
function closeShowl(){
setIsShowOpen(false)
}
const handleShow =(e)=>{
if(show.current&& !showl.current.contains(e.target)){
closeShow()
}
}
useEffect(()=>{
document.addEventListener('click',handleShow)
return () =>{
document.removeEventListener('click', handleShow)
}
},[])
return (
<div>
<div ref={show}>
<img className='taskbar__iconsRight' onClick={() =>
setIsShowOpen(!isShowOpen)}
src="https://winaero.com/blog/wp-content/uploads/2017/07/Control-
-icon.png"/>
{isShowOpen ? <Show closeShow={closeShow} />: null}
</div>
)
}
Show Component
import React, { useContext } from 'react'
import './Show.css'
import { useGlobalContext } from '../../context'
import WindowsIcons from '../../WindowsIcons/WindowsIcons'
import { GrClose } from 'react-icons/gr'
const Show = ({closeShow}) => {
const {remove, icons }= useGlobalContext()
}
return (
<div className='control__Panel'>
<div className='close__cont'>
<GrClose className='close' onClick={closeShow} />
<h3>Show</h3>
</div>
<div className='control__cont'>
{icons.map((unin)=>{
const { name, img, id} = unin
return (
<li className='control' key ={id}>
<div className='img__text'>
<img className='control__Img' src={img} />
<h4 className='control__name'>{name}</h4>
</div>
<button className='unin__button' onClick={() => remove(id)} >remove</button>
</li> )
})}
</div>
</div>
)
}
export default Show
Try stop propagation function, it should stop the propagation of the click event
<button
className='unin__button'
onClick={(e) => {
e.stopPropagation();
remove(id);
}}
>remove</button>
You have a few typos in your example. Are they in your code? If they are, you're always reach the closeShow() case in your handler, since you're using the wrong ref.
const App =()=>{
const[isShowOpen, setIsShowOpen]=React.useState(false) <<-- Here 'isShowlOpen'
const show = useRef(null) <<-- here 'S'how
function openShow(){
setIsShowOpen(true)
}
function closeShow(){ <<-- Here 'closeShowl'
setIsShowOpen(false)
}
const handleShow =(e)=>{
if(show.current&& !show.current.contains(e.target)){ <<-- here 'showl'
closeShow()
}
}
useEffect(()=>{
document.addEventListener('click',handleShow)
return () =>{
document.removeEventListener('click', handleShow)
}
},[])
return (
<div>
<div ref={show}>
<img className='taskbar__iconsRight' onClick={() =>
setIsShowOpen(!isShowOpen)}
src="https://winaero.com/blog/wp-content/uploads/2017/07/Control-
-icon.png"/>
{isShowOpen ? <Show closeShow={closeShow} />: null}
</div>
)
}
So here I have an App component where I have rendered the Navbar. I have a toggle function and logic on outside Click using useRef/useEffect. My intentions are when I click on the div where Navbar is located, I want to copy that div/img to specific part of the page using Css, and when it's moved I want in that copy text/h4 to be removed/dissapear. I would appriciate any help. Thanks
function App() {
const [isNavbarOpen, setIsNavbarOpen] = useState(false)
const navbar = useRef()
const openNavbar=()=>{
setIsNavbarOpen(true)
}
const closeNavbar=()=>{
setIsNavbarOpen(false)
}
const handleNavbar=(e)=>{
if(navbar.current && !navbar.current.contains(e.target)){
closeNavbar()
}
}
useEffect(()=>{
document.addEventListener('click', handleNavbar)
return() =>{
document.removeEventListener('click', handleNavbar)
}
},[])
return (
<>
<div className='smallProjects__container'
onClick={() => setIsNavbarOpen(!isNavbarOpen)} ref={navbar}>
<img
src="./images/icons/folr.png"/>
<h4>Navbar</h4>
{isNavbar? <Navbar closeNavbar={closeNavbar} />: null}
</div>
</>
);
}
export default App;
I'm building out a simple accordion component for a product page in my Next.js/react app. I've got it mostly working, however when a user clicks open a new accordion item I need to close the active one. Here's what my component looks like:
import React, { useRef, useState } from 'react';
import css from 'classnames';
import s from './ProductAccordion.module.scss';
interface FeatureProps {
title: string;
copy: string;
}
export const ProductAccordion = ({ content }: any) => {
return (
<div className={s.productAccordion}>
{content.features.map((feature: FeatureProps) => {
const [active, setActive] = useState(false);
const activeClass = active ? 'active' : '';
const toggleAccordion = () => {
setActive(!active);
};
return (
<div
className={css(s.productAccordion__section, s[activeClass])}
key={feature.title}
>
<button className={s.sectionTitle} onClick={toggleAccordion}>
<p className={s.sectionTitle__title}>{feature.title}</p>
<span className={s.button} />
</button>
<div className={css(s.sectionContent, s[activeClass])}>
<div className={s.sectionContent__copy}>{feature.copy}</div>
</div>
</div>
);
})}
</div>
);
};
How can I get my active accordion item to close when a new one is clicked? Thanks!
I would suggest:
moving your useState hook a level higher
instead of "active" being a boolean, make it a string that you can use to identify which item should be active
hopefully a feature has a unique identifier like an id or something that you can use to identify
You could do something like:
export const ProductAccordion = ({ content }: any) => {
const [active, setActive] = useState(''); // set up your useState here, so its value is available to all children elements
return (
<div className={s.productAccordion}>
{content.features.map((feature: FeatureProps) => {
const isActive = active === feature.id // feature.id here is just a stand in for some unique identifier that each feature has
const activeClass = isActive ? 'active' : '';
const toggleAccordion = () => {
if (isActive) {
setActive(''); // if the current item is active, and you toggle it, close the accordian
} else {
setActive(feature.id) // if the current item is not active, and you toggle it, open this section
}
};
return (
<div>
{/* your code here */}
</div>
);
};
Of course, there are many approaches you could take, and I'm sure a more elegant one than this exists. But this should hopefully get you in the right direction!