Close child modal while keeping parent open only on close button click - javascript

So I'm running into some issues and I can't figure out how to close a child modal and keep the parent modal open, so I wanted to ask the community to see if I can get some assistance.
So here is what I'm attempting to do:
I open click on the 'Launch parent modal', I want the parent modal to show (This works great).
When I launch the child modal from inside the parent, I see the child modal (This works great).
I want to be able to click on the close button inside the child modal and go back to the parent modal since it's still is-active, otherwise if they click anywhere in the shaded are it will close all the modals as it currently is doing.
So to sum it up, I just want the child modal to close so that I can see the parent modal only if the click on the close button.
document.addEventListener('DOMContentLoaded', function() {
const rootEl = document.documentElement;
const $modals = document.querySelectorAll('.modal');
const $modalButtons = document.querySelectorAll('.modal-button');
const $modalCloses = document.querySelectorAll('.modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .button');
if ($modalButtons.length > 0) {
$modalButtons.forEach(function ($el) {
$el.addEventListener('click', function () {
var target = $el.dataset.target;
openModal(target);
});
});
}
if ($modalCloses.length > 0) {
$modalCloses.forEach(function ($el) {
$el.addEventListener('click', function () {
closeModals();
});
});
}
function openModal(target) {
var $target = document.getElementById(target);
rootEl.classList.add('is-clipped');
$target.classList.add('is-active');
}
function closeModals() {
rootEl.classList.remove('is-clipped');
$modals.forEach(function ($el) {
$el.classList.remove('is-active');
});
}
document.addEventListener('keydown', function (event) {
var e = event || window.event;
if (e.keyCode === 27) {
closeModals();
closeDropdowns();
}
})
});
<link href="https://cdn.jsdelivr.net/npm/bulma#0.9.1/css/bulma.min.css" rel="stylesheet"/>
<button class="button is-primary is-large modal-button" data-target="modal-ter" aria-haspopup="true">Launch parent modal</button>
<div id="modal-ter" class="modal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Modal title</p>
<button class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body">
<p>
This is the parent modal (Scroll down and click the child modal button).
</p>
<button class="button is-primary is-large modal-button" data-target="modal-ter2" aria-haspopup="true">Launch child modal</button>
</section>
<footer class="modal-card-foot">
<button class="button is-success">Save changes</button>
<button class="button">Cancel</button>
</footer>
</div>
</div>
<div id="modal-ter2" class="modal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Modal title</p>
<button class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body">
<p>
This is the child modal
</p>
</section>
<footer class="modal-card-foot">
<button class="button is-success">Save changes</button>
<button class="button">Cancel</button>
</footer>
</div>
</div>

I think you should bind different events for success and cancel butttons, cause they have different logics.
document.addEventListener('DOMContentLoaded', function() {
const rootEl = document.documentElement;
const $modals = document.querySelectorAll('.modal');
const $modalButtons = document.querySelectorAll('.modal-button');
const $modalCloses = document.querySelectorAll('.modal-background, .modal-close, .modal-card-head .delete');
if ($modalButtons.length > 0) {
$modalButtons.forEach(function ($el) {
$el.addEventListener('click', function () {
var target = $el.dataset.target;
openModal(target);
});
});
}
if ($modalCloses.length > 0) {
$modalCloses.forEach(function ($el) {
$el.addEventListener('click', function () {
closeModals();
});
});
}
function openModal(target) {
var $target = document.getElementById(target);
rootEl.classList.add('is-clipped');
$target.classList.add('is-active');
}
function closeModals() {
rootEl.classList.remove('is-clipped');
$modals.forEach(function ($el) {
$el.classList.remove('is-active');
});
}
$modals.forEach(function ($el) {
$el.addEventListener('click', function(event) {
if (event.target.classList.contains('cancel')) {
$el.classList.remove('is-active');
}
})
});
document.addEventListener('keydown', function (event) {
var e = event || window.event;
if (e.keyCode === 27) {
closeModals();
closeDropdowns();
}
})
});
<link href="https://cdn.jsdelivr.net/npm/bulma#0.9.1/css/bulma.min.css" rel="stylesheet"/>
<button class="button is-primary is-large modal-button" data-target="modal-ter" aria-haspopup="true">Launch parent modal</button>
<div id="modal-ter" class="modal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Modal title</p>
<button class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body">
<p>
This is the parent modal (Scroll down and click the child modal button).
</p>
<button class="button is-primary is-large modal-button" data-target="modal-ter2" aria-haspopup="true">Launch child modal</button>
</section>
<footer class="modal-card-foot">
<button class="button is-success">Save changes</button>
<button class="button cancel">Cancel</button>
</footer>
</div>
</div>
<div id="modal-ter2" class="modal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Modal title</p>
<button class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body">
<p>
This is the child modal
</p>
</section>
<footer class="modal-card-foot">
<button class="button is-success">Save changes</button>
<button class="button cancel">Cancel</button>
</footer>
</div>
</div>

