How to change css property in react - javascript

I want to change css width property of my element on some condition
<div className="consoleLayoutRoot-sideMenu">
<ConsoleSideMenu />
</div>
css
.consoleLayoutRoot-sideMenu .ant-menu {
padding-top: 30px;
/* background-color: #191146 !important; */
}
I am doing this way..but nothing is happening
document.getElementsByClassName("conjnjnot-sideMenjnjbhbhu.annjn ").style.width = "77px";

That's not working because you're treating a list as though it were an element. But it's also fundamentally not how you would do this in a React project.
Instead, you'd have the component re-render when the condition becomes true (perhaps by setting a state member). When rendering the div, you optionally include a style or a class name depending on whether you want the width applied:
<div className={`consoleLayoutRoot-sideMenu ${shouldHaveWidthClass ? "width-class" : ""}`}>
<ConsoleSideMenu />
</div>
...where .width-class { width: 50px; } is in your stylesheet.
Or with inline style, but inline styles are best avoided:
<div className="consoleLayoutRoot-sideMenu" style={shouldHaveWidthSetting ? { width: "50px" } : undefined}>
<ConsoleSideMenu />
</div>
Here's an example (using a class);
const {useState} = React;
const ConsoleSideMenu = () => <span>x</span>;
const Example = () => {
const [includeWidth, setIncludeWidth] = useState(false);
const toggle = ({currentTarget: { checked }}) => {
setIncludeWidth(checked);
};
return <React.Fragment>
<div className={`consoleLayoutRoot-sideMenu ${includeWidth ? "width-class" : ""}`}>
<ConsoleSideMenu />
</div>
<label>
<input type="checkbox" onChange={toggle} checked={includeWidth} />
Include width class
</label>
</React.Fragment>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
.width-class {
width: 50px;
}
.consoleLayoutRoot-sideMenu {
border: 1px solid blue;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

Related

Add className of div clicked on to other specific div

I have the following function component. Within it, when a user clicks on any of the 4 divs, note_b, note_g, note_p, note_y, I want that class name to then be appended to the div with className note
This is my (incomplete) code
import React from 'react-dom';
import DraggableCore from 'react-draggable';
function Note(props) {
return (
<DraggableCore defaultPosition={{x: 1000, y: 200}}>
<div className={"note " + }>
<div id="note_head">
<div id="note_bin"></div>
<div className="note_b" onClick={}></div>
<div className="note_p" onClick={}></div>
<div className="note_g" onClick={}></div>
<div className="note_y" onClick={}></div>
<div id="note_exit"></div>
</div>
<p>
{props.message}
</p>
</div>
</DraggableCore>
)
}
export default Note;
Thank you #Andy, I took a second look at state hooks and came up with this:
import React, { useState } from 'react';
import DraggableCore from 'react-draggable';
function Note(props) {
const [bg, setBG] = useState('note_bg_b');
return (
<DraggableCore defaultPosition={{x: 1000, y: 200}}>
<div className={"note " + bg}>
<div id="note_head">
<div id="note_bin"></div>
<div className="note_b" onClick={() => setBG('note_b')}></div>
<div className="note_p" onClick={() => setBG('note_b')}></div>
<div className="note_g" onClick={() => setBG('note_b')}></div>
<div className="note_y" onClick={() => setBG('note_b')}></div>
<div id="note_exit"></div>
</div>
<p>
{props.message}
</p>
</div>
</DraggableCore>
)
}
export default Note;
This can probably be done in a cleaner, more efficient fashion. It is however functional.
You can use the onClick event handler for each of the four divs and add the className that was clicked on to the div with the className note.
EDIT for additional question: To prevent the added className from unloading when clicking within the note div, we can use an event listener to check where the click originated from and if it did not come from an element with the note_b, note_p, note_g, or note_y className, then the className should not be removed.
import React, { useState, useRef } from 'react';
import DraggableCore from 'react-draggable';
function Note(props) {
const [className, setClassName] = useState('');
const noteDiv = useRef(null);
const handleClick = e => {
setClassName(e.target.className);
}
//This function prevents the default event action from occurring when the page is unloaded.
//If the target element of the event does not have one of the specified class names, it removes the class from the element with the class "note".
const handleUnload = e => {
e.preventDefault();
const noteDiv = document.querySelector('.note');
if (!['.note', 'note_b', 'note_p', 'note_g', 'note_y'].includes(e.target.className)) {
noteDiv.classList.remove(e.target.className);
}
}
return (
<DraggableCore defaultPosition={{ x: 1000, y: 200 }}>
<div ref={noteDiv} className={`note ${className}`} onClick={handleUnload}>
<div id="note_head">
<div id="note_bin"></div>
<div className="note_b" onClick={handleClick}></div>
<div className="note_p" onClick={handleClick}></div>
<div className="note_g" onClick={handleClick}></div>
<div className="note_y" onClick={handleClick}></div>
<div id="note_exit"></div>
</div>
<p>
{props.message}
</p>
</div>
</DraggableCore>
)
}
export default Note;
If you separate out your classes a little, and add a data attribute for each note, you might get closer to what you need.
Instead of a className that looks like node_b use two classes note b - note can be the general class for all notes, and b can be the one that specifies one particular note. I've used colours here for clarity.
Adding the data attribute makes it more easy to identify each note in the code. In the click handler you can destructure that note id from the dataset of the clicked element, and then use it to set state, and you can use that state in the containing element.
Note: I've only used one click handler on the notes' containing element so that I can use event delegation.
const { useState } = React;
function Note({ message }) {
// Initialise a new state to hold the note id
const [ noteClass, setNoteClass ] = useState('');
// The handler first checks to see if the
// clicked element is a "note" element.
// if it is it destructures the note id from the
// element's dataset, and then uses it to set state
function handleClick(e) {
if (e.target.matches('.note')) {
const { note } = e.target.dataset;
setNoteClass(note);
}
}
// When the state changes the containing element's
// class changes too.
return (
<div className={noteClass}>
<div id="note_head">
<div id="note_bin" onClick={handleClick}>
<div data-note="b" className="note b">B</div>
<div data-note="p" className="note p">P</div>
<div data-note="g" className="note g">G</div>
<div data-note="y" className="note y">Y</div>
</div>
<p>{message}</p>
</div>
</div>
);
}
ReactDOM.render(
<Note message="Message" />,
document.getElementById('react')
);
.note { padding: 0.25em; border: 1px solid #4444; }
.note:not(:last-child) { margin-bottom: 0.25em; }
.note:hover { background-color: #fffff0; cursor: pointer; }
.b { color: red; }
.p { color: blue; }
.g { color: green; }
.y { color: gray; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

How to apply box shadow on a div when input element inside it is focused

So I have created a custom input element with increment and decrement button inside a parent div.
I want to apply a box shadow on the parent div when the input element is in focus and not when the increment and decrement arrow are clicked.
Code is something like
<div className='parent'>
<input type='number' onChange={onChange}>
<div className='arrow-btns'>
<Increment/>
<Decrement/>
</div>
</div>
For a pure React based solution, you can:
Create a boolean state focused that represents whether the input is focused or not
Make a style object with box shadow that's set based on that state
Set that style for the parent
Use onFocus and onBlur events on the input to set the state
Since we used a state, the component will re-render when it's changed, so it'll set the box shadow value accordingly:
const { useState } = React;
const Increment = () => <button>Increment</button>
const Decrement = () => <button>Decrement</button>
function App() {
const [focused, setFocused] = useState(false);
const style = {
width: '200px',
height: '90px',
boxShadow: focused? '0 0 5px rgba(0,0,0,0.5)' : 'none'
}
const setToFocused = () => setFocused(true);
const setToUnfocused = () => setFocused(false);
return (
<div className='parent' style={style}>
<input type='number' onFocus={setToFocused} onBlur={setToUnfocused}></input>
<Increment/>
<Decrement/>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
<script crossorigin src="https://unpkg.com/react#18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.production.min.js"></script>
<div id="root"></div>
It's also good to separate the intents of the style objects, where one is the base style and the other depends on the state. Then just merge them:
const { useState } = React;
const Increment = () => <button>Increment</button>
const Decrement = () => <button>Decrement</button>
function App() {
const [focused, setFocused] = useState(false);
const baseStyle = {
width: '200px',
height: '90px'
}
const focusedStyle = focused
? { boxShadow: '0 0 5px rgba(0,0,0,0.5)' }
: {}
const style = {...baseStyle, ...focusedStyle}
const setToFocused = () => setFocused(true);
const setToUnfocused = () => setFocused(false);
return (
<div className='parent' style={style}>
<input type='number' onFocus={setToFocused} onBlur={setToUnfocused}></input>
<Increment/>
<Decrement/>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
<script crossorigin src="https://unpkg.com/react#18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can use focus and blur event on the input, and add and remove the placeholder class which apply the box shadow on the parent by input.parentNode.classList.add('placeholder') and input.parentNode.classList.remove('placeholder')
const input = document.getElementById('input');
input.addEventListener('focus', () => {
input.parentNode.classList.add('placeholder')
})
input.addEventListener('blur', () => {
input.parentNode.classList.remove('placeholder')
})
.placeholder {
box-shadow: 2px 3px #888888;
}
<div className='parent'>
<input id="input" type='number' onChange={onChange}>
<div className='arrow-btns'>
<Increment/>
<Decrement/>
</div>
</div>
You can use the :has selector.
.parent:has(input:focus) {
-webkit-box-shadow: -4px 7px 31px 25px rgba(0,0,0,0.14);
-moz-box-shadow: -4px 7px 31px 25px rgba(0,0,0,0.14);
box-shadow: -4px 7px 31px 25px rgba(0,0,0,0.14);
}
Please note that few browsers are yet to support it and once they do, this'll be a simple solution for your use case. You can read more about it here.
https://developer.mozilla.org/en-US/docs/Web/CSS/:has

How do I create an overlay in React?

I am currently trying to create an overlay on an image when hovering. I am able to get a box displayed on screen but it's not placed over the image.
featured.js
const Featured = ({ images }) => {
if (!images || !Array.isArray(images)) return null;
return (
<section className={styles.featuredWrapper} id="work">
{images.map((image) => {
return (
<div className={styles.wrap}>
<GatsbyImage
image={image.gatsbyImageData}
alt="Link to the alt text"
className={styles.featuredImg}
/>
<div className={styles.featuredOverlay}>Test</div>
</div>
);
})}
</section>
);
};
featured.module.css
.featuredImg {
width: 100%;
position: relative;
}
.featuredOverlay {
position: absolute;
background: black;
opacity: 0.5;
width: 100%;
height: 100%;
z-index: 1;
}
Every explanation I see revolves around the use of positions absolute and relative which makes me think my issue is how I am rendering my component. Am I using the position properties on the wrong elements?
import { useState } from "react";
import "./styles.css";
function Home() {
const [showOverlay, setShowOverlay] = useState(false);
return (
<div>
<div className="main-container">
<div className="main-container__grid">
<img
src="https://miro.medium.com/max/2000/1*3SjDVyFY09xZ7NYMO5kj0g.png"
className="test"
alt="Placeholder"
onHover={() => setShowOverlay(true)}
/>
{showOverlay && <div className="targeting-box" />}
</div>
</div>
</div>
);
}
export default Home;

Focused elements display wrong style

I have element with width 400% and I want to move it to left by using translateX(-(index/4)*100%) when focused index changes.
Changing focused element translateX property with tab keyboard button displays it wrong on middle elements (1,2) even though using same hardcoded styling works as expected. What am I missing here?
const {useState} = React;
const App = () => {
const [curr, setCurr] = useState(0);
const carouselStyles = {
transform: `translateX(${-(curr / 4) * 100}%)`
// uncomment to see that styling works fine with hardcoded values 1,2..
// transform: `translateX(${-(1 / 4) * 100}%)`
};
const handleFocus = (num) => {
if (num !== curr) {
setCurr(num);
}
};
console.log(carouselStyles);
return (
<div>
<div className="carousel" style={carouselStyles}>
<div className="item">
11 very long text
<a href="/111" onFocus={() => handleFocus(0)}>
11111
</a>
</div>
<div className="item">
22 very long text
<a href="/222" onFocus={() => handleFocus(1)}>
22222
</a>
</div>
<div className="item">
33 very long text
<a href="/333" onFocus={() => handleFocus(2)}>
33333
</a>
</div>
<div className="item">
44 very long text
<a href="/444" onFocus={() => handleFocus(3)}>
44444
</a>
</div>
</div>
current: {curr}
</div>
);
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
.carousel {
display: flex;
align-items: center;
width: 400%;
}
.item {
flex: 0 1 100%;
text-align: center;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I needed to prevent the scrolling and in my provided example its enough to add this line into handleFocus function
window.scrollTo(0, 0);
But in my real scenario parent wrapper also had overflow: hidden; which prevented above code from working. So I've used refs
const handleFocus = (num) => {
if (num !== curr) {
setCurr(num);
carouselRef.current.scrollTo(0, 0);
}
};
return (
<div ref={carouselRef}>
<div className="carousel" style={carouselStyles}>
...
</div>
current: {curr}
</div>
);

Prevent focus on Expand More button after content is inserted in React

I need to list out a long name list inside my page while showing all names at first is not desirable.
So I try to add an expand more button on it.
However, using a button will keep the browser focus on that button after it's pressed, left the button position unchanged on the screen while the name was inserted before that button.
On the other hand, using any, not focusable element (eg. div with onclick function) will do the desired behavior but lost the accessibility at all. Making the "button" only clickable but not focusable.
How do I make the button flushed to list bottom like the snippet div block does? Or is there a better choice to expand the existing list?
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Removing focus from the button in the click handler is probably the most elegant approach: e.target.blur(). It will work on any HTML element, whether it is focusable or not (as with the div in your case).
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
e.target.blur()
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Inspired by #MiKo, temporally unmount the button after click and set a timeout to add it back seems to do the work. Since browser lose the focus on original expand button, this will keep content flush down without focusing the original button:
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const [showBtn, setShowBtn] = React.useState(true)
const handleExpand = e => {
setShowBtn(false)
setIdx(idx + 1)
setTimeout(() => setShowBtn(true), 10)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
{showBtn?
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div> :
<div></div>
}
</div>
}
But I'm still looking a method that doesn't need to 'unmount' a thing which should be there all time.

Categories