I'm new in javascript react. I'm trying to implement when user scrolled down, the navbar would drop a shadow. but unfortunately it doesn't work at all, what did I do wrong??
I think there's a problem with my logic, or perhaps I do the whole code wrongly, if there's an problem with the code, please do tell me where did I do it wrong, thank you
The Code :
class Navbar extends Component {
constructor() {
super(); // super allows you to access parent class's methods and allows us to use "this." in constructor().
this.state = {
clicked: false,
scrolled: false,
}
// Note here too these bindings are necessary to make `this` work in the callback
// In general, we use binding whenever we use "setState" when handling an event
this.handleScroll = this.handleScroll.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleScroll = () => {
const offset = window.scrollY;
if (offset > 200) {
this.setState({scrolled : !this.state.scrolled})
}
}
handleClick = () => {
this.setState({ clicked: !this.state.clicked})
}
render() {
return(
<div className='mycontainer' onScroll={this.handleScroll}>
{/* Note here this.scroll.scrolled changes to this.state.scrolled */}
<nav className={this.state.scrolled ? "NavbarItems Scroll" : "NavbarItems"}>
<h1 className="navbar-logo">React <i className="fab fa-react"></i></h1>
<div className="menu-icon" onClick={this.handleClick}>
<i className={this.state.clicked ? "fas fa-times" : 'fas fa-bars'}></i>
</div>
<ul className={this.state.clicked ? 'nav-menu active' : 'nav-menu'}>
{
MenuItems.map((items, index) => {
return (
<li key = {index}><a className={items.cName} href={items.url}>
{items.title}
</a></li>
);
})
}
</ul>
<p>{window.scrollY}</p>
</nav>
</div>
);
}
}
css :
.mycontainer {
top: 0;
left: 0;
width: 100%;
height: 200%;
}
.NavbarItems {
position: fixed;
width: 100vw;
height: 85px;
background-color: white;
transition: 0.2s;
display: flex;
align-items: center;
font-size: 1.2rem;
justify-content: center;
}
.Scroll {
box-shadow: 0px 1px 10px #999;
}
you need to subscribe to window.onScroll not for div className='mycontainer'
reed this topic
Update style of a component onScroll in React.js
and (it not related to your problem)
you shouldn't use
this.handleScroll = this.handleScroll.bind(this);
this.handleClick = this.handleClick.bind(this);
if you use class properties
handleScroll = () => {
...
}
handleClick = () => {
...
}
read this doc
https://reactjs.org/docs/faq-functions.html#how-do-i-bind-a-function-to-a-component-instance
Related
In my Class component Field.jsx render(), I'm expanding my <Position> component using <Flipper>, (an abstracted flip animation), like so:
import { Flipper, Flipped } from 'react-flip-toolkit'
import { Position } from "./Position";
import "./css/Position.css";
class Field extends Component {
constructor(props) {
super(props);
this.state = {
fullScreen: false,
};
}
toggleFullScreen() {
this.setState({ fullScreen: !this.state.fullScreen });
}
...
render() {
const { players } = this.props;
const { fullScreen } = this.state;
if(players){
return (
<div className="back">
<div className="field-wrapper" >
<Output output={this.props.strategy} />
<Flipper flipKey={fullScreen}>
<Flipped flipId="player">
<div className="field-row">
{this.getPlayersByPosition(players, 5).map((player,i) => (
<Position
key={i}
className={fullScreen ? "full-screen-player" : "player"}
getPositionData={this.getPositionData}
toggleFullScreen={this.toggleFullScreen.bind(this)}
>{player.name}</Position>
))}
</div>
</Flipped>
</Flipper>
</div>
</div>
);
}else{
return null}
}
When I render it, I get clickable items from the mapped function getPlayersByPosition(), like so:
And if I click on each item, it expands to a div with player name:
Which is passed as props.children at component <div>
Position.jsx
import React from "react";
import "./css/Position.css";
export const Position = props => (
<div
className={props.className}
onClick={() => {
props.getPositionData(props.children);
props.toggleFullScreen();
console.log(props.getPositionData(props.children))
}}
>
{props.children}
</div>
);
getPositionData(), however, returns an object with many items on its turn, as seen by console above:
{matches: 7, mean: 6.15, price: 9.46, value: 0.67, G: 3, …}
QUESTION:
How do I pass and print theses other props keys and values on the expanded purple div as text?, so as to end with:
Patrick de Paula
matches: 7
mean: 6.15
price:9.46
....
NOTE:
Position.css
.position-wrapper {
height: 4em;
display: flex;
justify-content: center;
align-items: center;
font-weight: lighter;
font-size: 1.4em;
color: #888888;
flex: 1;
/*outline: 1px solid #888888;*/
}
.player {
height: 4em;
width: 4em;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
font-weight: lighter;
font-size: 1.4em;
/*background-color: #66CD00;*/
color: #ffffff;
}
.full-screen-player {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
background-image: linear-gradient(
45deg,
rgb(121, 113, 234),
rgb(97, 71, 182)
);
}
Looks like the props are all set & ready to be print as seen on your console. You can access them via props.getPositionData(props.children).property_name_here or destructure them
export const Position = props => {
const { matches, mean, price } = props.getPositionData(props.children);
return (
<div
className={props.className}
onClick={() => {
props.getPositionData(props.children);
props.toggleFullScreen();
console.log(props.getPositionData(props.children))
}}
>
<p>Name: {props.children}</p>
<p>Matches: {matches}</p>
<p>Mean: {mean}</p>
<p>Price: {price}</p>
</div>
)
}
Regarding the issue on the fullScreen prop (see comments section):
Is there a way to print them ONLY after toggleFullScreen()
Since you already have a state on the Field component which holds your fullScreen value, on your Field component, you need to pass the fullScreen prop as well to the Position component. e.g., fullScreen={this.state.fullScreen}. Back on Position component, have some condition statements when you are rendering.
Example:
<>
{props.fullScreen &&
<p>Name: {props.children}</p>
}
</>
I have a class InventoryView which displays a list of stock items and is defined as follows :
class InventoryView extends Component {
...
render() {
...
{
consumableItemsArray.map((row, key) =>
<Item item={row} key={row.id} />
)}
...
}
}
The class Item is basically every stock in the list of stock items and is defined as follows :
class Item extends Component {
...
render() {
...
return (
<HorizontalRow>
...
<EditAStockItem></EditAStockItem>
</HorizontalRow>
)
}
The class EditAStockItem is basically an edit button which when clicked should display a Modal and is defined as follows :
class EditAStockItem extends Component {
constructor(props) {
super(props)
this.state = { isShowingInventoryUpdationModal: false }
}
editStockItem = event => {
event.preventDefault()
this.setState({ isShowingInventoryUpdationModal: true })
}
openInventoryUpdationHandler = () => {
console.log('Inside openInventoryUpdationHandler')
this.setState({
isShowingInventoryUpdationModal: true
});
}
closeInventoryUpdationHandler = () => {
this.setState({
isShowingInventoryUpdationModal: false
});
}
render() {
const { isShowingInventoryUpdationModal } = this.state
if(!isShowingInventoryUpdationModal)
return <EditStockItemButton onClick={this.editStockItem}><i class="fa fa-edit" aria-hidden="true"></i></EditStockItemButton>
else
{
return (
<div>
{ this.state.isShowingInventoryUpdationModal ? <div onClick=
{this.closeInventoryUpdationHandler}></div> : null }
<UpdateStockItemModal
className="modal"
show={this.state.isShowingInventoryUpdationModal}
close={this.closeInventoryUpdationHandler}>
Please insert a client name :
</UpdateStockItemModal>
</div>
)}
}
}
openInventoryUpdationHandler and closeInventoryUpdationHandler set the state of the variable isShowingInventoryUpdationModal which becomes true when the edit button is clicked. When the variable isShowingInventoryUpdationModal becomes true, a modal opens up and takes the place of the edit button thereby skewing the whole page up. I want the Modal to be on top of the entire page like a Modal does. Is there any way I can do this without changing the current structure of my code?
The Modal is defined as follows :
class UpdateStockItemModal extends Component {
constructor(props) {
super(props)
this.state = {
show : props.show,
close : props.close,
children : props.children,
}
}
prepareComponentState (props) {
var usedProps = props || this.props
this.state = {
show : usedProps.show,
close : usedProps.close,
children : usedProps.children,
}
}
componentWillReceiveProps = async (nextProps) => {
this.prepareComponentState(nextProps)
}
componentWillMount = async (props) => {
this.prepareComponentState()
}
render() {
var { stockName, totalQuantity, show, close, children } = this.state
return (
<div>
<div className="modal-wrapper"
style={{
transform: show ? 'translateY(0vh)' : 'translateY(-100vh)',
opacity: show ? '1' : '0'
}}>
<div className="modal-header">
<h3>Update Stock Item</h3>
<span className="close-modal-btn" onClick={close}>×</span>
</div>
<FormContainer>
<InputStockNameContainer>
<p>Enter Stock Name</p>
<InputText
type="text"
value={ stockName }
onChange={this.handleChangeInputStockName}
/>
</InputStockNameContainer>
<InputTotalQuantityContainer>
<p>Enter Total Quantity</p>
<InputText
type="text"
value={ totalQuantity }
onChange={this.handleChangeInputTotalQuantity}
/>
</InputTotalQuantityContainer>
</FormContainer>
<div className="modal-footer">
<button className="btn-cancel" onClick={close}>CLOSE</button>
<button className="btn-continue" onClick = {this.handleIncludeClient}>CONTINUE</button>
</div>
</div>
</div>
)
}
}
export default UpdateStockItemModal;
You can fix this whole thing with css, by having the modal with position fixed and to sit on top by using z-index.
Here you have my demo of a simple modal:
.modal {
position: fixed; /* Stay in place */
z-index: 1000; /* Sit on top */
padding-top: 100px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content */
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
/* The Close Button */
.close {
color: #aaaaaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
<div id="myModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<span class="close">×</span>
<p>Some text in the Modal..</p>
</div>
</div>
I created a Dropdown that when I click outside of it the dropdown disappears. I used a click event listener to determine if I clicked outside the dropdown.
After a few clicks, the page slows down and crashes. Perhaps the state is being rendered in a loop or too many events are being fired at once?
How do I fix this?
Also, is there a more React way to determine if I clicked outside an element? (Instead of using a document.body event listener)
Here is the codepen:
const items = [
{
value: 'User1'
},
{
value: 'User2'
},
{
value: 'User3'
},
{
value: 'User4'
},
{
value: 'User5'
}
];
class Dropdown extends React.Component {
state = {
isActive: false,
}
render() {
const { isActive } = this.state;
document.addEventListener('click', (evt) => {
if (evt.target.closest('#dropdownContent')) {
//console.warn('clicked inside target do nothing');
return;
}
if (evt.target.closest('#dropdownHeader')) {
//console.warn('clicked the header toggle');
this.setState({isActive: !isActive});
}
//console.warn('clicked outside target');
if (isActive) {
this.setState({isActive: false});
}
});
return (
<div id="container">
<div id="dropdownHeader">select option</div>
{isActive && (
<div id="dropdownContent">
{items.map((item) => (
<div id="item" key={item.value}>
{item.value}
</div>
))}
</div>
)}
</div>
);
};
}
ReactDOM.render(
<Dropdown items={items} />,
document.getElementById('root')
);
#container {
position: relative;
height: 250px;
border: 1px solid black;
}
#dropdownHeader {
width: 100%;
max-width: 12em;
padding: 0.2em 0 0.2em 0.2em;
margin: 1em;
cursor: pointer;
box-shadow: 0 1px 4px 3px rgba(34, 36, 38, 0.15);
}
#dropdownContent {
display: flex;
flex-direction: column;
position: absolute;
top: 3em;
width: 100%;
max-width: 12em;
margin-left: 1em;
box-shadow: 0 1px 4px 0 rgba(34, 36, 38, 0.15);
padding: 0.2em;
}
#item {
font-size: 12px;
font-weight: 500;
padding: 0.75em 1em 0.75em 2em;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root">
<!-- This element's contents will be replaced with your component. -->
</div>
There's a pretty simple explanation for what you're experiencing. :)
The way I was able to figure it out was the number of warnings that were showing up in the terminal every time I clicked somewhere was getting higher and higher, especially when the state changed.
The answer though is that since you were adding the event listener code in the render function, every time the code re-rendered it would add more and more event listeners slowing down your code.
Basically the solution is that you should move the adding of event listeners to componentDidMount so it's only run once.
Updated working javascript:
const items = [
{
value: 'User1'
},
{
value: 'User2'
},
{
value: 'User3'
},
{
value: 'User4'
},
{
value: 'User5'
}
];
class Dropdown extends React.Component {
state = {
isActive: false,
}
// added component did mount here
componentDidMount(){
const { isActive } = this.state;
document.addEventListener('click', (evt) => {
if (evt.target.closest('#dropdownContent')) {
console.warn('clicked inside target do nothing');
return;
}
if (evt.target.closest('#dropdownHeader')) {
console.warn('clicked the header toggle');
this.setState({isActive: !isActive});
}
console.warn('clicked outside target');
if (isActive) {
this.setState({isActive: false});
}
});
}
render() {
const { isActive } = this.state;
//removed event listener here
return (
<div id="container">
<div id="dropdownHeader">select option</div>
{isActive && (
<div id="dropdownContent">
{items.map((item) => (
<div id="item" key={item.value}>
{item.value}
</div>
))}
</div>
)}
</div>
);
};
}
ReactDOM.render(
<Dropdown items={items} />,
document.getElementById('root')
);
I am Using GatsbyJs and am trying to accomplish a active class toggle. I have a simple Component composed of a anchor tag with a span inside. I want to change some css by toggling a active class on the anchor when it is clicked. Here is my code so far, I am also using styled-components. I tried some vanilla js in the broken code section below, that obviously didn't work hence broken code.
Thanks in advance
styles
const Menu = styled.a`
position: absolute;
cursor: pointer; padding: 10px 35px 16px 0px;
span, span:before, span:after{
cursor: pointer;
border-radius: 1px;
height: 5px;
width: 35px;
background: #000000;
position: absolute;
display: block;
content: '';
transition: all 200ms ease-in-out;
}
span:before{
top: -10px;
}
span:after{
bottom: -10px;
}
.active span{
background-color: transparent;
}
.active span:before, .active span:after{
top:0;
}
.active span:before{
transform: rotate(45deg);
}
.active span:after{
transform: rotate(-45deg);
}
`
component
const TemplateWrapper = ({ children }) => (
<div>
<Wrapper>
<Menu id="nav-toggle" className="menu"><span></span></Menu>
{children()}
</Wrapper>
</div>
)
TemplateWrapper.propTypes = {
children: PropTypes.func,
}
export default TemplateWrapper
broken code
document.querySelector( "#nav-toggle" )
.addEventListener( "click", function() {
this.classList.toggle( "active" );
});
This is the html that is rendered
<a class="menu sc-bwzfXH SqHLW" id="nav-toggle"><span></span></a>
Okay, you can take a look here REACT - toggle class onclick
. There are several answers that show the way how you can achieve that, but things get messier when you want to use more than one class.
I like this package to handle the cases where you deal with styling with multiple classes based on the props or state: https://github.com/JedWatson/classnames.
I simplified the styles for the sake of clarity of this answer and assumed that the menu is a global class, you want to apply as well.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import styled from 'styled-components';
const Wrapper = styled.div``;
const Menu = styled.a`
span {
color: green;
}
&.active span {
color: blue;
}
`;
class TemplateWrapper extends Component {
state = {
isActive: false
};
handleClick = () => {
this.setState(state => ({ isActive: !state.isActive }));
};
render() {
const menuClass = classNames({
menu: true,
active: this.state.isActive
});
return (
<Wrapper>
<Menu id="nav-toggle" className={menuClass} onClick={this.handleClick}>
<span>Test</span>
</Menu>
{this.props.children}
</Wrapper>
);
}
}
TemplateWrapper.propTypes = {
children: PropTypes.node
};
TemplateWrapper.defaultProps = {
children: null
};
export default TemplateWrapper;
Notice the ampersand (&) before .active. As stated in the docs, styled components supports all of CSS plus nesting.
Ampersands (&) get replaced by our generated, unique classname for that styled component, making it easy to have complex logic.
However, you can achieve the same effect without classnames by fully utilizing styled components functionality. Take a look at this section of the documentation.
A few changes needed:
const Menu = styled.a`
span {
color: ${props => (props.active ? 'blue' : 'green')};
}
`;
The render method would look like that:
render() {
return (
<Wrapper>
<Menu id="nav-toggle" className="menu" active={this.state.isActive} onClick={this.handleClick}>
<span>Test</span>
</Menu>
{this.props.children}
</Wrapper>
);
}
You can use component state to mange this.
Code will look like:
Updated: Moved logic to Menu component as it makes more sense to place it there. Below working example
class Menu extends React.Component {
constructor(props) {
super(props);
this.menuClick = this.menuClick.bind(this);
this.state = {
menuClass: '',
}
}
menuClick(e) {
const menuClass = this.state.menuClass === '' ? 'active' : '';
this.setState({ menuClass });
}
render() {
const {children, id} = this.props;
const menuClassName = `menu sc-bwzfXH SqHLW nav-toggle ${this.state.menuClass}`;
return (<a className={menuClassName} id={id} onClick={this.menuClick}>{children}</a>);
}
}
ReactDOM.render(<Menu><span>Test link</span></Menu>, document.getElementById('menu'))
.menu {
font-weight: bolder;
color: blue;
}
.active{
color:red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="menu" />
I'm trying to appear a background color with transition when a click event happens and without further interaction fade out the background color after 1 or 2 seconds.
Basically i want to do what active property does in css but for a click event.
My current approach needs to fire the click event for the second time to fadeout the background color. How can i do this in one click
My approach
handleClick(id) {
this.setState({
active: !this.state.active
})
}
<div className={this.state.active ? "txt_vote_bar_div txt_vote_bar_div_active" : "txt_vote_bar_div txt_vote_bar_div_notactive"}
onClick={this.handleClick()}>
</div>
My CSS
.txt_vote_bar_div {
width: 100%;
height: 50px;
min-height: 50px;
position: relative;
border: 1px solid #C6C6C6;
border-radius: 5px;
margin-bottom: 10px;
cursor: pointer;
}
.txt_vote_bar_div_active {
background-color: #001f3f;
transition: 1s ease-in-out;
}
.txt_vote_bar_div_notactive {
background-color: #FFFFFF;
transition: 1s ease-in-out;
}
Please find running example
In your code one mistake, you did i.e onClick={this.handleClick()}
so due to this after every renders your click event is triggered.
This is What I did.
class Hello extends React.Component {
state = {
active: false
}
handleClick(e) {
this.setState({
active: !this.state.active
});
setTimeout(() => {
this.setState({
active: !this.state.active
});
}, 1000);
}
render() {
return (
<div className = { this.state.active ? "txt_vote_bar_div txt_vote_bar_div_active" : "txt_vote_bar_div txt_vote_bar_div_notactive" } onClick = { this.handleClick.bind(this) }></div>
);
}
}
ReactDOM.render( <
Hello initialName = "World" / > ,
document.getElementById('container')
);
You could do this by setting a timeout to change it back.
handleClick(id) {
this.setState({
active: !this.state.active
});
// The timeout will trigger after 1000ms. Use a fat arrow function
// to keep the same reference to this.
setTimeout(() => {
this.setState({
active: false
});
}, 1000);
}
If you can't use fat arrow function you could assign this to a variable like self and call self.setState from within the timeout handler.