Related

How can I create a second working model for another button?

I am currently creating a page where you can click on images, and then a modal should appear. Now the problem: the second modal does not appear.
I've tried many things but nothing worked
const modal = document.querySelector('.modal');
const openModal = document.querySelector('.open-button');
const closeModal = document.querySelector('.close-button');
openModal.addEventListener('click', () => {
modal.showModal();
})
closeModal.addEventListener('click', () => {
modal.close();
})
<button class="open-button">
<img src="https://via.placeholder.com/100.jpg" alt="">
</button>
<dialog class="modal">
<button class="close-button">close it</button>
</dialog>
<!-- second modal -->
<button class="open-button">
<img src="https://via.placeholder.com/100.jpg" alt="">
</button>
<dialog class="modal">
<button class="close-button">close it</button>
</dialog>

How to get Javascript click event, for modal popup activate, before allowing user to upload a picture attachment

I wish to have the user prompted by modal before uploading picture, how can I prevent the input from being activated at same time as my modal and instead have the input triggered upon closing my displayed modal?
const modal = document.querySelector('.modal');
const openModal = document.querySelector('.open-button');
const closeModal = document.querySelector('.close-button');
openModal.addEventListener('click', () => {
modal.showModal();
})
closeModal.addEventListener('click', () => {
modal.close();
})
<input class="open-button" type="file"
accept="image/png, image/jpeg" >
<button>Send</button>
</form>
</div>
</div>
</div>
</div>
<dialog class="modal" id="modal">
<h2>Hello</h2>
<p>This is a modal</p>
<button class="button close-button">Proceed</button>
</dialog>
<footer>
const modal = document.querySelector('.modal');
const openModal = document.querySelector('.open-button');
const closeModal = document.querySelector('.close-button');
closeModal.addEventListener('click', () => {
modal.close();
document.getElementById("input-browseFile").click();
})
function onclick_browseFile() {
modal.showModal()
}
<button onclick="onclick_browseFile()">Browse</button>
<input id="input-browseFile" class="open-button" type="file"
accept="image/png, image/jpeg" hidden>
<button>Send</button>
</form>
</div>
</div>
</div>
</div>
<dialog class="modal" id="modal">
<h2>Hello</h2>
<p>This is a modal</p>
<button class="button close-button">Proceed</button>
</dialog>
<footer>

Bootstap 5 modal is not working as expected?

