I've been trying to figure out how to close the modal I made by a window click but have no success. My modal is activated by adding a class and removing the 'show-modal' class. So I created the window event listener and seems like whenever I press the "a" tag in my HTML, it also considers that click as part of the window so the modal won't even open.
However, once I remove the window event listener, the modal is working fine; but the modal does not close unless I hit the "closeModal" button.
Is there something I am doing wrong or improve on? I tried googling around but their idea is different than mine.
I know I can do this using React & Boostrap but I am also trying to learn the vanilla JS way so I want to do this right!
const contact = document.querySelector('.contact');
const modal = document.querySelector('.modal');
const closeModal = document.querySelector('.closeBtn');
contact.addEventListener('click', showModal);
closeModal.addEventListener('click', modalClose);
window.addEventListener('click', modalClose);
function showModal(){
modal.classList.add('show-modal');
console.log('clicked')
}
function modalClose(){
modal.classList.remove('show-modal');
console.log('closed')
}
One way to get around this would be to add an "underlay" to the modal and listen for clicks on that instead of the window element.
Also I like to add the modal open class to the body so then it's easy to adjust styling on both the modal and modal underlay.
document.querySelector('button#open').addEventListener('click', e => {
document.body.classList.add('show-modal')
})
document.querySelector('button#close').addEventListener('click', e => {
document.body.classList.remove('show-modal')
})
document.querySelector('.underlay').addEventListener('click', e => {
document.body.classList.remove('show-modal')
})
.modal {
height: 100px;
width: 100px;
background-color: white;
display: none;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.underlay {
display: none;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
.show-modal .modal {
display: block;
}
.show-modal .underlay {
display: block;
}
<button id="open">Open Modal</button>
<div class="underlay"></div>
<div class="modal">
<button id="close">Close Modal</button>
</div>
</div>
You can use event.stopPropagation() to avoid event propagation, try this:
function showModal(){
event.stopPropagation();
modal.classList.add('show-modal');
console.log('clicked')
}
Related
The scenario is as follows.
Default Status (no layer popup)
When I click the button, layer popup shows.
Click the button or outside, layer popup will be hide.
I want to close the layer popup when I click background(outside) or button.
How can I do with Vanilla JS or jquery? (based on HTML)
I would appreciate it if you could answer.
When you open the popup attach a click listener to body that closes it and removes the listener.
You can use this code
//use by id
document.getElementById(#id).style.display = 'block';
document.getElementById(#id).style.display = 'none';
//use by className
document.getElementById(.className).style.display = 'none';
document.getElementById(.className).style.display = 'block';
or use jQuery
$(document).ready(function(){
$("#id").click(function(event){
// $("#id").toggle();
// $("#id").hide();
// $("#id").show();
});
});
Set id for your layer in HTML part like id="layerPopup"
Then on your JS code create event for your button
$(document).on('click', '#btnId', function(){
$("#layerPopup").hide();
});
You should appear a overlay which will cover the whole body, and give it css property z-index to lower from the button, and when apply click function on it same as my code
HTML
<div class="overlay"></div>
CSS
.overlay{
background-color: transparent;
inset: 0;
position: fixed;
z-index: 100;
display: none;
}
button{
z-index: 101;
}
JQuery
$('button').click(function(){
$('.overlay, popup').toggle();
});
$('.overlay').click(function(){
$('.overlay, popup').hide();
});
One standard way to handle such scenario is to have a backdrop div behind the popup and then add an event listener to it. You may choose to change backdrop's background color to increase pop up aesthetics visibly.
.backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
z-index: 10;
background: rgba(0, 0, 0, 0.75);
}
.modal {
position: fixed;
top: 30vh;
left: 10%;
width: 80%;
z-index: 100;
overflow: hidden;
}
<div class="backdrop" />
<div class="modal" />
And then you can add an event listener on backdrop:
$(document).on('click', '.backdrop', function(){
$(".modal").hide();
});
PS: There may be some syntax issues!
I have a screen
where I want to disable all the events when execution is going on.
When I click on the Execute button, an API is called which probably takes 4-5 minutes to respond. During that time, I don't want the user to click on the calendar cells or Month navigation arrows.
In short, I want to disable all the events on the center screen but not the left menu.
Is it possible to do that?
Yes sure, you can add a class with css pointer-events rule. Set it on the whole table and it will disable all events. Just add it when request starts and remove it when it ends. You can achieve that, by having a boolean variable isLoading in your state, or redux store and based on that add or remove the no-click class.
.no-click {
pointer-events: none;
}
You can use classic loading overlay box over your content when some flag (i.e. loading) is true.
Other way to do it is to use pointer-event: none in CSS. Use same flag to set class to your content block.
Here is a working example in codesanbox:
https://codesandbox.io/s/determined-dirac-fj0lv?file=/src/App.js
Here is code:
export default function App() {
const [loadingState, setLoadingState] = useState(false);
const [pointerEvent, setPointerEvent] = useState(false);
return (
<div className="App">
<div className="sidebar">Sidebar</div>
<div
className={classnames("content", {
"content-pointer-event-none": pointerEvent
})}
>
<button onClick={() => setLoadingState(true)}>
Show loading overlay
</button>
<button onClick={() => setPointerEvent(true)}>
Set pointer event in css
</button>
{loadingState && <div className="loading-overlay"></div>}
</div>
</div>
);
}
.App {
font-family: sans-serif;
text-align: center;
display: flex;
position: absolute;
width: 100%;
height: 100%;
}
.content {
flex: 1;
position: relative;
}
.loading-overlay {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.4);
z-index: 1;
}
.content-pointer-event-none {
pointer-events: none;
}
you see there's a javascript function called alert(); which creates a popup/dialog box is it possible to inspect it because the alert popup's design varies and is different depending on the browser I want it to look like the alert popup of Google Chrome on each and every browser and to do that I want to be able to see the CSS properties and styling of the alert popup and then put it compulsory by using the !important CSS property for all browsers to use that specific alert popup design, but that's just what I think is the way to do it but if there is some other way to do it please let me know about it and how to do it since I don't know if it's even possible.
Thanks in advance.
You cannot manipulate the "Allert" box on the browser. But you can create your own using a modal dialog box.
This example is a very simple preview of a custom modal window that you can change as you wish
function showModal() {
var mod = document.getElementById('myModal');
mod.innerHTML = 'Some text here!';
mod.innerHTML += '<button onclick="hideModal()">Close</button>'
mod.setAttribute('style', 'display: block;');
}
function hideModal() {
var mod = document.getElementById('myModal');
mod.setAttribute('style', 'display: none;');
}
#myModal {
position: absolute;
background: #000;
color: #fff;
border-radius: 10px;
text-align: center;
width: 200px;
height: 100px;
left: calc(50% - 120px);
top: calc(50% - 70px);
padding: 20px;
}
#myModal button {
display: block;
margin: 20px auto;
}
<button id="end-btn" type="button" name="" value="I am done!" onclick="showModal()">Click me</button>
<div id="myModal" style="display: none;">
</div>
I've created a simple modal that is allowed to be closed when you click outside of the content area. This is by design but it has an unintended side-effect. If I click anywhere in the content area (for example in a text field) and drag the mouse to beyond the content area and then release the click it will close the modal. I often have a habit of doing this and I can see how average users will perceive this as a bug so I'm trying to nip it prior to release.
var modal = document.getElementById("modal-container");
function openModal() { modal.classList.add("active"); }
function closeModal() { modal.classList.remove("active"); }
window.onclick = function (event) {
if (event.target == modal)
closeModal();
}
html, body {
margin: 0;
height: 100%;
}
.modal-container.active { top: 0; }
.modal-container {
position: absolute;
top: -500vh;
left: 0;
width: 100%;
height: 100%;
display: grid;
background-color: rgba(0, 0, 0, 0.75);
}
.modal-content {
height: 50%;
width: 50%;
margin: auto;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
<button onclick="openModal();">Open the Modal</button>
<div id="modal-container" class="modal-container">
<div class="modal-content">
<input type="text" />
</div>
</div>
To test it properly:
Click the 'Open the Modal' button.
Click in the text box at the center of the white panel.
Enter some text.
Press the left mouse button down in the text box.
Drag the mouse beyond the bounds of the white panel.
Release the mouse button.
The modal should now be closed.
Is there a way to prevent this without tracking the coordinates of the mouse?
Perhaps onmousedown instead of click?
That worked! Just need more coffee this morning I suppose. Going to write up a thorough answer later today for future readers.
Before you answer yourself with a valid cause (as noted in your Question Edit) -
take in consideration:
onmousedown might not always be the desired UX. (Sometimes experienced users to undo a mousedown not being registered as a click they on purpose move the mouse over another element for the mouseup event just to retain the current state.)
Remove inline JavaScript
Assign listeners using Element.addEventListener() to any button having the data-modal attribute
Use data-modal="#some_modal_id" even no the container element
Finally: use if (evt.target !== this) return;
const el_dataModal = document.querySelectorAll('[data-modal]');
function toggleModal(evt) {
if (evt.target !== this) return; // Do nothing if the element that propagated the event is not the `this` button which has the event attached.
const id = evt.currentTarget.getAttribute('data-modal');
document.querySelector(id).classList.toggle('active');
}
el_dataModal.forEach(el => el.addEventListener('click', toggleModal));
html, body {
margin: 0;
height: 100%;
}
.modal-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: grid;
background-color: rgba(0, 0, 0, 0.75);
opacity: 0; /* ADDED */
transition: 0.26s; /* ADDED */
visibility: hidden; /* ADDED */
}
.modal-container.active {
opacity: 1; /* ADDED */
visibility: visible; /* ADDED */
}
.modal-content {
height: 50%;
width: 50%;
margin: auto;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
<button data-modal="#modal-container">Open the Modal</button>
<div id="modal-container" class="modal-container" data-modal="#modal-container">
<div class="modal-content">
<input type="text">
<br><br>
<button data-modal="#modal-container">CLOSE MODAL TEST</button>
</div>
</div>
This is working example. Think, it matches that one you need))
var clickTarget = null;
var modal = document.getElementById("modal-container");
function openModal() {
modal.classList.add("active");
document.body.addEventListener('mousedown', onModalMouseDown, false);
document.body.addEventListener('mouseup', onModalMouseUp, false);
}
function closeModal() {
modal.classList.remove("active");
document.body.removeEventListener('mousedown', onModalMouseDown);
document.body.removeEventListener('mouseup', onModalMouseUp);
}
function onModalMouseDown(event) {
clickTarget = event.target;
}
function onModalMouseUp() {
if (clickTarget === modal) {
closeModal();
}
}
html, body {
margin: 0;
height: 100%;
}
.modal-container.active { top: 0; }
.modal-container {
position: absolute;
top: -500vh;
left: 0;
width: 100%;
height: 100%;
display: grid;
background-color: rgba(0, 0, 0, 0.75);
}
.modal-content {
height: 50%;
width: 50%;
margin: auto;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.modal-trigger-btn {
margin: 20px;
font-size: 16px;
}
<button onmousedown="openModal();" class="modal-trigger-btn">Open the Modal</button>
<div id="modal-container" class="modal-container">
<div class="modal-content">
<input type="text" placeholder="Start to drag outside..."/>
</div>
</div>
To answer this question myself, I thought about how the onclick event was working. A click is defined as the mouse button being pressed down, and then released. Both of those points have to occur to cause the onclick event to be raised (though you can't really have one without the other happening at some point before or after).
I haven't found any real documentation on the execution path below so it based on logical deduction. If you have any documentation on this please link it in a comment so that I can review it and adjust my answer for future readers.
User presses down the mouse button.
The onmousedown event is raised.
User releases the mouse button.
The onmouseup event is raised.
The onmouseclick event is raised.
I did write a test up to verify these results:
var ePath = document.getElementById("executionPath");
document.body.onmousedown = function (event) { ePath.innerHTML += "On Mouse Down<br>"; }
document.body.onmouseup = function (event) { ePath.innerHTML += "On Mouse Up<br>"; }
document.body.onclick = function (event) { ePath.innerHTML += "On Click<br>"; }
html, body { height: 100%; }
<p id="executionPath">Click the Window<br></p>
I believe the unintended behavior is caused by when the target is set for the onclick event. I think there are three possibilities (below from most to least likely) for when this is set, none of which I can confirm or deny:
The target is set when the mouse button is released.
The target is set when the mouse button is pressed down, then again when the mouse button is released.
The target is set continuously.
After analyzing my thoughts I determined that for my scenario onmousedown is likely to be the best solution. This will ensure that the modal closes only if the user initiates the click outside of the content area. A good way to couple this with onmouseup to ensure a full click is still achieved is demonstrated below. Though in my case I am okay with simply using onmousedown:
var initialTarget = null;
var modal = document.getElementById("modal-container");
function openModal() { modal.classList.add("active"); }
function closeModal() { modal.classList.remove("active"); }
window.onmousedown = function (event) { initialTarget = event.target; }
window.onmouseup = function (event) {
if (event.target == initialTarget)
closeModal();
}
html, body { height: 100%; }
.modal-container.active { top: 0; }
.modal-container {
position: absolute;
top: -500vh;
left: 0;
width: 100%;
height: 100%;
display: grid;
background-color: rgba(0, 0, 0, 0.75);
}
.modal-content {
height: 50%;
width: 50%;
margin: auto;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
<button onclick="openModal();">Open the Modal</button>
<div id="modal-container" class="modal-container">
<div class="modal-content">
<input type="text" />
</div>
</div>
The snippet above ensures that the click starts and ends on the modal container prior to closing the modal. This prevents the modal from closing if the user accidentally initiates a click outside of the content area and drags their mouse into the content area to complete the click. The same is true in the reverse order, and the modal will only close if the click is initiated and completed on the modal container.
The only thing I can't figure out is when the target for onclick is set which is probably more important in a proper explanation on the root cause of this issue so feel free to let me know!
I just need to have a modal close on click off of it. I tried 2 approaches:
Targeting a click event on body and check if the modal has a class and if it does show it
check the event.target and if it's not the modal hide it
Two attempts are below:
$(function(e) {
$("#filter-button").click(function(e) {
$(".dialog").toggleClass("show");
});
$("body").click(function() {
if ($(".dialog").hasClass("show")) {
$(".dialog").removeClass("show");
}
});
});
.dialog {
display: none;
width: 300px;
height: 300px;
background-color: red;
}
.show {
display: block !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="filter-button">SHOW/HIDE</button>
<div class="dialog"></div>
Upon click on "SHOW/HIDE" the modal (a red box) does not even open. I think this might have something to do with #filter-button being counted as a target? As a troubleshooting initiative for the above sample, I attempted to use e.currentTarget https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget so basically changing the above to:
$(function(e) {
console.log(e.currentTarget);
...
I got nothing in the console so I can't tell if that's the issue.
I also tried to log e.target and got no results in the console as well.
Why is that?
My next attempt:
$(function(e) {
$("#filter-button").click(function(e) {
$(".dialog").toggleClass("show");
});
if(e.currentTarget != $("#filter-button")) {
$(".dialog").removeClass("show");
}
});
.dialog {
display: none;
width: 300px;
height: 300px;
background-color: red;
}
.show {
display: block !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="filter-button">SHOW/HIDE</button>
<div class="dialog"></div>
The toggle function is restored, but clicking off of the modal does not close it. I found: Check if event target is hyperlink so I changed my code to:
$(function(e) {
$("#filter-button").click(function(e) {
$(".dialog").toggleClass("show");
});
if(e.target.tagName.toLowerCase() === 'body') {
$(".dialog").removeClass("show");
}
});
This breaks my previous code again, and now my dialog doesn't open at all.
howcome I can't console log e.target?
Why is the modal not opening at all in the first example? Is it because of a logic error with targeting body somehow?
Which is the better way? e.target or attaching a click event to the body?
To simplify things you could wrap your modal dialogue within a container that is full width and height of the viewport. This way you can show or hide the parent container if it is clicked instead of showing and hiding just the dialogue.
This also allows you to add an overlay with css later on to increase the visibility of the modal.
$(function(e) {
var modal = $(".modal-wrapper")
$("#filter-button").click(function(e) {
modal.toggleClass("show");
});
$(window).click(function(e) {
if (e.target == modal[0]) {
modal.removeClass("show");
}
});
});
.modal-wrapper {
display: none;
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
left: 0;
}
.modal {
width: 300px;
height: 300px;
background-color: red;
}
.show {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="filter-button">SHOW/HIDE</button>
<div class="modal-wrapper">
<div class="modal"></div>
</div>