Active classnames in menu on a one-page website using GatsbyJS - javascript

I'm using a single page template with GatsbyJS on which the menu scrolls to the different sections of the same page (#home, #about, #portfolio, etc). Is there a way to set an active classname on the links, highlighting the link the user is on?

Link provides two options for adding styles to the active link:
activeStyle — a style object that will only be applied when the current item is active
activeClassName — a class name that will only be added to the Link when the current item is active
Follow official docs: https://www.gatsbyjs.org/docs/gatsby-link/#add-custom-styles-for-the-currently-active-link
import React from "react"
import { Link } from "gatsby"
const SiteNavigation = () => (
<nav>
<Link
to="/"
{/* This assumes the `active` class is defined in your CSS */}
activeClassName="active"
>
Home
</Link>
<Link
to="/about/"
activeStyle={{ color: "red" }}
>
About
</Link>
</nav>
)

You can set the activeStyle or activeClassName prop to add styling attributes to the rendered element when it matches the current URL, and Gatsby also supports React Router's props exact, strict, isActive, and location. If any of these props are set, then React Router's NavLink component will be used instead of the default Link.
Example:
import Link from "gatsby-link"
render () {
<div>
<Link
to="/another-page/"
activeStyle={{
color: 'red'
}}
innerRef={(el) => { this.myLink = el }}
>
Another page
</Link>
</div>
}
Visit https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-link

I did it the hard way as I couldn't find another solution:
import React, { Component } from 'react';
import './Menu.css';
class Menu extends Component {
constructor(props) {
super(props)
this.state = {
home: true,
about: false,
portfolio: false
}
this.handleActive = this.handleActive.bind(this)
}
handleActive(button) {
switch (button) {
case 'home':
this.setState({
home: true,
about: false,
portfolio: false
});
break;
case 'about':
this.setState({
home: false,
about: true,
portfolio: false
});
break;
case 'portfolio':
this.setState({
home: false,
about: false,
portfolio: true
});
break;
default: break;
}
}
render() {
return (
<div id="nav-wrap">
<nav>
<input type="checkbox" id="checkbox1" />
<label htmlFor="checkbox1">
<ul className="menu first">
<li><a
className={this.state.home ? 'active' : null}
onClick={() => this.handleActive('home')}
href="#home">HOME</a></li>
<li><a
className={this.state.about ? 'active' : null}
onClick={() => this.handleActive('about')}
href="#about">ABOUT МЕ
</a></li>
<li><a
className={this.state.portfolio ? 'active' : null}
onClick={() => this.handleActive('portfolio')}
href="#portfolio">PORTFOLIO</a></li>
</ul>
<span className="toggle">☰</span>
</label>
</nav>
</div>
)
}
}
export default Menu;

As brooksrelyt noted in the comment above, you can easily use react-scrollspry to add a unique class name to the hash link that has been clicked. Here is how I use it:
import { Text } from 'rebass/styled-components'
import Scrollspy from 'react-scrollspy'
<Scrollspy
items={['home', 'features', 'faq']}
currentClassName="isCurrent"
>
<Link to="#home">Home</Link>
<Link to="#features">Features</Link>
<Link to="#faq">FAQ</Link>
</Scrollspy>
<Text id="home">Home</Text>
<Text id="features" mt={'400vh'}>Features</Text>
<Text id="faq" mt={'150vh'}>FAQ</Text>
In short, you wrap your links with a Scrollspy component and include (at the very least) two mandatory props: items and currentClassName.
items is an array of the hash names (without the hash character)
currentClassName is the name of the class you want to add to the selected link.
NOTE: I included the rebass Text component because it did not work properly for me when I used a simple div. You should read the documentation for how to use it in your particular case - as there are other props that may be needed in different situations.