I've run into a strange to me issue that I'm not sure how to solve correctly. I'm using Bootstap 5. Let me provide a small example:
const reportBtns = Array.from(document.querySelectorAll('.report'))
reportBtns.forEach((btn) => {
btn.addEventListener('click', () => {
const myModal = new bootstrap.Modal(document.getElementById('testModal'))
myModal.show()
const modalBtn = document.querySelector('.btn-submit')
modalBtn.addEventListener('click', (e) => {
console.log('clicked')
})
})
})
.report:hover {
cursor: pointer;
}
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.1/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<ul>
<li class="report">
<span>Report</span>
</li>
<li class="report">
<span>Report</span>
</li>
<li class="report">
<span>Report</span>
</li>
</ul>
<div class="modal" tabindex="-1" id="testModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-submit">Submit</button>
</div>
</div>
</div>
</div>
So, when I click Submit for the first time in my console I see clicked logged one time, however, when I close the modal, open it again and click Submit for the second time, I see clicked logged three times instead of two. Next time it will be 6 clicked and so on. I kind of understand that the reference to old modals is kept for some reason, and when I click Submit on a new modal previous modals are submitting too.
It doesn't make any sense to me why it is happening. I tried using modal.dispose() when modal was closed , but it didn't work, and I'm not even sure if I was using it correctly or it's been used for this purpose.
reportBtns.forEach((btn) => {
btn.addEventListener('click', async () => {
const myModal = new bootstrap.Modal(document.getElementById('testModal'))
myModal.show()
const modalRef = document.querySelector('#testModal')
modalRef.addEventListener('hidden.bs.modal', function () {
const modal = bootstrap.Modal.getInstance(modalRef)
if (modal) {
modal.dispose()
}
})
const modalBtn = document.querySelector('.btn-submit')
modalBtn.addEventListener('click', (e) => {
console.log('clicked')
})
})
})
I'd appreciate your help.
You add an event listener everytime the 'Report' button is clicked, yet you forgot to remove the event listener after the modal closes. The number of event listeners "stack up," and eventually multiple event listeners are fired when the submit button is clicked, causing it to log it multiple times.
I recommend adding an event listener to all buttons using querySelectorAll and a for loop once.
const reportBtns = Array.from(document.querySelectorAll('.report'))
reportBtns.forEach((btn) => {
btn.addEventListener('click', () => {
const myModal = new bootstrap.Modal(document.getElementById('testModal'))
myModal.show()
})
})
const modalBtns = document.querySelectorAll('.btn-submit')
for (let i = 0; i < modalBtns.length; i++) {
modalBtns[i].addEventListener('click', (e) => {
console.log('clicked')
})
}
.report:hover {
cursor: pointer;
}
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.1/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" />
<ul>
<li class="report">
<span>Report</span>
</li>
<li class="report">
<span>Report</span>
</li>
<li class="report">
<span>Report</span>
</li>
</ul>
<div class="modal" tabindex="-1" id="testModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-submit">Submit</button>
</div>
</div>
</div>
</div>

Display none if child and parent element have the same class

