目前我正在尝试使用Bootstrap模态框实现用户确认某些操作,并将这些相同的模态框用于不同的功能。在本问题中,我将使用“计算器”元素作为附加功能的示例。目前情况下,除非我在targetModal.on("hidden.bs.modal", function (e) {});
中添加JQuery的$target.off();
,否则确认将无法生效。
当我添加这段JQuery代码时,它会导致我在此页面上使用的任何其他功能(关于Bootstrap模态框)也出现故障,这意味着我需要为任何这些功能添加额外的代码。我不希望发生这种情况。如何使这些确认正常工作,同时保持其他功能正常工作而不为这些功能添加额外的代码?
应该发生什么:
- 当选择“否”时,“否”按钮(显然是)应保持相同颜色,但相反的按钮(“是”按钮)应变为灰色,并且“确认?”按钮应变为禁用状态(如果尚未)。如果可见,则应隐藏
<button class="btn btn-warning pending">Pending</button>
。 - 当选择“是”时,“是”按钮应保持相同颜色,但相反的按钮(“否”按钮)应变为灰色,并且“确认?”按钮应变为启用状态。
- 单击“确认?”按钮时,应使用与确认有关的任何内容填充模态框并打开给定模态框。
- 在确认期间未完成确认时(退出/关闭模态框),应执行
WillClose()
函数(这将更改给定确认控件的待处理按钮文本为“重试”)。 - 成功执行确认后(通过单击给定模态框内的“确认”按钮),模态框应隐藏/消失(并重置其中的内容)并执行
ConfirmModal()
函数(这将禁用给定确认控件的所有按钮并将待处理按钮文本更改为“已确认”)。 - 任何这些确认都应独立工作。
正在发生的情况:
确认元素触发彼此的待处理按钮,除非我向模态框添加$target.off()
(例如targetModal.off()
)。然而,如果这样做,其他功能将会“出现故障”。我的意思是,无论将要在模态框中“填充”、“插入”或“克隆”什么(你想如何称呼它),它都会被放置在模态框中多次(好像模态框没有重置一样,明白吗?)。
如何使以下内容正常工作?
//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>
我只是希望找到一种尽可能少写代码的解决方案。如果所给出的示例可以重写得更小,请告诉我。
编辑:我知道上面提供的代码每次单击某些控件时都会添加多个事件侦听器,从而引起问题。因此,我正在寻找一种(尽可能简单)的解决方案,以使所有这些控件可以独立工作,同时保持脚本不显眼,并尽量保留上面提供的代码格式。
ele.parentNode.querySelectorAll(pendingSelector)
,这会捕获所有的按钮。你需要有一种方法来排除当前不需要的按钮。 - VLAZthis
)并查找其父容器,然后在该容器内查找挂起按钮。那么为什么上面的代码会从“不同的父容器”获取挂起按钮呢? - Barrosy