【问题标题】:Bootstrap modal inside code is multiplying every time a new modal opens每次打开新模式时,代码中的引导模式都会成倍增加
【发布时间】:2020-10-25 00:37:10
【问题描述】:

你好,所以我为我构建的聊天创建了一个房间密码验证,并使用 Bootstrap 的 4 模式向用户显示密码输入。

我正在使用 jQuery 来打开模式,就像 $("#password-validation").modal(); 一样。它工作得很好。但是当你关闭模态框,重新打开它并提交输入后,“如果用户点击回车按钮”里面的代码会执行多次而不是on。

// #room-password-btn is the enter button
$("#room-password-btn").click(function(){ 
  console.log("Execute")
}); 

一个例子:所以如果你(打开模式按钮并关闭模式)* 3 然后点击你会在控制台中看到的输入按钮:

// first open and close
// second open and close
Execute // third open, enter
Execute
Execute

问题的工作模型:https://codepen.io/PachUp/pen/LYGQozz(尝试关闭它并按回车键,您会看到多条消息而不是一条,关闭的次数越多,您会看到的消息越多。我相信如果我修复那么问题就解决了)。

模态:

<div class="modal fade" id="password-validation" tabindex="-1" role="dialog" aria-labelledby="room-password-val-aria" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">Enter the room password</h4>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <label data-error="wrong" data-success="right">Room password</label>
          <input class="form-control" id="room-password-val">
        </div>
      </div>
      <div class="modal-footer d-flex justify-content-center">
        <button class="btn btn-default" id="room-password-btn">Enter</button>
      </div>
    </div>
  </div>
</div>

完整的 JS:

    corrent_pass = false;
    room_pass = ""
    $.ajax({
        type: "POST",
        url: "/validate",
        data: new_room,
        success: function(pass){
            room_pass = pass; // pass is the room password
        }
    });
    
    console.log("room pass: " + room_pass)
    console.log(room_pass.length)
    if(room_pass != "False"){ 
        $("#password-validation").modal('toggle');
        $('#password-validation').on('shown.bs.modal', function () {
            $('#room-password-val').trigger('focus');
            $("#room-password-btn").mousedown(function(){
                console.log("Execute")
                user_password = $("#room-password-val").val()
                console.log(user_password)
                console.log(room_pass)
                if(user_password == room_pass){
                    corrent_pass = false
                    $("#messages").html("");
                    $("#password-validation .close").click()
                    toastr["success"]("Entering you into the room!", "Corrent password")
                    console.log("Joining a room")
                    socket.emit('leave', {"room" : room})
                    socket.emit("join", {"room" : new_room})
                    room = new_room
                }
                else{
                    toastr["error"]("Sorry, you have entered the wrong password. Try again.", "Wrong password")
                }
            });
        });
    }
    else{
        console.log("no password")
        $("#messages").html("");
        console.log("Joining a room")
        socket.emit('leave', {"room" : room})
        socket.emit("join", {"room" : new_room})
        var room_msg = "You have joined " + new_room
        toastr["success"](room_msg, "Room joined")
        room = new_room
    }
}

【问题讨论】:

  • 您能分享一下您目前工作的代码吗?
  • @ShahnawazHossan 我做到了。我不想分享它,因为我认为 js 代码的整个部分(与问题相关)是相关的,但如果它对你有帮助

标签: javascript jquery bootstrap-4 modal-dialog bootstrap-modal


【解决方案1】:

Working JSFiddle(我用简单的alert()s 替换了您的toastr 内容,并注释掉了socket 内容)。

问题在于您正在添加基于可重复用户活动的事件侦听器。例如:

if(room_pass != "False"){ 
    // ...
    // Adding an event handler for the modal shown event based on 
    // previous user activity
    $('#password-validation').on('shown.bs.modal', function () {
        // ... 
        // Adding another event handler, inside a previous event handler
        $("#room-password-btn").mousedown(function(){

但添加事件处理程序并不会替换任何先前已附加的事件处理程序 - 它们会叠加,即使它是完全相同的事件处理程序。因此,如果您重复添加处理程序的条件,您将在旧处理程序之上添加一个新条件。并且它们会在触发时一起运行。

每次关闭模态框时,都会添加另一个处理程序以在它打开时执行某些操作,这反过来又会添加另一个处理程序以在 mousedown 上执行某些操作。第一次关闭它时,你有一组处理程序来做事。如果您打开它并再次关闭它,您将添加一个新集合,它们将在事件触发时全部运行。

最好只添加一次处理程序,不受任何用户行为或条件的影响。这样您就可以确保它们只连接一次。如果您希望处理程序采取的操作取决于用户所做的事情,您应该测试处理程序中的状态,而不是相反。

所以在这种情况下:

// Add event handlers, independent of any other activity
$('#password-validation').on('hidden.bs.modal', function () {
    console.log("Closed")
    // ...
});

$('#password-validation').on('shown.bs.modal', function () {
    $('#room-password-val').trigger('focus');
});

$("#room-password-btn").mousedown(function(){
    // ...
});

// ... 

if (room_pass != "False") {
    $("#password-validation").modal('toggle');

} else{
    console.log("Joining a room")
    // ...
}

请注意,如果需要,您也可以remove handlers using .off(),并且在某些复杂的情况下,这可能是可行的方法 - 随着状态的变化添加和删除处理程序。但是这里没有必要这么复杂。

更新

从 cmets 来看,实际问题出在一些未显示的代码中,该代码处理用户点击聊天室菜单,该菜单是从数据库中动态填充的。我在上面描述了如何处理此问题的一般情况 - 测试事件处理程序中的状态,以便您可以采取适当的措施。这是一个示例 - 不是基于您的实际 HTML 或代码,因为我没有。

HTML 示例:

<ul class="dropdown-menu">
    <li><a href="/rooms/chat1" data-room="room1">Chat 1</a></li>
    <li><a href="/rooms/chat2" data-room="room2">Chat 2</a></li>
    <li><a href="/rooms/chat3" data-room="room3">Chat 3</a></li>
</ul>

JS:

// Password will be set in AJAX below
var room_pass;

// Register a handler on .dropdown-menu, even if it has no elements
// on page load.  Every click will be filtered to check it it was 
// on an <a>.  This way you can dynamically add contents to the menu,
// and still handle those clicks.
$('.dropdown-menu').on('click', 'a', function(e) {

    // Don't actually follow the link 
    e.preventDefault();

    // In the handler, "$(this)" is the event target.  So you can 
    // find which link was clicked like this:
    var href = $(this).attr('href');
    
    // Or its text:
    var text = $(this).text();

    // Or maybe it has data attributes
    var room = $(this).data('room');

    // Not sure I understand your comments but maybe you want something
    // like this, where you pass something about the clicked link to the
    // AJAX query
    $.ajax({
        type: "POST",
        url: "/validate",
        data: room,
        success: function(pass) {
            room_pass = pass;
        }
    });
});

小注:

  • 使用mousedown 处理密码输入意味着用户无法使用键盘提交。这不是很好的用户体验,它看起来像一个表单,所以按 Enter 应该可以工作,当然还有可访问性问题。要解决此问题,您需要在模式中实际添加 &lt;form&gt;,并在该表单上将 mousedown 处理程序替换为 submit 处理程序。

  • corrent_pass 中的错字,可能是correct?只要始终使用它就不是很重要,但是拼写错误是给您未来的自己带来很多困惑的好方法! :-)

【讨论】:

  • 我理解,它可能在模型中工作,但它仍然是我正在处理的代码中的一个问题。那当然是在我按照你说的我应该做的改变你的代码之后。
  • @Patch 听起来您在某个地方还有另一个问题,我链接到的 JSFiddle 解决了您描述的问题,对吗?如果您可以使用显示问题的 simplified 代码更新您的问题和/或 Codepen,我可以看看。请尽量简化它,删除与这个问题无关的东西,让我们更容易让你的代码运行(例如套接字的东西,toastr 的东西......)。
  • 我没有“简化”它。唯一真正的区别是我首先检查用户是否从下拉菜单中单击了一个房间 P.S 您添加的 2 个小注释并不真正相关...
  • @Patch 我知道你没有简化它,我问你是否可以进行任何更新:-) 如果你能让我们更简单地运行你的代码,那么更简单帮助。这些笔记只是建议,这就是为什么我称它们为小笔记! :-) 你可以用也可以不用。
  • $(".dropdown-menu").on('click', '.room', function(event){ 在执行我在此处显示的代码之前,我会调用此行。演示它有点困难,因为我从数据库中获取房间,我需要创建下拉菜单。但基本上我在 JSFiddle 中的代码之前就有了。我需要将代码放在dropdown-menu 调用中,因为我正在使用该事件。
【解决方案2】:

您可以通过使用offafter your #password-validation 事件侦听器的一行代码来解决该问题 它基本上是做什么的 它在触发后取消绑定/删除事件侦听器,这样您就不会触发一堆事件再次打开模态框后

$('#password-validation').on('shown.bs.modal', function () {
$('#room-password-val').trigger('focus');
$("#password-validation").off("shown.bs.modal");

这里是jsfiddle

【讨论】:

  • 如果我将$("#password-validation").off("shown.bs.modal");$("#password-validation").modal('toggle'); 都放在off 中,则$("#password-validation").modal('toggle'); 需要在if(room_pass != "False"){ 中,其中一个只是取消modal('toggle') 一个
  • 这里的代码与您在 codepen 上发布的完全相同,jsfiddle
  • 是的,但它会重新打开,因为在代码中,当您关闭模式时它会重新打开,我说这不是我实际上正在做的事情,只是为了向您展示问题。
  • 您在对上述问题的询问中没有提及这些,而它重新打开的事实是因为在您的代码/问题中您说它是为了“测试目的”,在我的第一个代码中如果您足够注意我删除了重新打开行为并使用按钮模仿了现实生活中的场景,所以也许您将来应该更加清晰和准确
  • 我确实做到了,我在我的代码中评论了它。如果我要在我的问题中解释它,没有人会理解。我的问题再清楚不过了,当我按照您的方式进行操作时,您只是查看了模型文件的内部,它立即关闭了。反正别人已经回答了。 +1。
【解决方案3】:

您已经通过执行以下操作检查了您的模态是否显示在此行 $("#password-validation").modal(); 之后:

console.log($('#password-validation').is(':visible')) // false every time
console.log($('#password-validation').hasClass('in')) // false every time

所以这两行在显示模态之前首先执行。您可以在此事件显示模态后进行检查:

$('#password-validation').on('shown.bs.modal', function () {
    // will only come inside after the modal is shown
    console.log($('#password-validation').is(':visible')) // true every time if shown
    console.log($('#password-validation').hasClass('show')) // true every time if shown
});

仔细看,$('#password-validation').hasClass('in') 这在bootstrap 4 中不起作用。因此,您可以通过我在事件中提到的方式检查模态是否显示。

我猜这会解决你的问题。

【讨论】:

  • 嗯,这会影响我的笔记,但我认为它不会解决整个问题。
  • 是的,解决了我的笔记,但实际问题没有解决。
  • 多次执行是因为您的模态没有正确销毁。每次按下回车键后,只需通过 $('#password-validation').modal('dispose') 处理你的模式
  • 第三次打印(Execute) 是否在关闭模态并在处置模态时重新打开后多次打印?
  • 是的。而你所说的处置没有奏效。 “关闭模式并重新打开后”是的,然后第三次单击输入按钮。我在用户点击输入按钮后放置了 dispote,当我尝试关闭它时它会 modal.js:282 Uncaught TypeError: Cannot read property 'focus' of null Uncaught TypeError: Cannot read property 'backdrop' of null
【解决方案4】:

Bootstrap 现在有一个.one 方法,它只会在触发器上触发一次回调,然后再取消绑定。

$('#password-validation').one('shown.bs.modal', function () {
   $('#room-password-val').trigger('focus');
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-01
    相关资源
    最近更新 更多