为什么引导模式关闭事件“堆栈”?

时间:2019-05-17 07:24:31

标签: javascript jquery html bootstrap-modal

我试图制作一个可以多次添加到页面的确认控件,但是我偶然发现了导致这些控件彼此“接触”的错误(显然,我想让它们独立工作)。

我为此使用了Bootstrap,并且使用了尽可能少的jQuery(由于调试)。

经过切片后,我发现在我的代码中,Bootstrap关闭功能将运行多次,从而导致此问题。会有人知道为什么这个Bootstrap函数会多次运行吗?

这可能是我的问题的原因

$("").on("hidden.bs.modal", function (e) {
  //Why does this stack?
  alert("Why does this stack?");
});

这是我代码的上下文,它将在其中多次运行:

//Yes selector
const positiveSelector = ".positive";
//No selector
const negativeSelector = ".negative";
//Confirm? selector
const confirmSelector = ".init-confirm"
//Pending selector
const pendingSelector = ".pending";
//Yes ele
const positiveNodes = document.querySelectorAll(positiveSelector);
//No ele
const negativeNodes = document.querySelectorAll(negativeSelector);
//Confirm? ele
const confirmNodes = document.querySelectorAll(confirmSelector);
//Pending ele
const pendingNodes = document.querySelectorAll(pendingSelector);

//Modal related:
const $modalInit = $(".modal").html();
const targetModal = $("#bs-modal-xl");

positiveNodes.forEach(node => node.addEventListener("click", function () {
  let thisNode = this;
  thisNode.classList.add("btn-success");
  thisNode.parentNode.querySelectorAll(negativeSelector).forEach(node => node.classList.remove("btn-warning"));
  thisNode.parentNode.parentNode.querySelectorAll(confirmSelector).forEach(node => node.removeAttribute("disabled"));
}));

negativeNodes.forEach(node => node.addEventListener("click", function () {
  let thisNode = this;
  thisNode.classList.add("btn-warning");
  thisNode.parentNode.querySelectorAll(positiveSelector).forEach(node => node.classList.remove("btn-success"));
  thisNode.parentNode.parentNode.querySelectorAll(confirmSelector).forEach(node => node.setAttribute("disabled", ""));
  thisNode.parentNode.parentNode.querySelectorAll(pendingSelector).forEach(node => node.style.display = "none");
}));

confirmNodes.forEach(node => node.addEventListener("click", function () {
  let thisNode = this;

  thisNode.parentNode.querySelectorAll(pendingSelector).forEach(node => node.style.display = "inline-block");
  thisNode.parentNode.querySelectorAll(pendingSelector).forEach(node => node.classList.remove("btn-warning"));
  thisNode.parentNode.querySelectorAll(pendingSelector).forEach(node => node.classList.add("btn-danger"));
  thisNode.parentNode.querySelectorAll(pendingSelector).forEach(node => node.textContent = "Pending");

  //Populate modal:
  ModalHandler($(".my-ele").clone(), "My ele", undefined, true, true);
  $(".modal .my-ele").show();
  //Show Modal:
  targetModal.modal();

  $(".modal").on("click", ".my-ele button", function () {
    targetModal.modal("hide");
  });

  //When closing Modal:
  targetModal.on("hidden.bs.modal", function (e) {
    //Why does this stack?
    alert("Why does this stack?");
  });
}));


//Modal handling (not required when not using Modal):
function ModalHandler(content, title, footer = "", bigCloseBtn = false, emptyFooter = false) {
  $(".modal h4.modal-title").text(title);
  $(".modal .modal-body").html(content);

  if (footer != "" && footer != undefined) {
    $(".modal .modal-footer").html(footer);
  }

  if (bigCloseBtn) {
    $(".modal .modal-content .modal-header button.close").css("float", "right");
    $(".modal .modal-content .modal-header button.close").addClass("btn btn-lg btn-danger");
    //$(".modal .modal-content .modal-header button.close").html("close");
    $(".modal .modal-content .modal-header button.close").removeClass("close");
  }

  if (emptyFooter) {
    $(".modal .modal-content .modal-footer").html("");
  }
}

$(".modal").on("hidden.bs.modal", function () {
  $(".modal").html($modalInit);
});
#foo-container {
  padding: 5px;
}

.pending {
  display: none;
}

.my-ele {
  display: none;
}
<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">&times;</span></button>
        <h4 class="modal-title">Modal title</h4>
      </div>
      <div class="modal-body">
        <p>One fine body&hellip;</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="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>
</div>

<div class="my-ele">
  <button type="button" class="btn btn-lg btn-success">Clicky</button>
</div>

JSFiddle

