How would you close dialog box by clicking outside of it in addition to clicking the Esc button?
<!DOCTYPE html>
<html>
<body>
<p>Click the button to show the dialog.</p>
<button onclick="myFunction()">Show dialog</button>
<p><b>Note:</b> Use the "Esc" button to close the modal.</p>
<p><b>Note:</b> The dialog element is only supported in Chrome 37+, Safari 6+ and Opera 24+.</p>
<dialog id="myDialog">This is a dialog window</dialog>
<script>
function myFunction() {
document.getElementById("myDialog").showModal();
}
</script>
</body>
</html>
The showModal() method of the HTMLDialogElement interface displays the dialog as a modal, over the top of any other dialogs that might be present. It displays into the top layer, along with a ::backdrop pseudo-element. Interaction outside the dialog is blocked and the content outside it is rendered inert. MDN documentation
As you can see, outside of the dialog all other html nodes will be blocked. But, we can add a child node as a wrapper for our content, thus we separate our content from the outside area. Let's remove the extra padding from dialog with css and add it to the modal class.
const dialog = document.getElementById('myDialog');
const button = document.querySelector('button');
function openDialog() {
dialog.showModal();
}
function closeDialog(event) {
// If the target dialog is
if (!event.target.contains(dialog)) return;
dialog.close();
}
button.addEventListener('click', openDialog);
document.addEventListener('click', closeDialog);
dialog {
padding: 0;
}
.modal {
display: flex;
padding: 1rem;
}
dialog::backdrop {
background-color: rgba(255, 0, 0, 0.25);
}
<p>Click the button to show the dialog.</p>
<button>Show dialog</button>
<p><b>Note:</b> Use the "Esc" button to close the modal.</p>
<p><b>Note:</b> The dialog element is only supported in Chrome 37+, Safari 6+ and Opera 24+.</p>
<dialog id="myDialog">
<div class="modal">
<p>This is a dialog window</p>
</div>
</dialog>
In general, you can use the onblur- or onfocusout-event. Both fire when you leave the element aka. click on another element. (Though I haven't tested it with a dialog specifically.)
<dialog onblur="yourFunction()">
I know it is a little bit impractical but you can add a div that is as big as the whole screen and, when you click on 'show dialog', the div's display is block and when you click on the div that has the size of the screen, the dialog disappears.
<!DOCTYPE html>
<html>
<body>
<p>Click the button to show the dialog.</p>
<button onclick="myFunction()">Show dialog</button>
<p><b>Note:</b> Use the "Esc" button to close the modal.</p>
<p><b>Note:</b> The dialog element is only supported in Chrome 37+, Safari 6+ and Opera 24+.</p>
<div id="close-dialog-element" onclick="closeDialog()">
<dialog close id="myDialog">This is a dialog window</dialog>
</div>
<style>
#close-dialog-window {
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
#myDialog {
z-index: 11;
}
</style>
<script>
function closeDialog() {
document.getElementById('close-dialog-element').style.display = 'none';
document.getElementById('myDialog').close();
}
function myFunction() {
document.getElementById("myDialog").showModal();
}
</script>
</body>
</html>
.showModal() uses a ::backdrop which is a pseudo-element which is invisible to the DOM, therefore untouchable. The example below shows how to use the .show() method so that a click outside a <dialog> is possible.
Details are commented in example
// Reference <dialog> and <button>
const modal = document.querySelector('.modal');
const open = document.querySelector('.open');
// Bind click event to <button>
open.onclick = openModal;
// Bind click event to <main>
document.querySelector('main').onclick = closeModal;
// Bind keydown event close the <dialog> when Esc key is clicked
document.onkeydown = e => {
if (e.key === "Escape") {
closeModal(e);
}
}
function openModal(e) {
// This opens <dialog> with no ::backdrop
modal.show();
// A .open to <html> so it appears there's a ::backdrop
document.documentElement.classList.add('open');
// Disable <button>
this.disabled = true;
}
function closeModal(e) {
// Reference the tag the user clicked
const clk = e.target;
/*
If the user clicked the <dialog> or anything within the
<dialog> OR the <button>...
... do nothing...
... otherwise close <dialog>...
... remove .open from <html>...
... enable <button>
*/
if (modal.contains(clk) || clk.matches('.open')) {
return
}
modal.close();
document.documentElement.classList.remove('open');
open.disabled = false;
}
/* Simulate a ::backdrop */
:root.open {
overflow: hidden;
background-color: rgba(0, 0, 0, 0.2);
}
/* Cover all of viewport */
main {
width: 100%;
min-height: 100vh;
}
/* When <dialog> is open <button> should be inert */
.open .open {
pointer-events: none;
}
<!DOCTYPE html>
<html>
<body>
<!-- Wrap everything with <main> -->
<main>
<p>Click the button to show the dialog.</p>
<button class='open'>Show Modal</button>
<dialog class="modal">
<p><b>Note:</b> Use the "Esc" button to close the modal.</p>
<p><b>Note:</b> Or click outside of modal to close it.</p>
</dialog>
</main>
</body>
</html>
You can also try the way described by Adam Argyle here: https://web.dev/building-a-dialog-component/#adding-light-dismiss
Get the dialog element and add an event listener to the dialog
element
const dialog = document.querySelector('dialog);
dialog.addEventListener('click', lightDismiss);
Create the handler function and check whether the dialog element
triggered the event. If yes, call .close() method. This works
because, by default, the dialog element creates a backdrop that
covers all other elements on the page, and clicking there ===
clicking on the dialog element itself. This will not affect children
elements inside the dialog, so clicking there will not close the
dialog.
const lightDismiss = ({target:dialog}) => {
if (dialog.nodeName === 'DIALOG')
dialog.close('dismiss')
}
By the way, I recommend reading this wonderful dialog tutorial.
Related
I build a popup search box, that open if I click on search btn or press any key on keyboard.
for closing popup, press esc key AND press close button AND click outside the search box will do the job.
but I faced a problem, for open popup using search button, I use onclick attribute and call a function
and for closing popup if I click outside the popup, I used click event listener, and these interfere with each other, so when I click on search button, popup will open and immediately click event listener closes the popup !!!!
so what should I do ?!
search btn:
<i class="fas fa-search"></i>
opensearch() function:
function openSearch() {
document.getElementById("popup").style.display = "block";
document.getElementById("search-field").focus();
}
event listener for outside click:
document.addEventListener ('click', function(event) {
// Detect outside clicks
var isClickInside = document.getElementById('PopupSearchWrapper').contains(event.target);
if (!isClickInside) {
closeSearch();
}
});
(using bulma css classes)
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma#0.9.3/css/bulma.min.css">
<div class="modal" id="search">
<div class="modal-background" id='back'></div>
<div class="modal-card">
<button class="delete" id="result-close"></button>
</div>
</div>
Because the background and the card are siblings (with high z-index) then what's not the card will be the background. Simply set the background and close button .onclick to closeSearch()
key CSS properties
html {
margin:0;
padding:0
}
.modal{
align-items:center;
display:none; // display: flex when active
flex-direction:column;
justify-content:center;
overflow:hidden;
position:fixed;
z-index:40
}
.modal-background {
bottom:0;
left:0;
position:absolute;
right:0;
top:0
}
I've got modal window: parent div (wrapper with dark transparent background) and child div (with buttons, inputs and text). Both of them are fixed positioning.
In JavaScript I do simple code: If I click on parent fixed div (.modal-wrapper) - it must close (remove) whole modal window. And same result if I click on close button ('X' button in the corner of modal window).
Here is my simple JS code:
function doModalClose() {
modalWrapper.remove();
}
closeBtn.addEventListener('click', doModalClose);
modalWrapper.addEventListener('click', doModalClose);
And html markup:
<div class="modal-wrapper">
<div class="modal-window">
<div class="btn-wrapper"><span class="close-btn"></span></div>
other modal elements...
</div>
</div>
The main question is that when I click on parent div it works correctly, and as when I click on .close-btn. But if I clicked on child div (.modal-window) it closes whole modal too, which incorrect!
I make a simple alert(); check: when I click on parent one it should say Modal Wrapper and when I click on child one it should say Modal Window.
And when I click on child window it calls two alert(); functions: first is child's Modal Window and after that parent's Modal Wrapper.
So that's the reason of my issue, but I don't understand the reason of that kind of behavior. I use on child div z-index: 2; and z-index: 1; on parent div, but it still doesn't help.
Thank you for your attention!
This happens because one is inside another one, if you click the inner div you also click the outer one because the inner is inside the outer one. You need an extra handler just for .modal-window and to use event.stopPropagation(). Run this code snippet and see by yourself.
function doModalClose() {
modalWrapper.remove();
}
var closeBtn = document.getElementsByClassName('close-btn')[0]
var modalWrapper = document.getElementsByClassName('modal-wrapper')[0]
var modalWindow = document.getElementsByClassName('modal-window')[0]
var button1 = document.getElementsByClassName('button1')[0]
var button2 = document.getElementsByClassName('button2')[0]
closeBtn.addEventListener('click', doModalClose);
modalWrapper.addEventListener('click', doModalClose);
modalWindow.addEventListener('click', function (event) {
event.stopPropagation()
})
button1.addEventListener('click', function (event) {
alert('button1')
})
button2.addEventListener('click', function (event) {
alert('button2')
})
div {margin: 5px; padding: 5px}
.modal-wrapper {border: 2px solid red}
.modal-window {border: 2px solid blue}
.btn-wrapper, .btn {border: 2px solid green}
<div class="modal-wrapper">modal-wrapper
<div class="modal-window">modal-window
<div class="btn-wrapper"><span class="close-btn">button</span></div>
<a class="btn button1">Button1</a>
<a class="btn button2">Button2</a>
</div>
</div>
I have a simple HTML Code in combination with css and JavaScript.
When you are clicking on a link, your URL will be updated like "#link1". Then the visibility of an overlay turn on visible, which is only shown at "#link1"
Now the problem:
When I click on the link my screen automatically scrolls to this overlay, but I didn't want that.
I tried "return false" or "event.preventDefault()". This works, but my URL didn't get updated, so my overlay will not appear.
Dose anyone has an idea how I can do this?
Also tried:
return null
event.preventDefault()
onClick
----html----
<!--The Link-->
<area href="#link1" shape="rect" coords="0,0,100,100" />
<!--The Overlay-->
<div id="link1" class="overlay">
<div class="popup">
...some text...
</div>
</div>
----css----
.overlay {
visibility: hidden;
... some other stuff ...
}
.overlay:target {
visibility: visible;
}
Can you do it this way instead of using href="#link1", do href="javascript:void(0)", and make overlay property appear using jquery property link
$('body').on('click','a', function(){
//Something here
}
Start by setting the display property of the overlay class to none
You need to create a function that will get triggered when the page is loaded or when a link that starts with # is clicked.
That function will get the hash of the url, get the corresponding element and set its display property to something other than none
This way the element won't be visible when the page loads or the hash is changed.
if (document.readyState === "complate") onLoad();
else addEventListener("load", onLoad);
function onLoad() {
var target = document.getElementById(new URL(document.URL).hash.substring(1));
if (target && target.classList.contains("overlay")) {
target.classList.add("active");
}
}
document.querySelectorAll("[href^='#']").forEach(function(link) {
link.addEventListener("click", function() {
setTimeout(onLoad, 0);
});
});
.overlay {
display: none;
}
.overlay.active {
display: block;
}
Show overlay
<!--The Overlay-->
<div id="link1" class="overlay">
<div class="popup">
...some text...
</div>
</div>
The other simpler solution is to use position: fixed to make the elements position static and always have it on the view-port (elimination the need for scrolling).
I use an HTML <dialog> element. I want to be able to close the dialog when clicking outside of it.
Using "blur" or "focusout" event does not work.
I want the same thing as Material Design dialog, where it closes the dialog when you click outside of it:
https://material-components-web.appspot.com/dialog.html
How can I achieve that?
Thanks in advance.
When a dialog is opened in modal mode, a click anywhere on the viewport will be recorded as a click on that dialog.
The showModal() method of the HTMLDialogElement interface displays the dialog as a modal, over the top of any other dialogs that might be present. It displays into the top layer, along with a ::backdrop pseudo-element. Interaction outside the dialog is blocked and the content outside it is rendered inert.
Source: HTMLDialogElement.showModal()
One way to solve the question is to:
Nest a div inside your dialog and, using CSS, make sure it covers the same area as the dialog (note that browsers apply default styles to dialogs such as padding)
Add an event listener to close the dialog when a user clicks on the dialog element (anywhere on the viewport)
Add an event listener to prevent propagation of clicks on the div nested inside the dialog (so that the dialog does not get closed if a user clicks on it)
You can test this with the code snippet below.
const myButton = document.getElementById('myButton');
myButton.addEventListener('click', () => myDialog.showModal());
const myDialog = document.getElementById('myDialog');
myDialog.addEventListener('click', () => myDialog.close());
const myDiv = document.getElementById('myDiv');
myDiv.addEventListener('click', (event) => event.stopPropagation());
#myDialog {
width: 200px;
height: 100px;
padding: 0;
}
#myDiv {
width: 100%;
height: 100%;
padding: 1rem;
}
<button id="myButton">Open dialog</button>
<dialog id="myDialog">
<div id="myDiv">
Click me and I'll stay...
</div>
</dialog>
This is how I did it:
function dialogClickHandler(e) {
if (e.target.tagName !== 'DIALOG') //This prevents issues with forms
return;
const rect = e.target.getBoundingClientRect();
const clickedInDialog = (
rect.top <= e.clientY &&
e.clientY <= rect.top + rect.height &&
rect.left <= e.clientX &&
e.clientX <= rect.left + rect.width
);
if (clickedInDialog === false)
e.target.close();
}
Modal
To close a modal dialog (i.e. a dialog opened with showModal) by clicking on the backdrop, you could do as follows:
const button = document.getElementById('my-button');
const dialog = document.getElementById('my-dialog');
button.addEventListener('click', () => {dialog.showModal();});
// here's the closing part:
dialog.addEventListener('click', (event) => {
if (event.target.id !== 'my-div') {
dialog.close();
}
});
#my-dialog {padding: 0;}
#my-div {padding: 16px;}
<button id="my-button">open dialog</button>
<dialog id="my-dialog">
<div id="my-div">click outside to close</div>
</dialog>
This places the dialog content in a <div>, which is then used to detect whether the click was outside the dialog, as suggested here. The padding and margins in the example are adjusted to make sure the <dialog> border and <div> border coincide.
Note that the modal dialog's "background" can be selected in CSS using ::backdrop.
Non-modal
For a non-modal dialog (opened using show), you could add the event listener to the window element instead of the dialog, e.g.:
window.addEventListener('click', (event) => {
if (!['my-button', 'my-div'].includes(event.target.id)) {
dialog.close();
}
});
In this case we also need to filter out button clicks, otherwise the dialog is immediately closed after clicking the "open dialog" button.
Well, code it the same way you speak it.
If you click on an element that isn't the desired dialog, close the dialog.
Here is an example:
<div id="content">
<div id="dialog" class="dialogComponent">
<div id="foo" class="dialogComponent">
test 123 123 123 123
<input class="dialogComponent class2" type="text">
</div>
<button class="dialogComponent">Submit</button>
</div>
</div>
#content { width: 100%; height: 333px; background-color: black;}
#dialog { margin: 33px; background-color: blue; }
$('#content').click(function(e) {
if (!e.target.classList.contains("dialogComponent"))
alert('Closing Dialog');
});
https://jsfiddle.net/scd9mwk7/
I have created a fairly classic modal. Fixed modal in the middle of the screen. Semi-transparent background and a box in the middle.
The box contains some click event listeners (e.g a close X and a button that performs another action).
I want to allow the user to close the modal by clicking on the outer background.
HTML example
<div class="modal">
<div class="modal__inner">
<div class="modal_closebtn">X</div>
</div>
</div>
Currently, I have the following to handle closing the modal based on click of .modal, which covers the whole screen.
document.querySelector(".modal").addEventListener("click", () => {
this.toggleModal();
});
My problem is, this fires if you click inside the .modal__inner too.
So I tried adding an eventListener to stopPropagation()
document.querySelector(".modal__inner").addEventListener("click", e => {
e.stopPropagation();
});
This now blocks the eventListener for my .modal_closebtn and any other events that occur inside the modal.
How can I ensure that the click for the background div only works on the modal div not modal__inner or any others? Unfortunately when I review e.currentTarget after clicking inside my div it returns .modal not .modal__inner even though it does appear that I clicked inside that div.
Non jQuery solution please
To avoid the execution of querySelector too many times and gain a little of performance, store that result into a variable.
var elem = document.querySelector(".modal")
elem.addEventListener("click", (e) => {
if (e.target !== elem) return;
console.log('toggle!');
//this.toggleModal();
});
.modal {
width: 300px
height: 300px;
border: 1px solid #000
}
.modal__inner {
width: 100px
height: 100px;
border: 1px solid red
}
<div id="hola" class="modal">
Parent modal
<br>
<br>
<br>
<div id="epale" class="modal__inner">
Inner modal
<div class="modal_closebtn">X</div>
</div>
</div>
Happy coding!
The event object has the original target of the event, use that to figure out what is the source of the event.
var modal = document.querySelector(".modal");
modal.addEventListener("click", (e) => {
if (e.target !== modal) return;
this.toggleModal();
});