Bootstrap 每次打开新模态时,模态内部代码都会成倍增加
Bootstrap modal inside code is multiplying every time a new modal opens
你好,所以我为我建立的聊天创建了一个房间密码验证,我使用 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">×</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
}
}
你已经检查了你的模式是否在这一行之后 $("#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
中是行不通的。所以你可以通过我在事件中提到的这种方式来检查是否显示了模态。
我想这会解决你的问题。
Working JSFiddle(我用简单的 alert()
替换了您的 toastr
内容,并注释掉了 socket
内容)。
问题是您正在添加基于可重复用户的事件侦听器 activity。例如:
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()
,在某些复杂的情况下,这可能是可行的方法 - 在状态更改时添加和删除处理程序。但是这里不需要这么复杂。
更新
根据评论,实际问题出在一些未显示的代码中,这些代码处理用户单击聊天室菜单的操作,该菜单是从数据库动态填充的。我在上面描述了如何处理这个问题的一般情况 - 测试事件处理程序内的状态,以便您可以采取适当的措施。这是一个示例 - 不是基于您的实际 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
处理密码输入意味着用户无法使用键盘提交。这不是好的用户体验,它看起来像一个表单,所以点击回车应该可以,当然还有可访问性问题。要解决此问题,您需要在模态中实际添加一个 <form>
,并在该表单上将 mousedown
处理程序替换为 submit
处理程序。
corrent_pass
中的错字,可能是 correct
?只要始终如一地使用它并不重要,但拼写错误是给未来的自己带来很多困惑的好方法! :-)
你可以通过一行代码解决这个问题 off
正确的 after your #password-validation
事件监听器它基本上做了什么 unbind/removes 触发后的事件监听器,这样你就不会再次打开模态后会触发一堆事件
$('#password-validation').on('shown.bs.modal', function () {
$('#room-password-val').trigger('focus');
$("#password-validation").off("shown.bs.modal");
这里是jsfiddle
Bootstrap 现在有一个 .one
方法,它只会在触发器上触发一次回调,然后再解除绑定。
$('#password-validation').one('shown.bs.modal', function () {
$('#room-password-val').trigger('focus');
}
你好,所以我为我建立的聊天创建了一个房间密码验证,我使用 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">×</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
}
}
你已经检查了你的模式是否在这一行之后 $("#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
中是行不通的。所以你可以通过我在事件中提到的这种方式来检查是否显示了模态。
我想这会解决你的问题。
Working JSFiddle(我用简单的 alert()
替换了您的 toastr
内容,并注释掉了 socket
内容)。
问题是您正在添加基于可重复用户的事件侦听器 activity。例如:
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()
,在某些复杂的情况下,这可能是可行的方法 - 在状态更改时添加和删除处理程序。但是这里不需要这么复杂。
更新
根据评论,实际问题出在一些未显示的代码中,这些代码处理用户单击聊天室菜单的操作,该菜单是从数据库动态填充的。我在上面描述了如何处理这个问题的一般情况 - 测试事件处理程序内的状态,以便您可以采取适当的措施。这是一个示例 - 不是基于您的实际 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
处理密码输入意味着用户无法使用键盘提交。这不是好的用户体验,它看起来像一个表单,所以点击回车应该可以,当然还有可访问性问题。要解决此问题,您需要在模态中实际添加一个<form>
,并在该表单上将mousedown
处理程序替换为submit
处理程序。corrent_pass
中的错字,可能是correct
?只要始终如一地使用它并不重要,但拼写错误是给未来的自己带来很多困惑的好方法! :-)
你可以通过一行代码解决这个问题 off
正确的 after your #password-validation
事件监听器它基本上做了什么 unbind/removes 触发后的事件监听器,这样你就不会再次打开模态后会触发一堆事件
$('#password-validation').on('shown.bs.modal', function () {
$('#room-password-val').trigger('focus');
$("#password-validation").off("shown.bs.modal");
这里是jsfiddle
Bootstrap 现在有一个 .one
方法,它只会在触发器上触发一次回调,然后再解除绑定。
$('#password-validation').one('shown.bs.modal', function () {
$('#room-password-val').trigger('focus');
}