为了重现我的问题,必须通过单击“是”按钮以启用“确认?”来打开模式。按钮,然后单击此按钮。然后退出模态。该事件将运行一次,但是,当重复打开和关闭模式时,警报将开始堆叠(由于事件由于某种原因而开始堆叠,我不理解)。

我有

的原因

.on("hidden.bs.modal", function (e) {});eventListener

<button type="button" class="btn btn-lg btn-danger init-confirm" disabled>Confirm?</button>

是因为我需要将clicked元素作为参数传递给外部函数。

我可以使模式关闭函数仅运行一次而不是“将它们堆叠”吗?

1 个答案:

答案 0 :(得分:1)

这是因为该事件一次又一次地应用于targetModal.on。您需要将其转到off

targetModal.off();

//Yes selector
const positiveSelector = ".positive";
//No selector
const negativeSelector = ".negative";
//Confirm? selector
const confirmSelector = ".init-confirm"
//Pending selector
const pendingSelector = ".pending";
//Yes ele
const positiveNodes = document.querySelectorAll(positiveSelector);
//No ele
const negativeNodes = document.querySelectorAll(negativeSelector);
//Confirm? ele
const confirmNodes = document.querySelectorAll(confirmSelector);
//Pending ele
const pendingNodes = document.querySelectorAll(pendingSelector);

//Modal related:
const $modalInit = $(".modal").html();
const targetModal = $("#bs-modal-xl");

positiveNodes.forEach(node => node.addEventListener("click", function() {
  let thisNode = this;
  thisNode.classList.add("btn-success");
  thisNode.parentNode.querySelectorAll(negativeSelector).forEach(node => node.classList.remove("btn-warning"));
  thisNode.parentNode.parentNode.querySelectorAll(confirmSelector).forEach(node => node.removeAttribute("disabled"));
}));

negativeNodes.forEach(node => node.addEventListener("click", function() {
  let thisNode = this;
  thisNode.classList.add("btn-warning");
  thisNode.parentNode.querySelectorAll(positiveSelector).forEach(node => node.classList.remove("btn-success"));
  thisNode.parentNode.parentNode.querySelectorAll(confirmSelector).forEach(node => node.setAttribute("disabled", ""));
  thisNode.parentNode.parentNode.querySelectorAll(pendingSelector).forEach(node => node.style.display = "none");
}));

confirmNodes.forEach(node => node.addEventListener("click", function() {
  let thisNode = this;

  thisNode.parentNode.querySelectorAll(pendingSelector).forEach(node => node.style.display = "inline-block");
  thisNode.parentNode.querySelectorAll(pendingSelector).forEach(node => node.classList.remove("btn-warning"));
  thisNode.parentNode.querySelectorAll(pendingSelector).forEach(node => node.classList.add("btn-danger"));
  thisNode.parentNode.querySelectorAll(pendingSelector).forEach(node => node.textContent = "Pending");

  //Populate modal:
  ModalHandler($(".my-ele").clone(), "My ele", undefined, true, true);
  $(".modal .my-ele").show();
  //Show Modal:
  targetModal.modal();

  $(".modal").on("click", ".my-ele button", function() {
    targetModal.modal("hide");
  });

  //When closing Modal:
  targetModal.on("hidden.bs.modal", function(e) {
    //Why does this stack?
    alert("Why does this stack?");
    targetModal.off();
  });
}));


//Modal handling (not required when not using Modal):
function ModalHandler(content, title, footer = "", bigCloseBtn = false, emptyFooter = false) {
  $(".modal h4.modal-title").text(title);
  $(".modal .modal-body").html(content);

  if (footer != "" && footer != undefined) {
    $(".modal .modal-footer").html(footer);
  }

  if (bigCloseBtn) {
    $(".modal .modal-content .modal-header button.close").css("float", "right");
    $(".modal .modal-content .modal-header button.close").addClass("btn btn-lg btn-danger");
    //$(".modal .modal-content .modal-header button.close").html("close");
    $(".modal .modal-content .modal-header button.close").removeClass("close");
  }

  if (emptyFooter) {
    $(".modal .modal-content .modal-footer").html("");
  }
}

$(".modal").on("hidden.bs.modal", function() {
  $(".modal").html($modalInit);
});
#foo-container {
  padding: 5px;
}

.pending {
  display: none;
}

.my-ele {
  display: none;
}
<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">&times;</span></button>
        <h4 class="modal-title">Modal title</h4>
      </div>
      <div class="modal-body">
        <p>One fine body&hellip;</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="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>
</div>

<div class="my-ele">
  <button type="button" class="btn btn-lg btn-success">Clicky</button>
</div>