I did a website some weeks ago with this feature. I created a function to know what section are the active and put it on the state in react. This function is called everytime the user move the scrollbar.
In render, I change de className of the elements depending the element of the state. In every section/element that i want to track I put an ID.
Util function:
/**
* Helper function to get an element's exact position
* #param {element} element
* #return {x,y}
*/
export function getPosition(el) {
var xPos = 0;
var yPos = 0;
while (el) {
if (el.tagName == "BODY") {
// deal with browser quirks with body/window/document and page scroll
var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
var yScroll = el.scrollTop || document.documentElement.scrollTop;
xPos += (el.offsetLeft - xScroll + el.clientLeft);
yPos += (el.offsetTop - yScroll + el.clientTop);
} else {
// for all other non-BODY elements
xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);
yPos += (el.offsetTop - el.scrollTop + el.clientTop);
}
el = el.offsetParent;
}
return {
x: xPos,
y: yPos
};
}
On React component:
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
this.handleScroll();
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll() {
const {inAnimation} = this.state;
let activeElement = false;
this.props.data.items.forEach((value,i) => {
let element = document.getElementById(value.url.substring(1));
if(getPosition(element).y <= 0){
activeElement = value.url;
}
});
this.setState({
activeElement
});
}
}
render(){
...
items.map((item, i) => {
return <li key={i}>
<a className={'menu-item' + (activeElement == item.url ? ' active': '')}>{item.title}</a>
</li>;
});
...
}

Related

Remove border/style from previously clicked link - React/Nextjs

I have a function that triggers onClick which adds borders to the clicked element.
Here is the code in the component:
import { useEffect, useState } from 'react';
import Link from 'next/link';
import Logo from '../../components/logo.svg';
import React from 'react';
import getConfig from 'next/config';
import { useRouter } from 'next/router';
const {
publicRuntimeConfig: { domain },
} = getConfig();
export default function MediaNav() {
// If router equals link in list, show blue border
const router = useRouter();
const menu = ['Home', 'FAQs', 'Contact Us', 'Social Media'];
const [selectedPage, setPage] = useState({
borderColor: '',
});
const handleLinkClick = e => {
setPage({
borderStyle: '3px solid #005ba9',
});
e.target.style.borderLeft = selectedPage.borderStyle;
e.target.style.borderRight = selectedPage.borderStyle;
};
return (
<nav>
<div>
<Link href={`${domain}`}>
<a>
<Logo />
</a>
</Link>
</div>
<ul
>
<div>
<ul>
<li
>
All Sites
</li>
</ul>
<ul
>
{menu.map((item, i) => {
return (
<li key={i}>
<Link href={item === 'Home' ? '/subdomain/link' : `/subdomain/${item}`.replace(/\s+/g, '').toLowerCase()}>
<a onClick={handleLinkClick}>
{item}
</a>
</Link>
</li>
);
})}
</ul>
) : null}
</div>
</ul>
</nav>
);
}
At the moment, when the element in the link list is clicked, the blue border is being applied, however, if I click another link, I wanted the previously clicked element link to be removed its borders.
As this is NextJs and I have a Link tag wrapping up the link element, loading is not occurring, therefore I don't know how to make a difference between previously clicked element and currently clicked element.
Any idea how to remove the borders to already clicked link when next link is clicked?
I think a better approach to this problem would be having a state to save currentPage user visiting and depending on currentPage state giving a style to a element.
import { useEffect, useState } from 'react';
import Link from 'next/link';
import Logo from '../../components/logo.svg';
import React from 'react';
import getConfig from 'next/config';
import { useRouter } from 'next/router';
const {
publicRuntimeConfig: { domain },
} = getConfig();
export default function MediaNav() {
// If router equals link in list, show blue border
const router = useRouter();
const menu = ['Home', 'FAQs', 'Contact Us', 'Social Media'];
const [currentPage, setCurrentPage] = useState('')
const handleLinkClick = pageName => {
setCurrentPage(pageName);
};
return (
<nav>
<div>
<Link href={`${domain}`}>
<a>
<Logo />
</a>
</Link>
</div>
<ul
>
<div>
<ul>
<li
>
All Sites
</li>
</ul>
<ul>
{menu.map((item, i) => {
return (
<li key={i}>
<Link href={item === 'Home' ? '/subdomain/link' : `/subdomain/${item}`.replace(/\s+/g, '').toLowerCase()}>
<a onClick={() => handleLinkClick(item)} style={{ border: currentPage === item ? '3px solid #005ba9': 'initial' }}>
{item}
</a>
</Link>
</li>
);
})}
</ul>
</div>
</ul>
</nav>
);
}
I think instead of adding a style on a target element, you can try adding a class, let's say highlight on the element.
highlight class would look like -
.highlight {
border-left: '3px solid #005ba9',
border-right: '3px solid #005ba9'
}
So only the elements which have this class would show the border styles applied. Since the components would re-render on state changes, make sure you are applying the highlight class on the link in map function logic. This would ensure other elements won't have this class and this would solve the problem.
PS: Removing of the previously applied styles would be tedious. I think the above solution would work without much hassle and it would work great.

