css different style to specific div created by map - javascript

I am creating a list of divs, which was created with map.
function renderButtons(){
const options = [...Array(10).keys()] // returns [0,1,2...9]
return _.map(options, (option)=> renderOption(option))
}
function renderOption(option:number){
return (
<div className="option-container" onClick={() => setLowerContainerVisible(true)}>
<img alt="" src={"./images/feedback-icons/icon-"+option.toString()+".svg"}/>
{option+1}
</div>
)
}
this renders a list of divs, and I was able to change each div background, when hover, like this:
.option-container{
width: 76px;
height: 100px;
background-color: #7777ff;
display: flex;
}
.option-container:hover{
background-color: #adadf3;
}
I wish to be able to click on a div, and change its background color to white. everything I try will change the background of all the 10 divs to white. How can I make it so only the clicked one is changed?

I suggest that you use renderOption and renderButtons as two components rather than plain functions. In the RenderButtons component, you can use some state to maintain which item is clicked, and within RenderOption you can control whether the background color is white or not based on wehther or not the current rendered button is the clicked option. In your .map() method, you can use component rather than a function call <RenderOption option={option} ... />.
See example below:
const {useState} = React;
function RenderButtons() {
const [clickedItem, setClickedItem] = useState(-1);
return Array.from(
Array(10).keys(),
option => <RenderOption isClicked={clickedItem === option} option={option} setClicked={setClickedItem}/>
);
}
function RenderOption({isClicked, option, setClicked}) {
const handleClick = () => {
// setLowerContainerVisible(true) / other code to run when you click
setClicked(option); // set to current option
}
return (
<div className={"option-container " + (isClicked ? "clicked" : "")} onClick={handleClick}>
{option+1}
</div>
)
}
ReactDOM.render(<RenderButtons />, document.body);
.option-container {
width: 76px;
height: 100px;
background-color: #7777ff;
display: flex;
}
.option-container.clicked, .option-container:hover {
background-color: #adadf3;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
The className is a little messy as it involves a ternary, to clean this up it might be worth looking into using a node package such as classnames which allows you to easily build a list of classes based on conditions.

Do it in the event listener function:
<div className="option-container" onClick={highlightAndsetLowerContainerVisible}>
function highlightAndsetLowerContainerVisible(event){
event.preventDefault();
setLowerContainerVisible(true)
event.currentTarget.style.backgroundColor = "red";
}
You then might also want to reset the background color of the other divs

Related

onClick handler to change color of an element

I have a react element like this:
const [customStyle, setCustomStyles] = React.useState()
const handleSelect = (data) => {
const custom = {
border: '3px solid black',
backgroundColor: 'red',
padding: 20,
}
setCustomStyles(custom)
}
return (
<div
key={nanoid()}
className={styles.professorsContainer}
style={customStyle}
onClick={() => handleSelect(data)}
>
<p>
{data.date} {data.content}
</p>
</div>
)
Where I want to change the color of the {data.date} - {data.content} when I click on the text, but it does not want to change! any idea what I'm missing?
Something seems to be wrong with your block structure. Styles can be applied in the way you described. Find a working sandbox here: https://codesandbox.io/embed/kind-minsky-uq5w3?fontsize=14&hidenavigation=1&theme=dark
The correct CSS property for background color would be background-color.

How to access the div element with class and attach the popup to it using react?

i am trying to create a popupdialog to be shown when user clicks on a button. for that i am using portal.
i want it to look like in the picture below,
So basically, when user clicks on the add button i want the popup dialog to display like in the picture above.
in the popup component i want to render overlay with children. and when user clicks on overlay div the popup should close.
I have something that kind of works without using Portal and is like below,
below is my code that is without using Portal,
function Parent({isDialogOpen, setDialogOpen, setSomething}: Props) {
const [isClicked, setIsClicked] = React.useState(false);
const handleButtonClick = () => {
if (setIsDialogOpen) setIsDialogOpen(!isDialogOpen);
if (setSomething) setSomething(isDialogOpen);
setIsClicked(!isClicked);
};
return (
<button onClick={handleButtonClick}>click</button>
{isDialogOpen && isClicked &&
<Overlay>
<Dialog>
//some divs
</Dialog>
</Overlay>
}
);
}
const Overlay = styled.div`
position: fixed;
padding-top:60px;
bottom: 40px;
top: 0;
left: 0;
right: 0;
backdrop-filter: blur(8px);
z-index: 100;
display: flex;
justify-content: center;
align-items: center;
`;
const Dialog = styled.div`
padding: 16px;
width: 384px;
max-height: calc(100% - 200px);
display: flex;
flex-direction: column;
`;
Now i am rewriting above using portal like below,
function Parent({isDialogOpen, setDialogOpen, setSomething}: Props) {
const [isClicked, setIsClicked] = React.useState(false);
const handleButtonClick = () => {
if (setIsDialogOpen) setIsDialogOpen(!isDialogOpen);
if (setSomething) setSomething(isDialogOpen);
setIsClicked(!isClicked);
};
return (
<button onClick={handleButtonClick}>click</button>
{isDialogOpen && isClicked &&
<Popup setSomething={setSomething} setIsDialogOpen={setIsDialogOpen} setIsClicked=
{setIsClicked}>
<Dialog>
//some divs
</Dialog>
</Overlay>
}
);
}
function Popup({setIsClicked, setSomething, setIsDialogOpen, children}: Props) {
return ReactDom.createPortal(
<>
<Overlay
onClick={() => {
if (setIsDialogOpen) setIsDialogOpen(false);
if (setSomething) setSomething(true);
setIsClicked(false);
}}
>
{children}
</Overlay>
</>,
//dont know what to pass here
);
}
Basically as seen in picture above, i want to render the overlay with dialog.
now in popup component i want to create div with classname 'popup' and find the div element with class navbar and attach this div popup to the navbar div
and pass this div element with class popup in the reactDOM.createPortal.
i am new to react and not sure how to do this. could someone help me with this.
thanks.
As I mentioned to you in the comment, react doesn't create the dom node for you. You must do it yourself. How you do it depends on your needs. the most basic example i can think of is below:
When component mounts first time, we need to create the portal and insert it into document.body
once we are sure portal exists, we can render into the portal using our dom ref.
function Popup({setIsClicked, setSomething, setIsDialogOpen, children}: Props) {
const [portal,setPortal] = React.useState<HTMLDivElement|null>( (document.getElementById('my-portal') as HTMLDivElement)||null);
const createPortalIfNotExists = React.useCallback(()=>{
if(portal===null){
const el = document.createElement('div');
el.id='my-portal';
document.body.appendChild(el);
// switch this line for the one above if you want it to be first in tree
// document.body.insertBefore(el, document.body.firstChild);
setPortal(document.getElementById('my-portal') as HTMLDivElement);
}
},[portal]);
createPortalIfNotExists();
if(portal===null){
return null;
}
return ReactDom.createPortal(
<>
<Overlay
onClick={() => {
if (setIsDialogOpen) setIsDialogOpen(false);
if (setSomething) setSomething(true);
setIsClicked(false);
}}
>
{children}
</Overlay>
</>,
portal
);
}
This is just one possible way of doing it. There are other more advanced use cases where you would have the portal be rendered by some other element in your component tree. But this should be enough to get you started. Also, i haven't tested this as i don't have tsc/tslint on this machine so YMMV

Hiding child component on hover state of parent using styled-component

I have a react component like this -
const MyComponent = () => (
<ContainerSection>
<DeleteButtonContainer>
<Button
theme="plain"
autoWidth
onClick={() => {
onDeleteClick();
}}
>
Delete
</Button>
</DeleteButtonContainer>
</ContainerSection>
);
I want to show the DeleteButtonContainer only when the user hovers over ContainerSection. Both of them are styled-components. I couldn't find any way to do it using just css (using hover state of parent inside child), so I used something like this using state -
const MyComponent = ()=>{
const [isHoveredState, setHoveredState] = useState<boolean>(false);
return (<ContainerSection onMouseEnter={() => setHoveredState(true)} onMouseLeave={() => setHoveredState(false)}>
<DeleteButtonContainer style={{ display: isHoveredState ? 'block' : 'none' }}>
<Button
theme="plain"
autoWidth
disabled={!isHoveredState}
onClick={() => {
onDeleteClick();
}}
>
Delete
</Button>
</DeleteButtonContainer>
</ContainerSection>)
};
Now I want to always show DeleteButtonContainer when it's on mobile device since it doesn't have hover. I know I can always right more JS to achieve this, but I want to do it using CSS and if possible I want to remove state completely.
So is there a way to achieve this using just styled component and not writing custom JS?
You can reference one component in another, and use media queries to enable the rule for non mobile resolutions.
Hover the the golden bar to see the button, and shrink the width to disable the hover rule.
const DeleteButtonContainer = styled.div``;
const ContainerSection = styled.div`
height: 2em;
background: gold;
#media (min-width: 640px) { // when resolution is above 640px
&:not(:hover) ${DeleteButtonContainer} { // if DeleteButtonContainer is not under an hovered ContainerSection
display: none;
}
}
`;
const Button = styled.button``;
const MyComponent = () => (
<ContainerSection>
<DeleteButtonContainer>
<Button>
Delete
</Button>
</DeleteButtonContainer>
</ContainerSection>
);
ReactDOM.render(
<MyComponent />,
root
);
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/styled-components/4.4.0/styled-components.js"></script>
<div id="root"></div>

Styling individual child component in React

I have a parent ButtonGroupcomponent that takes in children like this.props.children. The children I'm passing to it is the btnItem component that renders out single buttons. We can add as many of these buttons as we want.
//ButtonGroup Component
render() {
return (
<div>
{this.props.children}
</div>
)
}
//buttonItem component:
render() {
return (
<button disabled={this.props.disabled}>{this.props.caption}</button>
)
}
//final render
<ButtonGroupComponent>
<buttonItem caption="Nothing"/>
<buttonItem caption="Something" disabled={true}/>
<buttonItem caption="Refresh"/>
</ButtonGroupComponent>
^ This is what I get out of the above code.
What I want to achieve is a way for me style the border radius of the first and last item so that they have a curved border. This would have to be dynamic as this styling will be dependent on how many children buttonItem we render.
I should also mention that I'm using styled-components for the css of each button.
you can use first and last child css pseudo selector here. in your Component write following code.
const ButtonGroupComponent= styled.div`
button:first-child {
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
button:last-child {
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
`;
and render function like this
<ButtonGroupComponent>
<buttonItem caption="Nothing"/>
<buttonItem caption="Something" disabled={true}/>
<buttonItem caption="Refresh"/>
</ButtonGroupComponent>
check out this demo
I wrap a quick example here, I didn't test it yet but you could try:
const StyledButtonGroupComponent = styled(
({ willBeStyled, children, ...rest }) =>(
<ButtonGroupComponent {...rest}>{children}</ButtonGroupComponent>
))`
${props => React.Children.toArray(props.children).length <= props.willBeStyled && `
...first child, last child styles go here...
`}
`

Detect click outside div using javascript

I'd like to detect a click inside or outside a div area. The tricky part is that the div will contain other elements and if one of the elements inside the div is clicked, it should be considered a click inside, the same way if an element from outside the div is clicked, it should be considered an outside click.
I've been researching a lot but all I could find were examples in jquery and I need pure javascript.
Any suggestion will be appreciated.
It depends on the individual use case but it sounds like in this example there are likely to be other nested elements inside the main div e.g. more divs, lists etc. Using Node.contains would be a useful way to check whether the target element is within the div that is being checked.
window.addEventListener('click', function(e){
if (document.getElementById('clickbox').contains(e.target)){
// Clicked in box
} else{
// Clicked outside the box
}
});
An example that has a nested list inside is here.
You can check if the clicked Element is the div you want to check or not:
document.getElementById('outer-container').onclick = function(e) {
if(e.target != document.getElementById('content-area')) {
console.log('You clicked outside');
} else {
console.log('You clicked inside');
}
}
Referring to Here.
you can apply if check for that inside your click event
if(event.target.parentElement.id == 'yourID')
In Angular 6 and IONIC 3, I do same as here:
import {Component} from 'angular/core';
#Component({
selector: 'my-app',
template: `
<ion-content padding (click)="onClick($event)">
<div id="warning-container">
</div>
</ion-content>
`
})
export class AppComponent {
onClick(event) {
var target = event.target || event.srcElement || event.currentTarget;
if (document.getElementById('warning-container').contains(target)){
// Clicked in box
} else{
// Clicked outside the box
}
}
}
This working fine on web/android/ios.
It might be helpful for someone, Thanks.
Try this solution it uses pure javascript and it solves your problem. I added css just for better overview... but it is not needed.
document.getElementById('outer-div').addEventListener('click', function(){
alert('clicked outer div...');
});
document.getElementById('inner-div').addEventListener('click', function(e){
e.stopPropagation()
alert('clicked inner div...');
});
#outer-div{
width: 200px;
height: 200px;
position: relative;
background: black;
}
#inner-div{
top: 50px;
left: 50px;
width: 100px;
height: 100px;
position: absolute;
background: red;
}
<div id="outer-div">
<div id="inner-div">
</div>
</div>
I came up with a hack for this that's working well for me and that might help others.
When I pop up my dialog DIV, I simultaneously display another transparent DIV just behind it, covering the whole screen.
This invisible background DIV closes the dialog DIV onClick.
This is pretty straightforward, so I'm not going to bother with the code here. LMK in the comments if you want to see it and I'll add it in.
HTH!
closePopover () {
var windowBody = window
var popover = document.getElementById('popover-wrapper') as HTMLDivElement;
windowBody?.addEventListener('click', function(event){
if(popover === event.target) {
console.log("clicked on the div")
}
if(popover !== event.target) {
console.log("clicked outside the div")
}
})
}
}
I recently needed a simple vanilla JS solution which solves for:
Ignoring specific selectors including whether a parent contains one of these selectors
Ignoring specific DOM nodes
This solution has worked quite well in my app.
const isClickedOutsideElement = ({ clickEvent, elToCheckOutside, ignoreElems = [], ignoreSelectors = [] }) => {
const clickedEl = clickEvent.srcElement;
const didClickOnIgnoredEl = ignoreElems.filter(el => el).some(element => element.contains(clickedEl) || element.isEqualNode(clickedEl));
const didClickOnIgnoredSelector = ignoreSelectors.length ? ignoreSelectors.map(selector => clickedEl.closest(selector)).reduce((curr, accumulator) => curr && accumulator, true) : false;
if (
isDOMElement(elToCheckOutside) &&
!elToCheckOutside.contains(clickedEl) &&
!didClickOnIgnoredEl &&
!didClickOnIgnoredSelector
){
return true;
}
return false;
}
const isDOMElement = (element) => {
return element instanceof Element || element instanceof HTMLDocument;
}
In React you can use useClickOutside hook from react-cool-onclickoutside.
Demo from Github:
import { useClickOutside } from 'use-events';
const Example = () => {
const ref1 = React.useRef(null);
const ref2 = React.useRef(null);
const [isActive] = useClickOutside([ref1, ref2], event => console.log(event));
return (
<div>
<div ref={ref1} style={{ border: '1px dotted black' }}>
You are {isActive ? 'clicking' : 'not clicking'} outside of this div
</div>
<br />
<div ref={ref2} style={{ border: '1px dotted black' }}>
You are {isActive ? 'clicking' : 'not clicking'} outside of this div
</div>
</div>
);
};
Live demo

Categories