Mouse pointer cannot detect element to trigger onClick() - javascript

From the snippet below, you can see that there are 2 Box components that are "underneath" the BigBox component, hence the mouse cannot detect them to trigger onClick(). Is there a way for the mouse to detect those 2 components.
I understand that you can just render the BigBox component first then Box like what I did with the <Box id="yes onClick"> component
However, for the project I'm working on, I specifically need to render a <Box>-like component first then a <BigBox>-like component.
const boxStyle = {
position: "absolute",
border: "1px #999 solid",
borderRadius: "10px",
textAlign: "center",
}
const Box = (props) => {
return (
<div style = {{ ...boxStyle, left: props.left, top: props.top, height: 50, width: 50 }}
onClick = {() => console.log("clicked")} > { props.id } </div>
);
};
const BigBox = (props) => {
return (
<div style = {{ ...boxStyle, left: props.left, top: props.top, height: 200, width: 200 }}> Big Box </div>
);
}
const App = () => {
return (
<div >
<Box id="no onClick 1" left={40} top={50}></Box>
<Box id="no onClick 2" left={80} top={100}></Box>
<BigBox left={10} top={10}></BigBox>
<Box id="yes onClick" left={150} top={150}></Box>
</div>
);
};
ReactDOM.render( <App/> ,
document.getElementById('root')
);
<div id="root"></div>
<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>

Option 1 - pointer-events
You can set the CSS Property pointer-events of the BigBox style to none.
const boxStyle = {
position: "absolute",
border: "1px #999 solid",
borderRadius: "10px",
textAlign: "center",
}
const Box = (props) => {
return (
<div style = {{ ...boxStyle, left: props.left, top: props.top, height: 50, width: 50 }}
onClick = {() => console.log("clicked")} > { props.id } </div>
);
};
const BigBox = (props) => {
return (
<div style = {{ ...boxStyle, pointerEvents: 'none', left: props.left, top: props.top, height: 200, width: 200 }}> Big Box </div>
);
}
const App = () => {
return (
<div >
<Box id="no onClick 1" left={40} top={50}></Box>
<Box id="no onClick 2" left={80} top={100}></Box>
<BigBox left={10} top={10}></BigBox>
<Box id="yes onClick" left={150} top={150}></Box>
</div>
);
};
ReactDOM.render( <App/> ,
document.getElementById('root')
);
<div id="root"></div>
<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>
Option 2 - z-index
Another solution would be to set the z-index of you Boxes to something bigger than the z-index of the BigBox component. That way the BigBox can still receive pointer events.
Option 3 - Use the layout hierarchy
Probably the most idiomatic way would be, to put your boxes inside BigBox.

Related

Same page section navigation using dynamic useRef hook in React

There are many packages available to navigate between sections on same page. But I don't want to integrate any package as dependency for doing a simple task. Here is a sample code for navigation which is not dynamic
import React, { useRef } from "react";
function Navigation() {
const top = useRef(null);
const scrollAnySection = (ref) => {
window.scrollTo({
top: ref.current.offsetTop - 10,
behavior: 'smooth',
});
};
return (
<>
<div className="navigation" style={{ width: "80%", margin: "3rem auto" }}>
<section className="menu--container">
<h3 onClick={() => scrollAnySection(top)}>Top</h3>
</section>
<section>
<h3 ref={top} className="section--items"
style={{ margin:"100rem 0",textAlign:"center",border:"1px solid blue" }}> Top </h3>
</section>
</div>
</>
);
}
export default Navigation;
But I am getting difficulty with dynamic useRef here where the ref of HTML element will be called from an array. Below is a sample what I am trying to do for dynamic contents. But I am not getting the expected result.
import React, { useRef } from "react";
function Navigation() {
const listSection = ["Head", "Body", "Main", "Footer", "Bottom"];
const navigateToSection = useRef([]);
const scrollAnySection = (ref) => {
window.scrollTo({
top: ref.current.offsetTop - 10,
behavior: 'smooth',
});
};
return (
<>
<div className="navigation" style={{ width: "80%", margin: "3rem auto" }}>
<section className="menu--container" style={{ display: "flex", justifyContent: "space-between"}}>
{ listSection.map((item) => (
<h3
style={{ padding: ".5rem 2rem", borderRadius: "25rem", border: "1px solid blue" }}
className="menu--items"
onClick={() => scrollAnySection ({ item }) }
>{ item }</h3>
))
}
</section>
<section>
{ listSection.map((item, index) => (
<h3
className="section--items"
style={{ margin: "100rem 0", textAlign: "center", border: "1px solid blue" }}
ref={ ref => { navigateToSection.current[index] = ref; }}
>This is { item } section</h3>
))
}
</section>
</div>
</>
);
}
export default Navigation;
I am getting Uncaught TypeError: ref.current is undefined at scrollAnySection and onClick() event.
Any suggestion or a better approach will be appreciated. Thanks.

