In the code below, when I click on the burger menu, the design changes from burger to an X and the menu opens successfully. However, when I click on a link within the menu, the menu does close BUT the X menu does NOT change its design back to a burger.In other words, the menu closes but the css of the icon does not change back. Does anyone know what I am doing wrong?
const handleClick = (event: any) => {
if (!menuOpen) {
event.currentTarget.classList.add("open");
setMenu(true);
} else {
event.currentTarget.classList.remove("open");
setMenu(false);
}
};
<div className="mt-4 container page-container">
<div className="row">
<div className="d-grid gap-2 pb-3 col-4 mx-auto ">
<div className="menu-btn" onClick={handleClick}>
<div className="menu-btn_burger"></div>
</div>
</div>
</div>
{menuOpen && (
<div className="row">
<div className="d-grid gap-2 pt-3 col-6 mx-auto ">
<Link to="/add" onClick={handleClick} className="btn btn-primary btn-sm ">
Add Task
</Link>
<Link to="/" onClick={handleClick} className="btn btn-primary btn-sm">
All Reminders <span className="badge badge-light text-dark">{tasks.length > 0 && tasks.length}</span>
</Link>
</div>
</div>}
The problem is from the event target
In the first case, the target is the div with class menu-button but in the second case, it is referring to the link element.
How to solve it?
Use Refs
const menuRef = useRef(null);
const handleClick = (event: any) => {
if (!menuOpen) {
menuRef.current.classList.add("open");
setMenu(true);
} else {
menuRef.current.classList.remove("open");
setMenu(false);
}
};
<div className="mt-4 container page-container">
<div className="row">
<div className="d-grid gap-2 pb-3 col-4 mx-auto ">
<div ref={menuRef} className="menu-btn" onClick={handleClick}>
<div className="menu-btn_burger"></div>
</div>
</div>
</div>
{menuOpen && (
<div className="row">
<div className="d-grid gap-2 pt-3 col-6 mx-auto ">
<Link to="/add" onClick={handleClick} className="btn btn-primary btn-sm ">
Add Task
</Link>
<Link to="/" onClick={handleClick} className="btn btn-primary btn-sm">
All Reminders <span className="badge badge-light text-dark">{tasks.length > 0 && tasks.length}</span>
</Link>
</div>
</div>}
see more in the official docs
React Ref
Related
I would like to do some changes to the Ui by sliding the slider with Carousel tailwind and NextJS, yet I have no idea to do so?!
this is an example of my code
...
const slider = sliderData.map((item, index) => {
return (
<>
<div
className={`carousel-item ${
index == 0 && "active"
} relative float-left w-full`}
>
<Link href={item.link + `?storeId=${state.id}`}>
<button className="block w-full">
<img src={item.image} className="block w-full" alt="..." />
</button>
</Link>
<div className="carousel-caption hidden md:block absolute text-center">
<h5 className="text-xl">{item.title}</h5>
<p>{item.description}</p>
</div>
</div>
</>
);
});
const sliderButton = sliderData.map((item, index) => {
return (
<>
<button
type="button"
data-bs-target="#carouselExampleCaptions"
data-bs-slide-to={`${index}`}
className="active"
aria-current="true"
aria-label={item.title}
></button>
</>
);
});
...
return (
<>
<div className="flex flex-col">
<div
id="carouselExampleCaptions"
className="carousel slide relative w-screen"
data-bs-ride="carousel"
>
<div className="carousel-indicators absolute right-0 bottom-0 left-0 flex justify-center p-0 mb-4">
{sliderButton}
</div>
<div className="carousel-inner relative w-full overflow-hidden">
{slider}
</div>
<button
className="carousel-control-prev absolute top-0 bottom-0 flex items-center justify-center p-0 text-center border-0 hover:outline-none hover:no-underline focus:outline-none focus:no-underline left-0"
type="button"
data-bs-target="#carouselExampleCaptions"
data-bs-slide="prev"
>
<span
className="carousel-control-prev-icon inline-block bg-no-repeat"
aria-hidden="true"
></span>
<span className="visually-hidden">Previous</span>
</button>
<button
className="carousel-control-next absolute top-0 bottom-0 flex items-center justify-center p-0 text-center border-0 hover:outline-none hover:no-underline focus:outline-none focus:no-underline right-0"
type="button"
data-bs-target="#carouselExampleCaptions"
data-bs-slide="next"
>
<span
className="carousel-control-next-icon inline-block bg-no-repeat"
aria-hidden="true"
></span>
<span className="visually-hidden">Next</span>
</button>
</div>
<div>
<CatalogView
store={state.loadedStore}
categories={state.loadedCategories}
products={state.loadedProducts}
selectedCategory={state.selectedCategory}
/>
</div>
</div>
</>
);
the changes I'm trying to do is to change the selectedCategory on slide, which are stored in the state object and the state hook to change them, yet how to let's say if the user tap on next or slide the slider to call setState.
The code is for mobile menu but when I open it, it stays open and won't close.
I want it to close by clicking the menu links and tapping outside.
I know I haven't even added in the code but I don't know how to add into it.
Can anyone help me with the code? Any help is appreciated!
Here is the code :
import { useState } from "react";
import { HiMenuAlt3, HiX } from "react-icons/hi";
export default function NavBar() {
const [navbar, setNavbar] = useState(false);
return (
<nav className="w-full bg-white shadow">
<div className="justify-between px-4 mx-auto lg:max-w-7xl md:items-center md:flex md:px-8">
<div>
<div className="flex items-center justify-between py-3 md:py-5 md:block">
<a href="javascript:void(0)">
<h2 className="text-2xl font-bold">LOGO</h2>
</a>
<div className="md:hidden">
<button
className="p-2 text-gray-700 rounded-md outline-none focus:border-gray-400 focus:border"
onClick={() => setNavbar(!navbar)}
>
{navbar ? (
<HiX className="text-4xl" /> // Close icon
) : (
<HiMenuAlt3 className="text-4xl" /> // Open icon
)}
</button>
</div>
</div>
</div>
<div>
<div
className={`flex-1 justify-self-center pb-3 mt-8 md:block md:pb-0 md:mt-0 ${
navbar ? "block" : "hidden"
}`}
>
<ul className="items-center justify-center space-y-8 md:flex md:space-x-6 md:space-y-0">
<li className="text-gray-600 hover:text-blue-600">
Home
</li>
<li className="text-gray-600 hover:text-blue-600">
Blog
</li>
<li className="text-gray-600 hover:text-blue-600">
About US
</li>
<li className="text-gray-600 hover:text-blue-600">
Contact US
</li>
</ul>
</div>
</div>
</div>
</nav>
);
}
You do have code to close it, setNavbar(false). What you need is a hooked called useOnClickOutside. https://usehooks.com/useOnClickOutside/
To use it,
function NavBar() {
const ref = useRef()
useOnClickOutside(ref, () => setNavbar(false));
return (
<nav ref={ref}>
...
</nav>
)
}
I just want to ask help with the comment section down below of the JavaScript Code. I want to work the toggle button / hamburger button but it does not work because addEventListener is not required for ReactJS. What code must be encoded? Thank you.
I am doing a Traversy Media Tutorial using Tailwind CSS. I want to use React as framework because of its composable component feature and efficiency to use.
import React from 'react';
import logo from '../images/logo.svg';
// const btn = document.getElementById('menu-btn')
// const nav = document.getElementById('menu')
// btn.addEventListener('click', () => {
// btn.classList.toggle('open')
// nav.classList.toggle('flex')
// nav.classList.toggle('hidden')
// })
const Navbar = () => {
return (
<div>
<nav className='relative container mx-auto p-6'>
{/* Flex Container */}
<div className='flex items-center justify-between'>
{/* Logo */}
<div className='pt-2'>
<img src={logo} alt='logo.svg'></img>
</div>
{/* Menu Items */}
<div className='hidden space-x-6 md:flex'>
<a href='/' className='hover:text-darkGrayishBlue'>
Pricing
</a>
<a href='/' className='hover:text-darkGrayishBlue'>
Product
</a>
<a href='/' className='hover:text-darkGrayishBlue'>
About Us
</a>
<a href='/' className='hover:text-darkGrayishBlue'>
Careers
</a>
<a href='/' className='hover:text-darkGrayishBlue'>
Community
</a>
</div>
{/* Button */}
<div>
<a
href='/'
className='hidden p-3 px-6 pt-2 text-white bg-brightRed rounded-full baseline hover:bg-brightRedLight md:block'
>
Get Started
</a>
</div>
{/* Hambuger Icon */}
<button
id='menu-btn'
className='block hamburger md:hidden focus:outline-none'
>
<span className='hamburger-top'></span>
<span className='hamburger-middle'></span>
<span className='hamburger-bottom'></span>
</button>
</div>
{/* Mobile Menu */}
<div className='md:hidden'>
<div
id='menu'
className='absolute flex-col items-center hidden self-end py-8 mt-10 space-y-6 font-bold bg-white sm:w-auto sm:self-center left-6 right-6 drop-shadow-md'
>
<a href='/'>Pricing</a>
<a href='/'>Product</a>
<a href='/'>About</a>
<a href='/'>Careers</a>
<a href='/'>Community</a>
</div>
</div>
</nav>
</div>
);
};
export default Navbar;
What you could do is define an open state and a click listener for the button.
Then change the class of the objects based on the open flag:
import React from 'react';
import logo from '../images/logo.svg';
const Navbar = () => {
const [open, setOpen] = useState(false);
const handleMenuClick = () => {
setOpen((prev) => !prev);
};
return (
<div>
<nav className='relative container mx-auto p-6'>
{/* Flex Container */}
<div className='flex items-center justify-between'>
{/* ... */}
{/* Hambuger Icon */}
<button
id='menu-btn'
className={`block hamburger md:hidden focus:outline-none ${
open && 'open'
}`}
onClick={() => handleMenuClick()}
>
<span className='hamburger-top'></span>
<span className='hamburger-middle'></span>
<span className='hamburger-bottom'></span>
</button>
</div>
{/* Mobile Menu */}
<div className='md:hidden'>
<div
id='menu'
className={`absolute flex-col items-center hidden self-end py-8 mt-10 space-y-6 font-bold bg-white sm:w-auto sm:self-center left-6 right-6 drop-shadow-md ${
open ? 'flex' : 'hidden'
}`}
>
<a href='/'>Pricing</a>
<a href='/'>Product</a>
<a href='/'>About</a>
<a href='/'>Careers</a>
<a href='/'>Community</a>
</div>
</div>
</nav>
</div>
);
};
export default Navbar;
```
You need to define the event handling where you define your button. In your case, it would look something like this:
<button
id='menu-btn'
className='block hamburger md:hidden focus:outline-none'
onClick={doAction}>
>
Official documentation regarding event handling in react.
I want to include my - button in my gray bar. As you can see in the picture, my blue button exceeds the gray bar
export default function App() {
...
return (
<div className="text-md font-bold">
Invitee {i + 1}
<span className="float-right ">
{!!i && (
<Button className="rounded-md float-right mt-4" primary onClick={() => removeInvitee(Id)}>
-
</Button>
)}
</span>
...
)
You can use the flexbox properties to make the design you want. I had created the very similar example below , please add your functions here and replace class with className.
<script src="https://cdn.tailwindcss.com"></script>
<div class="container mx-auto py-4">
<div class="bg-gray-200 text-md flex items-center justify-between rounded-r-md">
<div class=" font-bold text-gray-500 ml-4" >Invitee 2</div>
<div class="">
<Button primary class="rounded-md bg-black text-white py-2 px-4" onclick="removeInvitee(pId)">
-
</Button>
</div>
</div>
</div>
const [isAgent, setAgent] = useState(true);
{isSupervisor.map((value, index) => {
return <div className="text-gray-700 rounded-lg" key={index}>
{isAgent ? <div className="flex flex-row">
<div className=" flex-none text-center">
<div>Halo</div>
</div>
<div className="flex-grow text-gray-700 px-2 py-1 my-auto">
<button onClick={() => setAgent(true)} className="bg-custom-14 ">Edit</button>
</div>
</div>
</div>
</div> : <div>
<div className="flex flex-col my-2 mx-8">
<div className=" flex-none text-center">
<div>Halo</div>
</div>
<div className="flex justify-end">
<button onClick={() => setAgent(false)} className="bg-custom-14">Discard</button>
</div>
</div> </div>
problem:
when i click a condition button, all button follow the condition, is there any solution to make a separate condition in every button ? thanks
I write this answer assuming that in this list of sections with buttons you only need to change one section on clicking 'edit'.
Since isAgent and setAgent affect all sections at once, your state should have a map of states for each sections, isAgent should be a function to retrieve balue from this state by a section key, and setAgent should accept the section key.
So, your react state may look ike this:
class State {
editingAgents = {};
isAgent = (index) => editingAgents[index];
setAgents = (index, isAgent) => editingAgents[index] = isAgent;
}
and modified component code may be like this (notice how isAgent and setAgent are used now)
const [isAgent, setAgent] = useState(true);
{isSupervisor.map((value, index) => {
return <div className="text-gray-700 hover:bg-custom-8 rounded-lg" key={index}>
{isAgent(index) ? <div className="flex flex-row">
<div className=" flex-none text-center mx-2 my-1">
<img src={Ane} className=" w-54px lg:w-50px object-contain"></img>
</div>
<div className="flex-grow text-gray-700 px-2 py-1 my-auto">
<button onClick={() => setAgent(index, true)} className="bg-custom-14 ">Edit</button>
</div>
</div>
</div>
</div> : <form onSubmit={handleSubmit(updateAgent)}>
<div className="flex flex-col my-2 mx-8">
<div className="m-2 lg:mt-4 hidden">
<div className="inline-block w-3/12 text-base lg:text-sm font-semibold">Id</div>
</div>
<div className="flex justify-end">
<button onClick={() => setAgent(index, false)} className="bg-custom-14">Discard</button>
</div>
</div> </form>