ReactJS — Creating Refs and accessing DOM elements to use fullpage.js

I am trying to migrate a code I created in JS/JQuery to ReactJS. The code uses the library fullpage.js. The aim is to change the title according to the section on focus. I am struggling to correctly capture the properties of elements in the DOM using Refs. I tried to follow the ReactJS documentation but the example in the docs made everything more confusing.
My question is, how can I store these properties and then apply it using CSS on the required DOM element? Thank you in advance.
Pseudocode
Store title width
Store title height
Apply these properties to the
parents' frame and mask
Listen to the scroll events of fullpage.js
and translate the title position accordingly
Codepen (JS/JQuery)
$(document).ready(function() {
var titleWidth = $(".title").outerWidth();
var titleHeight = $(".title").outerHeight();
$("#mask").css({ height: titleHeight + "px", width: titleWidth + "px" });
$("#frame").css("top", titleHeight);
new fullpage("#fullpage", {
sectionsColor: ["yellow", "orange", "#C0C0C0", "#ADD8E6"],
afterRender: function() {
$("#frame").transition({ top: "-=" + titleHeight, delay: 1000 });
},
onLeave: function(origin, destination, direction) {
var leavingSection = this;
//after leaving section 2
if (direction == "down") {
$("#frame").transition({ top: "-=" + titleHeight });
} else if (direction == "up") {
$("#frame").transition({ top: "+=" + titleHeight });
}
}
});
});
Codesandbox (ReactJS)
import React from "react";
import ReactDOM from "react-dom";
import "fullpage.js/vendors/scrolloverflow"; // Optional. When using scrollOverflow:true
import ReactFullpage from "#fullpage/react-fullpage";
import "./styles.css";
class MySection extends React.Component {
render() {
return (
<div className="section">
<h3>{this.props.content}</h3>
</div>
);
}
}
const anchors = ["firstPage", "secondPage", "thirdPage"];
const FullpageWrapper = () => (
<ReactFullpage
anchors={anchors}
navigation
navigationTooltips={anchors}
sectionsColor={["#282c34", "#ff5f45", "#0798ec"]}
onLeave={(origin, destination, direction) => {
console.log("onLeave event", { origin, destination, direction });
//after leaving section 2
if (origin.index === 1 && direction === "down") {
alert("Going to section 3!");
} else if (origin.index === 1 && direction === "up") {
alert("Going to section 1!");
}
}}
render={({ state, fullpageApi }) => {
console.log("render prop change", state, fullpageApi); // eslint-disable-line no-console
return (
<div>
<MySection content={"Slide down!"} />
<MySection content={"Keep going!"} />
<MySection content={"Slide up!"} />
</div>
);
}}
/>
);
class Index extends React.Component {
constructor(props) {
super(props);
this.title = React.createRef();
// this.title = null;
// this.titleRef = element => {
// this.title = element;
// };
// this.setTitle = () => {
// Focus the text input using the raw DOM API
// if (this.title) this.title.current.offsetWidth;
// };
}
componentDidMount() {
// console.log(this.setTitle())
const titleWidth = this.title.current.offsetWidth;
const titleHeight = this.title.current.offsetHeight;
console.log(titleWidth, titleHeight);
}
render() {
return (
<div class="wrapper">
<div id="mask">
<div id="frame">
<h1 ref={this.title} class="title">
One
</h1>
<h1 ref={this.title} class="title">
Two
</h1>
<h1 ref={this.title} class="title">
Three
</h1>
<h1 ref={this.title} class="title">
Four
</h1>
</div>
</div>
<div class="content">
<FullpageWrapper />
</div>
</div>
);
}
}
ReactDOM.render(<Index />, document.getElementById("react-root"));