Update scroll so that cursor remains with the dragged item when new items are added

I have a list of items in a scrollable div (overflow-y: scroll). When I start dragging (in the real website I am using react-beautiful-dnd) some of the items will expand to show subitems since they are dropdown. This causes the position of the items to shift down and so the item that I was dragging moves downwards but my cursor remains in the same position.
Here's the link to the problem: https://codesandbox.io/s/gracious-einstein-vvsbtp?file=/src/App.js
import { useState } from "react";
export default function App() {
const [show, setShow] = useState(false);
const handleDrag = () => {
setShow(true);
};
const DisplayHello = () => {
return (
<>
{new Array(5).fill(0, 0, 5).map((ele, index) => {
return (
<p style={{ margin: "5px" }} key={index}>
Hello
</p>
);
})}
</>
);
};
return (
<div className="App" style={{ height: "400px" }}>
<div
style={{
display: "flex",
flexDirection: "column",
height: "100%",
border: "1px solid red",
width: "200px",
overflow: "scroll"
}}
>
<DisplayHello />
{show && <DisplayHello />}
<div
style={{ backgroundColor: "dodgerblue" }}
draggable="true"
onDrag={handleDrag}
>
Drag Me
</div>
{show && <DisplayHello />}
<DisplayHello />
</div>
</div>
);
}
What I want is that even if the items expand, the cursor should remain on the draggable item. Is this even possible?

Is there a way to perform a click event only on a parent element?

