Reputation: 1019
I have a problem with the React.js Dialog
component which contains a list of items. There is also an overlay in the Dialog
. It closes automatically when the user clicks on it. But the scenario is something different. The dialog also includes a list of items that have some external links wrapped around the <a>
element and some with the <div>
.
But the method of closing the dialog is not working properly. The Dialog automatically closes when I click inside any list item.
Please see this:
My goal was to close the dialog whenever the user clicked on the overlay or whenever the user clicked on the list item wrapped with the <a>
element. Otherwise, it should not be closed.
I still can't figure out What is the correct approach to tackle this issue?
App.js
import React, { useState } from 'react';
import Dialog from './Dialog';
import './App.css';
const App = () => {
const [isOpen, setIsOpen] = useState(false);
const onClickHandler = (isFalse) => {
if (!isFalse) {
setIsOpen(false);
} else {
setIsOpen(!isOpen);
}
};
return (
<div>
<button type="button" onClick={onClickHandler}>
Open Dialog
</button>
<Dialog isOpen={isOpen} onClick={onClickHandler} />
</div>
);
};
export default App;
Dialog.js
import React from 'react';
const Dialog = (props) => {
const { isOpen, onClick } = props;
const onCloseHandle = () => {
onClick(false);
};
return (
<div
className={isOpen ? 'dialog-wrap open' : 'dialog-wrap'}
onClick={onCloseHandle}
>
<div className="dialog">
<ul className="dialog-list">
<li>
<a
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
React.js
</a>
</li>
<li>
<a
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Another Link
</a>
</li>
<li>
<div>Should Not Close onClick</div>
</li>
<li>
<div>Should Not Close onClick</div>
</li>
</ul>
</div>
</div>
);
};
export default Dialog;
App.css
*,:before,:after {
box-sizing: border-box;
}
.dialog-wrap.open {
display: flex;
}
.dialog-wrap {
display: none;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding: 16px;
background-color: rgba(0,0,0,0.66);
}
@media (min-width: 640px) {
.dialog-wrap {
padding: 48px 32px;
}
}
@media (min-width: 1024px) {
.dialog-wrap {
padding-left: 40px;
padding-right: 40px;
}
}
.dialog {
position: relative;
margin: auto;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 12px 16px rgba(0, 0, 0, 0.08);
border-radius: 4px;
width: 500px;
}
.dialog-list {
margin-bottom: 0;
padding-left: 0;
color: #333;
list-style: none;
}
.dialog-list > li {
border-bottom: 1px solid #eee;
padding: 24px;
}
.dialog-list > li > a {
color: #333;
display: block;
text-decoration: none;
}
.dialog-list > li > div {
cursor: pointer;
}
Upvotes: 0
Views: 553
Reputation:
in this situation, I would create a .dialog-background
element and give onCloseHandle
to that.
I also hive my a
elements the onCloseHandle
as onClick
prop as well
If you do not want to change your react tree you can decide that from the event like this:
<div
className={isOpen ? "dialog-wrap open" : "dialog-wrap"}
onClick={(e) => {
if (!e.target.closest('.dialog') || e.target.closest('a')) {
onClick(false)
}
}}
/>
Upvotes: 1
Reputation: 9959
I think your click event is propagating up the elements, and triggering onCloseHandle
on your .dialog-wrap
.
The simplest solution to this would be to use Event.stopPropagation()
on the <div className="dialog">
E.g.
<div className="dialog" onClick={ e => e.stopPropagation() }>
(React technically uses "synthetic events", but they still include a stopPropagation
option as long as it's being interacted with inside React)
Upvotes: 1