Unable to implement a side Navbar using React - javascript

I wanted to implement a hamburger side navbar using react but I am unable to implement the changes that must be displayed on Clicking the hamburger. I am still learning React so please explain what am I doing wrong and feel free to suggest an alternative approach. Here is my code.
import React from 'react';
import * as FaIcons from "react-icons/fa";
class Navbar extends React.Component {
constructor() {
super();
this.state = {
navbarState : 1,
style : {}
}
}
openNav() {
this.setState({"width" : "250px"});
}
closeNav() {
this.setState({"width" : "0px"});
}
handleClick() {
this.setState((prevState) => {
if(prevState.navbarState === 0) {
return {navbarState : 1, style : {width : '250px'}};
}
else {
return {navbarState : 0, style : {width : '0px'}};
}
});
}
render() {
return (
<div>
<div id="mySidenav" className="sidenav" style={this.state.style} >
{console.log(this.state.navbarState)}
<a href="#" className="closebtn" onClick={() => this.handleClick}>×</a>
About
Services
Clients
Contact
</div>
<span onClick={() => this.handleClick}>
{/* <FaIcons.FaBars /> */}
<div className = "hamburger">
<div className="line"></div>
<div className="line"></div>
<div className="line"></div>
</div>
</span>
</div>
);
}
}
export default Navbar;
And my CSS is as follows :
.hamburger{
position: absolute;
cursor: pointer;
margin: 5px;
right: 5%;
top: 5%;
}
.line{
width: 30px;
height: 3px;
background: white;
margin: 5px;
}
.sidenav {
height: 100%; /* 100% Full-height */
width: 0; /* 0 width - change this with JavaScript */
position: fixed; /* Stay in place */
z-index: 1; /* Stay on top */
top: 0; /* Stay at the top */
right: 0;
background-color: black; /* Black*/
overflow-x: hidden; /* Disable horizontal scroll */
padding-top: 60px; /* Place content 60px from the top */
transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */
}
.sidenav-active {
height: 100%; /* 100% Full-height */
width: 250px; /* 0 width - change this with JavaScript */
position: fixed; /* Stay in place */
z-index: 1; /* Stay on top */
top: 0; /* Stay at the top */
right: 0;
background-color: black; /* Black*/
overflow-x: hidden; /* Disable horizontal scroll */
padding-top: 60px; /* Place content 60px from the top */
transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */
}
/* The navigation menu links */
.sidenav a, .sidenav-active a {
padding: 8px 8px 8px 32px;
text-decoration: none;
font-size: 25px;
color: white;
display: block;
transition: 0.3s;
}
/* When you mouse over the navigation links, change their color */
.sidenav a:hover, .sidenav-active a:hover {
color: #f1f1f1;
}
/* Position and style the close button (top right corner) */
.sidenav .closebtn, .sidenav-active .closebtn {
position: absolute;
top: 0;
right: 25px;
font-size: 36px;
margin-left: 50px;
}

First it looks like as you're using a Class-Based Component and you're not binding your handleClick function correctly. Solution to this is either to bind it in your constructor or use an arrow function like so:
handleClick = () => { ... }
This will apply similarly to your other two functions. For further information on Arrow Functions and Binding your functions within Class-Based Components see: https://reactjs.org/docs/faq-functions.html
Secondly, I suspect that handleClick being called in your <span>. Instead of
<span onClick={() => this.handleClick} ... />
Try
<span onClick={this.handleClick} ... /> or <span onClick={() => this.handleClick()} ... />
Let me know if this helps!

Related

sidebar navigation text not smooth while transition