In my app I would like to be able to click an item (background color, text, etc), have a modal pop up with a color picker, then change the color of the item.
The issue I'm having is that I made an onClick handler for the parent element to update a background color, but it's also activating when anything within the parent element is clicked.
I have an example in Codesandbox and you can see that whether you click the background or the buttons, the color picker comes up when I only want it activated for the background.
If anyone is familiar with Chakra-ui, this is my code:
const Navbar = () => {
const [display, setDisplay] = useState('none');
const [color, setColor] = useState('#1A202C');
const [showColorPicker, setShowColorPicker] = useState(false);
const { isOpen, onOpen, onClose } = useDisclosure();
/*
On click, showColorPicker becomes true and isOpen also becomes true
in order to display the modal with a color picker
*/
const handleModalClick = () => {
onOpen();
if (!showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
};
return (
<div>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent
bg='gray.600'
style={{ boxShadow: '2px 2px 2px 1px rgba(0, 0, 0, 0.2)' }}>
<ModalCloseButton color='gray.200' />
<ModalBody style={{ borderRadius: '10px' }}>
<Center>
{showColorPicker && (
<ChromePicker
color={color}
onChange={(updatedColor) => setColor(updatedColor.hex)}
/>
)}
</Center>
</ModalBody>
</ModalContent>
</Modal>
// Flex === a div with display flex
<Flex
bg={color}
color='gray.200'
style={{
textTransform: 'uppercase',
fontWeight: 'bold',
}}
onClick={handleModalClick}>
<Link p='5' _hover={{ color: 'cyan.400' }}>
<Text fontSize='xl'>Color Selector</Text>
</Link>
<Spacer />
<Flex
display={['none', 'none', 'flex', 'flex']}
fontSize='md'
align='center'>
<Link p='5' _hover={{ color: 'cyan.400' }}>
About
</Link>
<Link p='5' _hover={{ color: 'cyan.400' }}>
Portfolio
</Link>
<Link p='5' _hover={{ color: 'cyan.400' }}>
Contact
</Link>
</Flex>
</Flex>
...
</div>
);
};
Is there a way to show the color picker only when the background is clicked?
The app is also deployed on Netlify if you want to see the real example or all of the code on GitHub.
The event object has a target property, which holds the exact element that the user interacted with to trigger the event. So, you can just check if the target element is the parent element to know if they interacted with the parent directly or one of their children.
Here's one way of doing it:
if (e.target.classList.contains('navbar') && !showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
A more robust way of doing it would be to store the parent in a React ref, and make sure that e.target is exactly the same as that ref. (This is one of the places where it's ok to use a ref).
Here's a complete example that uses a ref. (in won't run in StackOverflow, because I didn't properly load up the libraries, but it'll work).
import "./styles.css";
import React, { useState, useRef } from "react";
import { ChromePicker } from "react-color";
export default function App() {
const [display, setDisplay] = useState("none");
const [color, setColor] = useState("#1A202C");
const [showColorPicker, setShowColorPicker] = useState(false);
const navBarRef = useRef();
const handleModalClick = e => {
if (e.target === navBarRef.current && !showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
};
return (
<>
<div
className="navbar"
ref={navBarRef}
style={{ backgroundColor: `${color}`, color: "white" }}
onClick={handleModalClick}
>
<button style={{ padding: "10px 15px 10px 15px", margin: "20px" }}>
Left
</button>
<button style={{ padding: "10px 15px 10px 15px", margin: "20px" }}>
Right
</button>
</div>
{showColorPicker && (
<ChromePicker
color={color}
onChange={(updatedColor) => setColor(updatedColor.hex)}
/>
)}
</>
);
}
Whats happening is called "Event Bubbling" and it is the intended behavior (you can read more about it here). Eventually, you'll find that it is very useful.
If you want to only handle events that are triggered from the same element where the handler is attached, you can do something like this:
const parent = document.getElementById('parent');
const handler = (e) => {
if (e.target !== e.currentTarget) {
return;
}
console.log('PARENT CLICKED!');
};
parent.addEventListener('click', handler);
#parent {
background-color: #123ff0;
}
.box {
display: inline-block;
height: 50px;
width: 50px;
background-color: #000000;
}
p {
color: #ffffff;
background-color: #000000;
}
<div id='parent'>
<span class='box'></span>
<span class='box'></span>
<p>This is a paragraph.</p>
<span class='box'></span>
<span class='box'></span>
</div>
The event object provided gives you some default preventions that you can use.
Example:
const handleClick = (event) => {
event.stopPropagation();
}
Should be added on clickable elements that are part of the parent and that should not trigger the event.
This will prevent your event to be propagated to the parent element.
I've forked your codesanbox and added my solution in.
https://codesandbox.io/s/infallible-hellman-66sln?file=/src/App.js
I think the above solutions are also correct and everything will depend on the situation.
More info here: https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation

React Js - Styling innerHTML on component - Not working - Parent styling is overriding child styling

I have two react components.
List container
List
The list needs to be inside the list container. Like so:
<Container innerhtml={<List></List>} ></Container>
The content of both components renders. However the styling of the child is overridden by the parents styling. (In this case, the background color)
This is the full code:
import React from "react";
export default function main() {
return <div>
<Container
innerhtml={<List></List>}
></Container>
</div>
}
function List() {
return <div style={{ backgroundColor: "#red!important", height: "150px", width: "150px" }}>
this is a list
</div>
}
function Container(props) {
return <div style={{ backgroundColor: "#94e49d38", height: "400px", width: "100vw-10px" }}>
this is container
{props.innerhtml}
</div>
}
I think it may be a similar thing to this: Style not working for innerHTML in Angular
However I cant find a React equivalent.
How can I get the list style to work?
Thank you
By refactoring a bit your code I found this Solution:
export default function Main() {
return (
<div>
<Container>
<List></List>
</Container>
</div>
);
}
function List() {
return (
<div style={{ backgroundColor: "red", height: "150px", width: "150px" }}>
this is a list
</div>
);
}
function Container(props) {
return (
<div
style={{
backgroundColor: "#94e49d38",
height: "400px",
width: "100vw-10px"
}}
>
{props.children}
</div>
);
}
by passing props.childreninstead of innerHtml and by removing the "#" before red this works fine, see the sandbox
import React from "react";
export default function Main() {
return (
<div>
<Container>
<List/>
</Container>
</div>
}
function List() {
return (
<div style={{ backgroundColor: "#75e936", height: "150px", width: "150px" }}>
this is a list
</div>
}
function Container(props) {
return(
<div style={{ backgroundColor: "#94e49d38", height: "400px", width: "100vw-10px" }}>
this is container
{props.children}
</div>
)
}
You put hashtag which is not required.
Change from
backgroundColor: "#red !important"
to
backgroundColor: "red !important"
#red is an invalid property value. It should be red as shown below.
backgroundColor: " red !important"

ReactJs, Semantic UI , toggle button

Hello i wanted to make a toggle button menu
ie by clicking on the button the div would increase the height with some transition
but I can't imagine how to do this in react
function App() {
const handleClick = e => {
e.preventDefault();
};
return (
<div
className="Wrapper"
style={{
backgroundColor: "#000",
height: "30px",
width: "200px"
}}
>
<Button
style={{ padding: 0, height: "30px", width: "200px" }}
circular
icon="settings"
onClick={handleClick}
>
Button
</Button>
</div>
);
}
my codebox: https://codesandbox.io/s/funny-minsky-j9x6x
You need to add a state variable to your function. You can now use hooks for that.
import React from "react";
import ReactDOM from "react-dom";
import { Button } from "semantic-ui-react";
import "./styles.css";
import { useState } from 'react';
function App() {
const [open, setOpen] = useState(0); // declare new state variable "open" with setter
const handleClick = e => {
e.preventDefault();
setOpen(!open);
};
return (
<div
className="Wrapper"
style={{
backgroundColor: "#000",
height: (open ? "400px" : "30px"), // make the div tall when "open"
width: "200px",
transition: "height 1s" // transition for 1 second
}}
>
<Button
style={{ padding: 0, height: "30px", width: "200px" }}
circular
onClick={handleClick}
>
Button
</Button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Categories

Resources