I have these menus around my horizontal scrolling site.
<section class="fun-stuff portfolio-cards" id="slide-1">
<div class="nav-list">
<button class="about">ABOUT</button>
<button class="fun-stuff">FUN STUFF</button>
<button class="home">HOME</button>
<button class="professional">PROFESSIONAL</button>
<button class="contact">CONTACT</button>
</div>
</section
The goal is to filter them based if the button classes have the same class as the ones in the section. If they have the same class (in this example 'fun-stuff') then that button will display:none.
My jQuery. I feel I'm close or totally over-complicating it.
jQuery(document).ready(function($) {
var theList = $('.nav-list button');
var i;
for (i=0; i < theList.length; i++){
var theButtons = '"' + theList[i].className + '"';
var parentClassName = theList[i].closest('section').className;
// tried this one. Was close i believe but no dice
if(theButtons = theList[i].closest('section').hasClass('"' + theButtons + '"') ){
// display that button as none
}
// tried this as well and for each button got 'no' in the console
if( $(theList[i].closest('section') ).hasClass(theButtons) ){
console.log('yes');
} else {
console.log ('no');
}
}
});
Yes you overdid it somewhat
$(function() {
$("button").each(function() {
var $parent = $(this).closest("section"), // the section wrapping the button
same = $parent.hasClass($(this).attr("class")); // parent has this button's class too
$(this).toggle(!same); // hide if the same (show if not)
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="fun-stuff portfolio-cards" id="slide-1">
<div class="nav-list">
<button class="about">ABOUT</button>
<button class="fun-stuff">FUN STUFF</button>
<button class="home">HOME</button>
<button class="professional">PROFESSIONAL</button>
<button class="contact">CONTACT</button>
</div>
</section>
If you have more than one class on the button and you want to hide if one of them matches
$(function() {
$("button").each(function() {
var bList = this.classList,
parentList = $(this).closest("section")[0].classList, // the DOM element's classList
same = [...parentList] // convert to iterable
.filter(ele => bList.contains(ele)) // look up parent class in button classList
.length>0; // found?
$(this).toggle(!same);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="fun-stuff portfolio-cards" id="slide-1">
<div class="nav-list">
<button class="about">ABOUT</button>
<button class="fun-stuff bla">FUN STUFF</button>
<button class="home">HOME</button>
<button class="professional">PROFESSIONAL</button>
<button class="contact">CONTACT</button>
</div>
</section>
You can turn the section classes into a regular array, then filter and hide the buttons.
jQuery(document).ready(function($) {
var classes = $('#slide-1').attr("class").split(/\s+/);
classes.forEach(function (cls) {
$('.nav-list button').filter('.' + cls).hide()
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="fun-stuff portfolio-cards" id="slide-1">
<div class="nav-list">
<button class="about">ABOUT</button>
<button class="fun-stuff">FUN STUFF</button>
<button class="home">HOME</button>
<button class="professional">PROFESSIONAL</button>
<button class="contact">CONTACT</button>
</div>
</section>
You can do this pretty easily with vanilla Javascript:
const classNames = document.getElementById("slide-1").classList;
document.querySelectorAll(".nav-list > button").forEach((el) => {
if(classNames.contains(el.classList)){
el.style.display = "none";
}
});
<section class="fun-stuff portfolio-cards" id="slide-1">
<div class="nav-list">
<button class="about">ABOUT</button>
<button class="fun-stuff">FUN STUFF</button>
<button class="home">HOME</button>
<button class="professional">PROFESSIONAL</button>
<button class="contact">CONTACT</button>
</div>
</section>
Edit: Done with two lines of code:
const classList = document.getElementById("slide-1").classList;
document.querySelectorAll(".nav-list > button").forEach(el => el.style.display = classList.contains(el.classList) ? "none": "initial");
<section class="fun-stuff portfolio-cards" id="slide-1">
<div class="nav-list">
<button class="about">ABOUT</button>
<button class="fun-stuff">FUN STUFF</button>
<button class="home">HOME</button>
<button class="professional">PROFESSIONAL</button>
<button class="contact">CONTACT</button>
</div>
</section>

Reset bootstrap modal

So, I am using bootstrap's modal.
I want to make a wizard style modal and came up with the following solution:
div snippets:
<div class="modal-instance hide fade" id="step1">
<div id="stepa">
...
</div>
</div>
<div id="stepb" style="display: none;">
...
</div>
Upon pressing a button in step-a step-b is loaded.
javascript snippet:
$("#stepa").replaceWith($('#stepb'));
document.getElementById('stepb').style.display = 'block';
This works ok.
But when I dismiss the modal. The div stepa has still been replaced by stepb. My solution was to build a replacement back to stepa when the modal is hidden:
$("#myModal").on("hidden", function() {
//replace the child
});
I tried:
$('#step1').children().remove();
$('#step1').append($('#stepa'));
and
$("#step1").children("div:first").replaceWith($('#stepa'));
But I am having a hardtime selecting step-a as a replacement div, probably due to it not being a separate div. My question is, is this the right approach for a wizard styled modal or should I take another approach?
It's much simpler just to hide the previous and next steps, instead of copying them.
<button type="button" data-toggle="modal" data-target="#myModal">Launch modal</button>
<div id="myModal" class="modal hide fade" data-step="1">
<div class="step step1">
<h1>Step 1</h1>
<button class="btn next">Next</button>
</div>
<div class="step step2">
<h1>Step 2</h1>
<button class="btn previous">Previous</button>
<button class="btn next">Next</button>
</div>
<div class="step step3">
<h1>Step 3</h1>
<button class="btn previous">Previous</button>
<button class="btn done" data-dismiss="modal">Done</button>
</div>
</div>
<style type="text/css">
#myModal > .step { display: none; }​
</style>
<script type="text/javascript">
$(function() {
showStep(parseInt($('#myModal').data('step')) || 1);
$('#myModal .next').on('click', function () {
showStep(parseInt($('#myModal').data('step')) + 1);
});
$('#myModal .previous').on('click', function () {
showStep(parseInt($('#myModal').data('step')) - 1);
});
$('#myModal').on('hidden', function() {
showStep(1);
});
function showStep(step) {
$('#myModal').data('step', step);
$('#myModal > .step').hide();
$('#myModal > .step' + step).show();
}
});​
</script>
http://jsfiddle.net/7kg7z/5/

Categories

Resources