i try to change css but not work for text short text is ok but i have to put long paragraph when click it not smooth while transition working i use template from w3school, i want text show up smooth like button or short text please advise me how I should use it to remember for next time i can help in community if i saw someone ask like me thank you.
/* Set the width of the side navigation to 250px */
function openNav() {
document.getElementById("mySidenav").style.width = "400px";
}
/* Set the width of the side navigation to 0 */
function closeNav() {
document.getElementById("mySidenav").style.width = "0";
}
/* The side navigation menu */
.sidenav {
height: 100%; /* 100% Full-height */
width: 0; /* 0 width - change this with JavaScript */
position: fixed; /* Stay in place */
z-index: 1; /* Stay on top */
top: 0;
left: 0;
background-color: #111; /* Black*/
overflow-x: hidden; /* Disable horizontal scroll */
padding-top: 60px; /* Place content 60px from the top */
transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */
}
/* The navigation menu links */
.sidenav a {
padding: 8px 8px 8px 32px;
text-decoration: none;
font-size: 25px;
font-family: Gotham;
color: #818181;
display: block;
transition: 0.3s
}
/* When you mouse over the navigation links, change their color */
.sidenav a:hover, .offcanvas a:focus{
color: #f1f1f1;
}
/* Position and style the close button (top right corner) */
.sidenav .closebtn {
position: absolute;
top: 0;
right: 25px;
font-size: 36px;
margin-left: 50px;
}
/* Style page content - use this if you want to push the page content to the right when you open the side navigation */
#main {
transition: margin-left .5s;
padding: 20px;
}
/* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */
#media screen and (max-height: 450px) {
.sidenav {padding-top: 15px;}
.sidenav a {font-size: 18px;}
}
<body bgcolor="#E6E6FA">
<div id="mySidenav" class="sidenav">
×
<p style=color:white>TEST TEST TEST TEST TEST TEST TEST TEST
TEST TEST TEST TEST
TEST TEST TEST TEST
TEST TEST TEST TEST
TEST TEST TEST TEST</p>
</div>
<span onclick="openNav()"><img src="https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/menu-alt-512.png" width="40px" style="padding-top: 40px; padding-left: 40px;"></span>
<div id="main">
</div>
You don't need two separate JavaScript functions. Only one called toggleNav
Use Element.classList.toggle() to toggle a CSS class
Don't animate the width. Use transform: translateX(-100%) to hide the Nav, and translateX(0%) to show (open).
Don't use links if you actually want buttons. Links (Anchors) are used to navigate
Don't use inline on* JS attributes and style attributes
const toggleNav = () => {
document.querySelector("#mySidenav").classList.toggle("is-open");
};
document.querySelectorAll(".toggleNav").forEach(el => {
el.addEventListener("click", toggleNav)
});
/* QuickReset */ * {margin:0; box-sizing: border-box;}
#mySidenav {
position: fixed;
overflow-x: hidden;
width: calc(100vw - 200px); /* try not to use fixed px */
height: 100vh;
z-index: 200;
top: 0;
left: 0;
padding: 60px;
background-color: #111;
color: #fff;
transition: 0.5s;
transform: translateX(-100%); /* hide it by minus own width */
}
#mySidenav.is-open {
transform: translateX(0%); /* show it */
}
.toggleNav {
padding: 1rem 1.3rem;
background: none;
border: none;
font-family: Gotham;
font-size: 2rem;
cursor: pointer;
}
.toggleNav:hover {
color: #999;
}
#mySidenav .toggleNav {
position: absolute;
top: 0;
right: 0;
color: #777;
}
<div id="mySidenav">
<button type="button" class="toggleNav">✕</button>
<p>
TEST TEST TEST TEST TEST TEST
TEST TEST TEST TEST TEST TEST
TEST TEST TEST TEST TEST TEST
TEST TEST TEST TEST TEST TEST
</p>
</div>
<button type="button" class="toggleNav">☰</button>
<div id="main"></div>
If I haven't understood it wrong you want to make text word position won't change then write the following code:
<p id="sidenavText">TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST</p>
Set width on the text too
var sidenavWidth = 400;
document.querySelector("#sidenavText").style.width = sidenavWidth + "px";
function openNav() {
document.getElementById("mySidenav").style.width = sidenavWidth + "px";
}
function closeNav() {
document.getElementById("mySidenav").style.width = "0";
}
var sidenavWidth = 400;
document.querySelector("#sidenavText").style.width = sidenavWidth + "px";
function openNav() {
document.getElementById("mySidenav").style.width = sidenavWidth + "px";
}
function closeNav() {
document.getElementById("mySidenav").style.width = "0";
}
.sidenav {
height: 100%; /* 100% Full-height */
width: 0; /* 0 width - change this with JavaScript */
position: fixed; /* Stay in place */
z-index: 1; /* Stay on top */
top: 0;
left: 0;
background-color: #111; /* Black*/
overflow-x: hidden; /* Disable horizontal scroll */
padding-top: 60px; /* Place content 60px from the top */
transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */
}
/* The navigation menu links */
.sidenav a {
padding: 8px 8px 8px 32px;
text-decoration: none;
font-size: 25px;
font-family: Gotham;
color: #818181;
display: block;
transition: 0.3s
}
/* When you mouse over the navigation links, change their color */
.sidenav a:hover, .offcanvas a:focus{
color: #f1f1f1;
}
/* Position and style the close button (top right corner) */
.sidenav .closebtn {
position: absolute;
top: 0;
right: 25px;
font-size: 36px;
margin-left: 50px;
}
/* Style page content - use this if you want to push the page content to the right when you open the side navigation */
#main {
transition: margin-left .5s;
padding: 20px;
}
/* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */
#media screen and (max-height: 450px) {
.sidenav {padding-top: 15px;}
.sidenav a {font-size: 18px;}
}
<body bgcolor="#E6E6FA">
<div id="mySidenav" class="sidenav">
×
<p id="sidenavText" style="color:white">TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST</p>
</div>
<span onclick="openNav()"><img src="https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/menu-alt-512.png" width="40px" style="padding-top: 40px; padding-left: 40px;"></span>
<div id="main">
</div>