Change the style of the react component based on its props

I have a Header.js:
const Header = () => {
return (
<header className="header">
<Logo/>
<HeaderMenu target="home-link"/>
</header>);
}
and a HeaderMenu.js:
class HeaderMenu extends React.Component {
render() {
return(
<nav className="header-menu">
<a id="home-link">Home</a>
<a id="project-link">Projects</a>
<a id="blog-link">Blog</a>
<a id="about-me-link">About Me</a>
</nav>
);
}
}
How can I change an a elements style based on target props?
Example: if target="home-link", then an a element with id="home-link" has its text underlined and another a element doesn't.
You can do this by applying a class to any element that you want to have styled differently. In your example of giving a different style to the link for the current page, you can iterate over your links and give an "active" class to the link whose id matches the target.
For Example:
class HeaderMenu extends React.Component {
render() {
const { target } = this.props;
const linkData = [
{id: "home-link", title: "Home"},
{id: "project-link", title: "Projects"},
{id: "blog-link", title: "Blog"},
{id: "about-me-link", title: "About Me"},
];
const linkElements = linkNames.map(e => (
<a id={ e.id } className={ e.id === target ? "active" : "" }>{ e.title }</a>
));
return(
<nav className="header-menu">
{ linkElements }
</nav>
);
}
}
However if you use React Router (which you may want to do so the page doesn't refresh when a link is clicked) the <NavLink> component takes an activeClassName prop, which applies the given className any time the current location (url) matches the NavLink's to prop (which is analogous to the a tag's href).
Save menu data into an variable.
Use map to add a tag
Set an className = 'active' on the a where target is matched.
then write css to add underline.
HeaderMenu.js
class HeaderMenu extends React.Component {
constructor(props) {
super(props);
this.menus = [
{ id: 'home-link', name: 'Home' },
{ id: 'project-link', name: 'Projests' },
// ...
];
}
render() {
return(
<nav className="header-menu">
{
this.menus.map(menu => (<a id={menu.id} className={this.props.target === menu.id ? 'active' : ''}>{menu.name}</a>))
}
</nav>
);
}
}
css:
.active {
// text style when active
}

React hidden navigation menu

I am working with ReactJS at the moment (I am very much a beginner). What I wanting to do is show/hide a menu component when a link is clicked in the site navigation. The menu is being built as a component that sits within a component (header).
The user clicks the menu button and that then toggles the menu to be shown or hidden, I am however having problems working out where the logic to show and hide the menu component should live the showing an hiding is relatively simple I basically want to add and remove a class to the menu component to show or hide it.
I have a similar show/hide working for my login and register forms, but the show and hide classes for these are added in the header component not the child component, here is my header component JS so far.
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Menu from './menu';
import LoginForm from '../Login';
import RegisterForm from '../Register';
export default class Explore extends Component {
constructor(props) {
super(props);
this.state = {
loginMenuVisible : false,
registerMenuVisible : false,
mainMenuVisible : false
};
console.log(this.state);
//this.triggerMenu = this.triggerMenu.bind(this);
}
render() {
return (
<div className="site__navigation">
<Menu />
<header className="site__header">
<img src="img/logo-full-color.png" alt="meatFree" />
<ul className="header__navigation">
<li className="header__navigation__item">
<a href="/register" onClick={this.toggleMenu.bind(this, 'register')}>Register</a>
<ul className={this.state.registerMenuVisible ? "dropdown visible" : "dropdown"}>
<li>
<LoginForm />
</li>
</ul>
</li>
<li className="header__navigation__item">
<a href="#" onClick={this.toggleMenu.bind(this, 'login')}>Login</a>
<ul className={this.state.loginMenuVisible ? "dropdown visible" : "dropdown"}>
<li>
<LoginForm />
</li>
</ul>
</li>
<li className="header__navigation__item">
<a href="" className="nav__toggle">
Menu
</a>
</li>
</ul>
</header>
</div>
);
}
toggleMenu(type, e) {
e.preventDefault();
console.log(type);
switch(type) {
case 'login':
if(this.state.loginMenuVisible) {
this.setState({loginMenuVisible : false});
} else {
this.setState({
registerMenuVisible : false,
loginMenuVisible : true
});
}
break;
case 'register':
if(this.state.registerMenuVisible) {
this.setState({registerMenu : false});
} else {
this.setState({
registerMenuVisible : true
});
}
break;
case 'menu':
this.setState({mainMenuVisible : true });
}
}
}
AS you see I have a element in the state called mainMenuVisible, I want to be able to maniuplate this within the Menu component.
I like to keep things simple:
export default class Example extends Component {
constructor(){
super(props)
this.state = {
showMenu: false
}
this.menuShowToggle = this.menuShowToggle.bind(this);
}
menuShowToggle = () => {
this.setState({showMenu: !this.state.showMenu})
}
render = () => {
return (
<div>
<div className={this.state.showMenu ? 'showMenu': 'hideMenu'}>Hidden Menu</div>
<button type="button" onClick={this.menuShowToggle} value="toggle" />
</div>
)
}
}
To achieve this using your current setup, you could pass the toggleMenu() function as a prop to your rendered Menu component
<Menu toggleMenu={this.toggleMenu} />
Then within the Menu component, you could call toggleMenu() in a click handler and pass in 'menu' as the type.
If you are using ReactJs you should consider the following pattern:
1. Set a state specifically to show/hide the the menu
2. Create a function which will toggle the state
3. Add a conditional rendering for the component menu
Here is how the the code can be done:
Step 1.
constructor(props) {
super(props);
this.state = {
isComponenetMenuVisisble : false, // by default to disable it
};
}
Step 2.
toggleComponentMenu = () => {
const isComponenetMenuVisisble = !this.state.isComponenetMenuVisisble;
this.setState({ isComponenetMenuVisisble });
}
Step 3.
{this.state.isComponenetMenuVisisble && <Menu />}
// if isComponenetMenuVisisble is true render <Menu />
And then you can add the event handler like the following
<div className="your-element" onClick={this.toggleComponentMenu}>
... your stuff
</div>
A good point to highlight in the code above is that I am using a method rather than a function in step 2. When using a method instead of a function there will be no need to bind the function on event handlers because this context will be the class itself. Much more details can be found about this in here http://blog.andrewray.me/react-es6-autobinding-and-createclass/

React JS onClick event handler

I have
var TestApp = React.createClass({
getComponent: function(){
console.log(this.props);
},
render: function(){
return(
<div>
<ul>
<li onClick={this.getComponent}>Component 1</li>
</ul>
</div>
);
}
});
React.renderComponent(<TestApp />, document.body);
I want to color the background of the clicked list element. How can I do this in React ?
Something like
$('li').on('click', function(){
$(this).css({'background-color': '#ccc'});
});
Why not:
onItemClick: function (event) {
event.currentTarget.style.backgroundColor = '#ccc';
},
render: function() {
return (
<div>
<ul>
<li onClick={this.onItemClick}>Component 1</li>
</ul>
</div>
);
}
And if you want to be more React-ive about it, you might want to set the selected item as state of its containing React component, then reference that state to determine the item's color within render:
onItemClick: function (event) {
this.setState({ selectedItem: event.currentTarget.dataset.id });
//where 'id' = whatever suffix you give the data-* li attribute
},
render: function() {
return (
<div>
<ul>
<li onClick={this.onItemClick} data-id="1" className={this.state.selectedItem == 1 ? "on" : "off"}>Component 1</li>
<li onClick={this.onItemClick} data-id="2" className={this.state.selectedItem == 2 ? "on" : "off"}>Component 2</li>
<li onClick={this.onItemClick} data-id="3" className={this.state.selectedItem == 3 ? "on" : "off"}>Component 3</li>
</ul>
</div>
);
},
You'd want to put those <li>s into a loop, and you need to make the li.on and li.off styles set your background-color.
Two ways I can think of are
var TestApp = React.createClass({
getComponent: function(index) {
$(this.getDOMNode()).find('li:nth-child(' + index + ')').css({
'background-color': '#ccc'
});
},
render: function() {
return (
<div>
<ul>
<li onClick={this.getComponent.bind(this, 1)}>Component 1</li>
<li onClick={this.getComponent.bind(this, 2)}>Component 2</li>
<li onClick={this.getComponent.bind(this, 3)}>Component 3</li>
</ul>
</div>
);
}
});
React.renderComponent(<TestApp /> , document.getElementById('soln1'));
This is my personal favorite.
var ListItem = React.createClass({
getInitialState: function() {
return {
isSelected: false
};
},
handleClick: function() {
this.setState({
isSelected: true
})
},
render: function() {
var isSelected = this.state.isSelected;
var style = {
'background-color': ''
};
if (isSelected) {
style = {
'background-color': '#ccc'
};
}
return (
<li onClick={this.handleClick} style={style}>{this.props.content}</li>
);
}
});
var TestApp2 = React.createClass({
getComponent: function(index) {
$(this.getDOMNode()).find('li:nth-child(' + index + ')').css({
'background-color': '#ccc'
});
},
render: function() {
return (
<div>
<ul>
<ListItem content="Component 1" />
<ListItem content="Component 2" />
<ListItem content="Component 3" />
</ul>
</div>
);
}
});
React.renderComponent(<TestApp2 /> , document.getElementById('soln2'));
Here is a DEMO
I hope this helps.
Here is how you define a react onClick event handler, which was answering the question title... using es6 syntax
import React, { Component } from 'react';
export default class Test extends Component {
handleClick(e) {
e.preventDefault()
console.log(e.target)
}
render() {
return (
<a href='#' onClick={e => this.handleClick(e)}>click me</a>
)
}
}
Use ECMA2015. Arrow functions make "this" a lot more intuitive.
import React from 'react';
class TestApp extends React.Component {
getComponent(e, index) {
$(e.target).css({
'background-color': '#ccc'
});
}
render() {
return (
<div>
<ul>
<li onClick={(e) => this.getComponent(e, 1)}>Component 1</li>
<li onClick={(e) => this.getComponent(e, 2)}>Component 2</li>
<li onClick={(e) => this.getComponent(e, 3)}>Component 3</li>
</ul>
</div>
);
}
});
React.renderComponent(<TestApp /> , document.getElementById('soln1'));`
If you're using ES6, here's some simple example code:
import React from 'wherever_react_is';
class TestApp extends React.Component {
getComponent(event) {
console.log('li item clicked!');
event.currentTarget.style.backgroundColor = '#ccc';
}
render() {
return(
<div>
<ul>
<li onClick={this.getComponent.bind(this)}>Component 1</li>
</ul>
</div>
);
}
}
export default TestApp;
In ES6 class bodies, functions no longer require the 'function' keyword and they don't need to be separated by commas. You can also use the => syntax as well if you wish.
Here's an example with dynamically created elements:
import React from 'wherever_react_is';
class TestApp extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{name: 'Name 1', id: 123},
{name: 'Name 2', id: 456}
]
}
}
getComponent(event) {
console.log('li item clicked!');
event.currentTarget.style.backgroundColor = '#ccc';
}
render() {
<div>
<ul>
{this.state.data.map(d => {
return(
<li key={d.id} onClick={this.getComponent.bind(this)}>{d.name}</li>
)}
)}
</ul>
</div>
);
}
}
export default TestApp;
Note that each dynamically created element should have a unique reference 'key'.
Furthermore, if you would like to pass the actual data object (rather than the event) into your onClick function, you will need to pass that into your bind. For example:
New onClick function:
getComponent(object) {
console.log(object.name);
}
Passing in the data object:
{this.state.data.map(d => {
return(
<li key={d.id} onClick={this.getComponent.bind(this, d)}>{d.name}</li>
)}
)}
Handling events with React elements is very similar to handling events
on DOM elements. There are some syntactic differences:
React events are named using camelCase, rather than lowercase.
With JSX you pass a function as the event handler, rather than a string.
So as mentioned in React documentation, they quite similar to normal HTML when it comes to Event Handling, but event names in React using camelcase, because they are not really HTML, they are JavaScript, also, you pass the function while we passing function call in a string format for HTML, they are different, but the concepts are pretty similar...
Look at the example below, pay attention to the way event get passed to the function:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
import React from 'react';
class MyComponent extends React.Component {
getComponent(event) {
event.target.style.backgroundColor = '#ccc';
// or you can write
//arguments[0].target.style.backgroundColor = '#ccc';
}
render() {
return(
<div>
<ul>
<li onClick={this.getComponent.bind(this)}>Component 1</li>
</ul>
</div>
);
}
}
export { MyComponent }; // use this to be possible in future imports with {} like: import {MyComponent} from './MyComponent'
export default MyComponent;
class FrontendSkillList extends React.Component {
constructor() {
super();
this.state = { selectedSkill: {} };
}
render() {
return (
<ul>
{this.props.skills.map((skill, i) => (
<li
className={
this.state.selectedSkill.id === skill.id ? "selected" : ""
}
onClick={this.selectSkill.bind(this, skill)}
style={{ cursor: "pointer" }}
key={skill.id}
>
{skill.name}
</li>
))}
</ul>
);
}
selectSkill(selected) {
if (selected.id !== this.state.selectedSkill.id) {
this.setState({ selectedSkill: selected });
} else {
this.setState({ selectedSkill: {} });
}
}
}
const data = [
{ id: "1", name: "HTML5" },
{ id: "2", name: "CSS3" },
{ id: "3", name: "ES6 & ES7" }
];
const element = (
<div>
<h1>Frontend Skill List</h1>
<FrontendSkillList skills={data} />
</div>
);
ReactDOM.render(element, document.getElementById("root"));
.selected {
background-color: rgba(217, 83, 79, 0.8);
}
<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"></div>
#user544079 Hope this demo can help :) I recommend changing background color by toggling classname.
import React from 'react';
class MyComponent extends React.Component {
getComponent(event) {
event.target.style.backgroundColor = '#ccc';
// or you can write
//arguments[0].target.style.backgroundColor = '#ccc';
}
render() {
return(
<div>
<ul>
<li onClick={this.getComponent.bind(this)}>Component 1</li>
</ul>
</div>
);
}
}
export { MyComponent }; // use this to be possible in future imports with {} like: import {MyComponent} from './MyComponent'
export default MyComponent;
You can make use of the React.createClone method. Create your element, than create a clone of it. During the clone's creation, you can inject props. Inject an onClick : method prop like this
{ onClick : () => this.changeColor(originalElement, index) }
the changeColor method will set the state with the duplicate, allowing you sto set the color in the process.
render()
{
return(
<ul>
{this.state.items.map((val, ind) => {
let item = <li key={ind}>{val}</li>;
let props = {
onClick: () => this.Click(item, ind),
key : ind,
ind
}
let clone = React.cloneElement(item, props, [val]);
return clone;
})}
</ul>
)
}
This is a non-standard (but not so uncommon) React pattern that doesn't use JSX, instead putting everything inline. Also, it's Coffeescript.
The 'React-way' to do this would be with the component's own state:
(c = console.log.bind console)
mock_items: [
{
name: 'item_a'
uid: shortid()
}
{
name: 'item_b'
uid: shortid()
}
{
name: 'item_c'
uid: shortid()
}
]
getInitialState: ->
lighted_item: null
render: ->
div null,
ul null,
for item, idx in #mock_items
uid = item.uid
li
key: uid
onClick: do (idx, uid) =>
(e) =>
# justf to illustrate these are bound in closure by the do lambda,
c idx
c uid
#setState
lighted_item: uid
style:
cursor: 'pointer'
background: do (uid) =>
c #state.lighted_item
c 'and uid', uid
if #state.lighted_item is uid then 'magenta' else 'chartreuse'
# background: 'chartreuse'
item.name
This example works -- I tested it locally.
You can check out this example code exactly at my github.
Originally the env was only local for my own whiteboard r&d purposes but I posted it to Github for this. It may get written over at some point but you can check out the commit from Sept 8, 2016 to see this.
More generally, if you want to see how this CS/no-JSX pattern for React works, check out some recent work here. It's possible I will have time to fully implement a POC for this app idea, the stack for which includes NodeJS, Primus, Redis, & React.

Categories

Resources