Currently I am trying to make it possible for a user to confirm something by opening Bootstrap modals as well as using these same modals for different features. In this question I will be using a "calculator" sort of element as an example for an additional feature. As it stands now, the confirmations won't work unless I add JQuery's $target.off(); to targetModal.on("hidden.bs.modal", function (e) {});.
When I add this piece of JQuery code, it will cause any other features I use (regarding Bootstrap modals) on this page to break as well, meaning I would require to add additional code for any of those features. I would not desire for that to happen. How could I make these confirmations work properly whilst keeping other features work fine without adding additional code for these features?
What should happen:
- When "No" is selected, the button (for "no" obviously) should stay the same color, however the opposite button (the "yes" button) should turn grey-ish and the "Confirm?" button should turn disabled (if not already). Also if a
<button class="btn btn-warning pending">Pending</button>is visible, this should be hidden. - When "Yes" is selected, the button (for "yes") should stay the same color, however the opposite button (the "no" button) should turn grey-ish and the "Confirm?" button should turn enabled.
- When the "Confirm?" button is clicked, it should populate a modal with anything related to a confirmation and open up given modal.
- When the modal has been exited out/closed during the confirmation without completing the confirmation, function
WillClose()(which in turn should change the pending button text of given confirmation control to "try again") should be executed. - When a confirmation has successfuly been executed (by clicking a "confirm" button inside given modal), the modal should hide/disappear (and reset the content inside) and execute
ConfirmModal()function (which in turn should disable all buttons for given confirmation control and change the pending button text to "Confirmed"). - Any of these confirmations should work independently from one another.
What is happening:
The confirmation elements trigger one another's pending buttons, unless I add $target.off() to the modal (e.g. targetModal.off()). Doing the latter however, will cause other features to "break". What I mean by this, is that whatever will be "populated", "inserted" or "cloned" (whatever you want to call this) inside a modal, it will be placed inside the modal multiple times (as if the modal does not reset, makes sense?).
How could I make the following work?
//Fields:
//Yes selector
const positiveSelector = ".positive";
//No selector
const negativeSelector = ".negative";
//Confirm? selector
const confirmSelector = ".init-confirm";
//Pending selector
const pendingSelector = ".pending";
//calTrigger selector
const calcTriggerSelector = ".calc-trigger > button";
//Yes elements
const positiveNodes = document.querySelectorAll(positiveSelector);
//No elements
const negativeNodes = document.querySelectorAll(negativeSelector);
//Confirm? elements
const confirmNodes = document.querySelectorAll(confirmSelector);
//Pending elements
const pendingNodes = document.querySelectorAll(pendingSelector);
//calcTrigger elements
const calcTriggerNodes = document.querySelectorAll(calcTriggerSelector);
//Modal
const targetModalSelector = "#bs-modal-xl";
const targetModal = $(targetModalSelector);
const $modalInit = targetModal.html();
//Eventlisteners:
positiveNodes.forEach(node => node.addEventListener("click", function () {
EnableConfirmBtn(this);
}));
negativeNodes.forEach(node => node.addEventListener("click", function () {
DisableConfirmBtn(this);
}));
confirmNodes.forEach(node => node.addEventListener("click", function () {
OpenConfirmModal(this);
}));
calcTriggerNodes.forEach(node => node.addEventListener("click", calcTrigger));
//Reset modal when closing
targetModal.on("hidden.bs.modal", function () {
targetModal.html($modalInit);
});
//Methods:
function EnableConfirmBtn(ele) {
ele.classList.add("btn-success");
ele.parentNode.querySelectorAll(negativeSelector).forEach(node => node.classList.remove("btn-warning"));
ele.parentNode.parentNode.querySelectorAll(confirmSelector).forEach(node => node.removeAttribute("disabled"));
}
function DisableConfirmBtn(ele) {
ele.classList.add("btn-warning");
ele.parentNode.querySelectorAll(positiveSelector).forEach(node => node.classList.remove("btn-success"));
ele.parentNode.parentNode.querySelectorAll(confirmSelector).forEach(node => node.setAttribute("disabled", ""));
}
function OpenConfirmModal(ele) {
ele.parentNode.querySelectorAll(pendingSelector).forEach(node => node.style.display = "inline-block");
ele.parentNode.querySelectorAll(pendingSelector).forEach(node => node.classList.remove("btn-warning"));
ele.parentNode.querySelectorAll(pendingSelector).forEach(node => node.classList.add("btn-danger"));
ele.parentNode.querySelectorAll(pendingSelector).forEach(node => node.textContent = "Pending");
$(targetModalSelector + " .modal-body").html($(".clone-one").clone());
$(targetModalSelector + " .clone-one").show();
$(targetModalSelector + " h4.modal-title").text("");
$(targetModalSelector + " .modal-content .modal-footer").html("");
targetModal.modal();
targetModal.on("click", ".clone-one", function () {
targetModal.modal("hide");
ConfirmModal(ele);
});
targetModal.on("hidden.bs.modal", function (e) {
WillClose(ele);
//Make use of targetModal.off(); here? <--
//targetModal.off();
targetModal.html($modalInit);
});
}
function ConfirmModal(ele) {
ele.parentNode.querySelectorAll(pendingSelector).forEach(node => node.textContent = "Confirmed");
ele.parentNode.querySelectorAll(pendingSelector).forEach(node => node.classList.remove("btn-danger", "btn-warning"));
ele.parentNode.querySelectorAll(pendingSelector).forEach(node => node.classList.add("btn-success"));
ele.parentNode.querySelectorAll(confirmSelector).forEach(node => node.style.display = "none");
ele.parentNode.querySelectorAll(".btn-group > button").forEach(node => node.setAttribute("disabled", ""));
}
function WillClose(ele) {
ele.parentNode.querySelectorAll(pendingSelector).forEach(node => node.textContent = "Try again");
ele.parentNode.querySelectorAll(pendingSelector).forEach(node => node.classList.remove("btn-danger"));
ele.parentNode.querySelectorAll(pendingSelector).forEach(node => node.classList.add("btn-warning"));
}
function calcTrigger() {
ModalHandler($(".calc").clone(), "", "", true, true);
$(targetModalSelector + " .calc").show();
targetModal.modal();
document.querySelectorAll(targetModalSelector + " .calc #number-one-btn").forEach(node => node.addEventListener("click", function () {
document.querySelectorAll(targetModalSelector + " .calc .result-container input").forEach(node => node.value += "1");
}));
document.querySelectorAll(targetModalSelector + " .calc #number-two-btn").forEach(node => node.addEventListener("click", function () {
document.querySelectorAll(targetModalSelector + " .calc .result-container input").forEach(node => node.value += "2");
}));
document.querySelectorAll(targetModalSelector + " .calc #number-three-btn").forEach(node => node.addEventListener("click", function () {
document.querySelectorAll(targetModalSelector + " .calc .result-container input").forEach(node => node.value += "3");
}));
//I would not want to be using something like this:
//Reset modal when closing
//targetModal.on("hidden.bs.modal", function () {
//targetModal.off();
//targetModal.html($modalInit);
//});
}
//Modal handling (not required when not using Modal):
function ModalHandler(content, title, footer = "", bigCloseBtn = false, emptyFooter = false) {
$(targetModalSelector + " h4.modal-title").text(title);
$(targetModalSelector + " .modal-body").html(content);
if (footer != "" && footer != undefined) {
$(targetModalSelector + " .modal-footer").html(footer);
}
if (bigCloseBtn) {
$(targetModalSelector + " .modal-content .modal-header button.close").css("float", "right");
$(targetModalSelector + " .modal-content .modal-header button.close").addClass("btn btn-lg btn-danger");
//$(".modal .modal-content .modal-header button.close").html("close");
$(targetModalSelector + " .modal-content .modal-header button.close").removeClass("close");
}
if (emptyFooter) {
$(targetModalSelector + " .modal-content .modal-footer").html("");
}
}
#foo-container {
padding: 5px;
}
.pending {
display: none;
}
.clone-one, .calc {
display: none;
}
.calc {
width: 100%;
}
.calc button, .calc .result-container {
margin-top: 3px;
margin-bottom: 3px;
}
.calc [class*="col-"] {
padding-left: 3px;
padding-right: 3px;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<div class="modal" id="bs-modal-xl" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">
<p>One fine body…</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
<div id="calc" class="calc">
<form>
<div class="row">
<div class="col-xs-9">
<div class="result-container">
<input type="text" class="form-control" disabled>
</div>
</div>
<div class="col-xs-3">
<button type="button" class="btn btn-default btn-block" value="x" id="">
<span class="glyphicon glyphicon-remove"></span>
</button>
</div>
</div>
<div class="row">
<div class="col-xs-3">
<button type="button" class="btn btn-default btn-block" value="1" id="number-one-btn">
1
</button>
</div>
<div class="col-xs-3">
<button type="button" class="btn btn-default btn-block" value="2" id="number-two-btn">
2
</button>
</div>
<div class="col-xs-3">
<button type="button" class="btn btn-default btn-block" value="3" id="number-three-btn">
3
</button>
</div>
<div class="col-xs-3">
<button type="button" class="btn btn-default btn-block" value="" id="number-one-btn">
<span class="glyphicon glyphicon-arrow-left"></span>
</button>
</div>
</div>
</form>
</div>
<div class="container">
<div id="foo-container">
<div class="confirmation-box">
<div class="btn-group btn-group-lg" role="group" aria-label="...">
<button type="button" class="btn btn-success positive">Yes</button>
<button type="button" class="btn btn-warning negative">No</button>
</div>
<button type="button" class="btn btn-lg btn-danger init-confirm" disabled>Confirm?</button>
<button type="button" class="btn btn-lg btn-danger pending" disabled>Pending</button>
</div>
<br />
<div class="confirmation-box">
<div class="btn-group btn-group-lg" role="group" aria-label="...">
<button type="button" class="btn btn-success positive">Yes</button>
<button type="button" class="btn btn-warning negative">No</button>
</div>
<button type="button" class="btn btn-lg btn-danger init-confirm" disabled>Confirm?</button>
<button type="button" class="btn btn-lg btn-danger pending" disabled>Pending</button>
</div>
<br />
<div class="calc-trigger">
<button class="btn btn-lg btn-default">
Calc trigger
</button>
</div>
</div>
<div class="clone-one">
<button type="button" class="btn btn-lg btn-success">Clicky</button>
</div>
</div>
I am merely looking for a solution that requires minimal code as possible. If given example can be rewritten to be smaller, please let me know.
Edit: I am aware that the code provided above adds multiple event listeners the more I click on certain controls causing problems. So that is why I am looking for a (simple as possible) solution to make all these controls work independently whilst keeping the script unobtrusive as well as to keep as much of the formatting of the code provided above.
from How could I make a reusable confirmation control using Bootstrap Modals and buttons?
No comments:
Post a Comment