Text appear/disappear on top of image with button toggle

In mobile, I'm trying to create a toggle that appears on top of an image, that when tapped on, makes text appear on top of the image too.
I basically want to recreate how The Guardian newspaper handles the little (i) icon in the bottom right corner on mobile.
And on desktop, the the text is there by default under the image and the (i) icon is gone.
So far I've managed to find a similar solution elsewhere online but it's not quite working right as I need it to.
function toggleText() {
var text = document.getElementById("demo");
if (text.style.display === "none") {
text.style.display = "block";
} else {
text.style.display = "none";
}
}
#blog {
width: 100%;
}
#blog figure {
position: relative;
}
#blog figure figcaption {
display: none;
position: absolute;
bottom: 0;
left: 0;
box-sizing: border-box;
width: 100%;
color: black;
text-align: left;
background: rgba(0, 0, 0, 0.5);
}
#blog figure button {
position: absolute;
bottom: 0;
right: 0;
color: black;
border: 5px solid black;
}
<div id="blog">
<figure>
<img src="https://cdn2.hubspot.net/hubfs/4635813/marble-around-the-world.jpg" alt="A photo of a slab of marble for example">
<figcaption id="demo" style='display: none'>A photo of a slab of marble for example</figcaption>
<button type='button' onclick="toggleText()">(i)</button>
</figure>
</div>
Don't use IDs. Your code should be reusable!
Don't use inline JS on* handlers, use Element.addEventListener() instead
Don't use inline style attributes.
Don't use el.style.display === "something" to check for display styles. Use Element.classList.toggle() instead
This straightforward example uses JavaScript to simply toggle a className "is-active" on the button's parent, the figure Element.
Everything else (icon symbol change, caption animation etc...) is handled purely by CSS:
document.querySelectorAll("figure button").forEach(EL_btn => {
EL_btn.addEventListener("click", () => {
EL_btn.closest("figure").classList.toggle("is-active");
});
});
/* QuickReset */ * {margin: 0; box-sizing: border-box;}
img {
max-width: 100%; /* Never extend images more than available */
}
figure {
position: relative;
overflow: hidden; /* overflow hidden to allow figcaption hide bottom */
}
figure img {
display: block; /* prevent "bottom space" caused by inline elements */
}
figure figcaption {
position: absolute;
width: 100%;
bottom: 0;
padding: 1rem;
padding-right: 4rem; /* Prevent text going under the button icon */
color: #fff;
background: rgba(0, 0, 0, 0.5);
transform: translateY(100%); /* Move down, out of view */
transition: transform 0.3s; /* Add some transition animation */
}
figure.is-active figcaption {
transform: translateY(0%); /* Move into view */
}
figure button {
position: absolute;
width: 2rem;
height: 2rem;
bottom: 0.5rem;
right: 0.5rem;
border-radius: 50%;
color: #fff;
background: rgba(0, 0, 0, 0.5);
border: 0;
cursor: pointer;
}
figure button::before {
content: "\2139"; /* i icon */
}
figure.is-active button::before {
content: "\2A09"; /* x icon */
}
<figure>
<img src="https://cdn2.hubspot.net/hubfs/4635813/marble-around-the-world.jpg" alt="A photo of a slab of marble for example">
<figcaption>A photo of a slab of marble for example</figcaption>
<button type="button"></button>
</figure>
The above will work for any number of such elements on your website without the need to add any more CSS or JS.
I see a couple things that could mess this up, one is the fact that there is nothing to make your image adjust to your mobile screen, more-over there is also margin that is there by default, so I suggest these changes to the CSS:
First I'd set box-sizing to border-box and margin to 0, this should be a regular practice by the way.
*{
box-sizing: border-box;
margin: 0;
}
Then select the image and make it adjust to your page as such
#blog figure img{
height: auto;
width:100%;
}
Finally, for some styling you can add some padding to your blog div to make the image slightly smaller on your screen
#blog {
width: 100%;
padding: 35px;
}
This is the Fiddle for it.

Unable to give priority to button clicks when sidebar is toggled on smaller screens

Here's my codepen:
https://codepen.io/rjtbansal/pen/BaLawaO
Basically I have Expand/Collapse button which is toggling to show/hide sidebar. On smaller screens (less than 500px width) it should auto-collapse. I have been able to accomplish this with a media query. However, I would like the button take precedence so even after auto-collapsing when the button is clicked it should then expand/collapse the sidebar.
Here's the HTml:
<body>
<div id="sidebar" class="sidebar">
Sidebar
</div>
<button id="toggle"> Expand/Collapse </button>
</body>
css:
body {
background-color: #1d2126;
color: white;
height: 100%;
}
.sidebar {
width: 300px;
background-color: yellow;
color: black;
height: 100%;
position: absolute;
}
.sidebar--collapse {
display: none;
}
.sidebar--expand {
display: block;
}
button {
position: absolute;
}
#media screen and (max-width: 500px){
.sidebar {
display: none;
}
}
js:
const toggleButton = document.querySelector("#toggle");
toggleButton.addEventListener("click", function() {
const sidebar = document.querySelector("#sidebar");
const sidebarClasses = sidebar.classList;
if(sidebarClasses.contains('sidebar--expand')){
sidebar.classList.remove('sidebar--expand');
sidebar.classList.add('sidebar--collapse');
}
else {
sidebar.classList.remove('sidebar--collapse');
sidebar.classList.add('sidebar--expand');
}
});
You can use the !imprtant keywork.
.sidebar--expand {
display: block !important;
}

React/Redux/CSS - Modal placement

i have a Card and a Modal component like so
class Card extends Component {
constructor() {
super();
this.state = { showModal: false }
}
render() {
const { showModal } = this.state;
return (
{/* Some code */}
{showModal && <Modal />}
)
}
}
So, I don't want to render my Modal until it is required. Logic.
The Modal's job, in this context, is to display the full content of the Card component (The Card has a fixed height, so I cannot displaying every bit of information in there).
Now, at a higher level I have the following:
class App extends Component {
/* Some Code */
render() {
return (
<div>
<Content /> {/* Our content */}
<Overlay /> {/* Our all-purpose-overlay (we need it for the Modal) */}
</div>
)
}
}
The problem is that my Card component has the style position: relative and the Modal component has the a z-index and the style position: absolute.
I did some research and I read that nested elements with non-static
position will not work the same way regarding the z-index. The z-index will take effect only within the non-static element.
So I cannot make my Modal appear above the the Overlay component. Furthermore, The Card component has a overflow:hidden property. And so, the Modal gets cropped.
Here is a simple demonstration.
function toggleModal() {
var app = document.getElementById("app");
app.classList.toggle("-modal");
}
:root {
background: #ddd;
}
#app,
#overlay {
height:100vh;
width:100%;
}
#app {
display: flex;
align-items: center;
justify-content: center;
}
/* Overlay */
#overlay {
position: absolute;
top: 0;
left: 0;
z-index: 1;
visibility: hidden;
background: rgba(0,0,0,0.3);
}
.-modal #overlay {
visibility: visible !important;
}
/* Card */
#card {
position: relative;
width: 250px;
padding: 1em;
border-radius: 5px;
background: white;
overflow: hidden; /* Problem */
}
.content {
padding: 4em 0;
text-align: center;
}
.btn {
padding: 1em 2em;
border:none;
color:white;
float: right;
cursor: pointer;
transition: 0.2s ease;
background: #00abff;
}
.btn:hover {
background: #0c9de4;
}
/* Modal */
#modal {
width: 300px;
position: absolute;
top:0;
left: 50px; /* Modal Gets clipped */
visibility: hidden;
background-color:red;
}
.-modal #modal {
visibility: visible;
}
<div id="app">
<div id="card">
<div class="content">content</div>
<button class="btn" onclick="toggleModal()">Display Modal</button>
<div id="modal">
<div class="content">
<div>Modal</div>
<div>With same content</div>
<div>(under the Overlay and also cropped)</div>
</div>
</div>
</div>
<div id="overlay" onclick="toggleModal()"></div>
</div>
Question : How should I organize my components so that I do not get this problem?
I thought of having only one Modal at top level. Then I simply display it. But I need to render the children of the Card into the Modal.
How should I proceed?
Since you are using React, what I would actually recommend is to use a Portal to render your modal. One thing Portals solve in React are precisely this issue. If you are using a more recent version of React (16+) then you already have access to portals.
If you are using an older version of React, that is okay. You can use an implementation like this (react-portal) instead.
Now you will have your components/markup rendered in a totally separate React tree outside of the context of your containing <div> that has position: relative on it and you can absolute or fixed position it wherever you need to.

Javascript function delete media query effect

I am working on a website that's look like this image:
The idea is when clicking on the button of the aside menu the aside menu will close and the bigwrapper will expand its size to fit the blank space so the result will look like this
I have written a function in Javascript to this and it just worked right, however, I wanted to make a media query that let the menu and the menu button disappear when the screen size get less than 840px it also worked
Now the problem is if someone clicked the button before the media query executed the media query effect will get disabled I think that javascript function delete the media query effect how can I fix that here is the code
<div id="mySidenav" class="sidenav">
<img class="menuicons" src="images/icons/menu/home2.png" alt="">Home
<img class="menuicons" src="images/icons/menu/offer.png" alt="">Offers
<img class="menuicons" src="images/icons/menu/cart4.png" alt="">Cart
<img class="menuicons" src="images/icons/menu/about.png" alt="">About us
<img class="menuicons" src="images/icons/menu/contact us.png" alt="">Contact us
</div>
<div id="menu_button" onclick="nav();categoriesScaler()">
<img src="images/icons/menu2.png" alt="Not availabale" />
</div>
<div id="bigwrapper">
<!--Some content goes here-->
</div>
here is the function
<script>
var hidden = false;
function nav()
{
if(hidden == false)
{
closeNav();
hidden = true;
}
else
{
openNav();
hidden = false;
}
}
function openNav()
{
document.getElementById("mySidenav").style.width = "30%";
document.getElementById("bigwrapper").style.width = "60%";
document.getElementById("bigwrapper").style.marginLeft = "35%";
}
/* Close/hide the sidenav */
function closeNav()
{
document.getElementById("mySidenav").style.width = "0";
document.getElementById("bigwrapper").style.width = "90%";
document.getElementById("bigwrapper").style.marginLeft = "5%";
}
</script>
here is the css for bigwrapper and sidenav at the end will be the media query
/* The side navigation menu */
.sidenav {
height: 800px; /* 100% Full-height */
width: 30%; /* 0 width - change this with JavaScript */
/*position: relative; /* Stay in place */
z-index: 1; /* Stay on top */
/* top: 0;
left: 0;*/
background-color: #292929; /* Black*/
overflow-x: hidden; /* Disable horizontal scroll */
padding-top: 60px; /* Place content 60px from the top */
transition: 0.4s; /* 0.5 second transition effect to slide in the sidenav */
float: left;
box-sizing: border-box;
}
/* The navigation menu links */
.sidenav a {
padding: 8px 8px 8px 32px;
text-decoration: none;
font-size: 25px;
color: #818181;
display: block;
margin-bottom: 25px;
transition: 0.3s;
font-size: 30px;
font-weight: bold;
}
/* When you mouse over the navigation links, change their color */
.sidenav a:hover, .offcanvas a:focus{
color: #f1f1f1;
}
/*big wrapper*/
#bigwrapper
{
border : 1px solid #000;
width : 60%;
margin : 0 5% 0 35%;
min-height: 800px;
background: #F5F5F5;
padding: 3%;
box-sizing: border-box;
transition: 0.4s;
}
here is the media query
#media screen and (max-width : 840px)
{
.sidenav,#menu_button
{
display: none;
}
#bigwrapper
{
width: 90%;
margin-left: 5%;
}
}
Sorry cause the question is too long.
The widths set by your openNav and closeNav functions seem to be in line with the css, so we can simply use those functions based on width, testing using resize event.
I think the following should work, and the nav should reappear when the browser goes back to > 840px. Add this at the bottom of your script:
window.onresize=function(){
if (window.outerWidth <= 840) {
closeNav();
} else {
openNav();
}
}
Also your button is not visible (outside media query), so add this to your CSS:
#menu_button {
display: block; /* or similar style */
}
BTW, this would be a lot simpler using a class based system. Instead of setting styles you could add classes. When clicking the button you could add .open class to .sidenav (and next button click remove .open). Then in your media queries you could style .sidenav.open { display:none} You could do similar things with the width of the other elements. You can also check if .sidenav has this .open class instead of using var hidden. Much simpler.

Categories