第二个触发Modal的按钮首次点击无响应且无法正常关闭
第二个签名按钮点击异常:首次需双击打开Modal,打开后无法关闭
我开发了一个表单签名功能,需要用户通过签名板签署三个独立的签名,为此做了三个按钮打开同一个签名Modal。但第二个按钮存在两个异常:
- 首次点击无法打开Modal,必须点击第二次才生效;
- Modal打开后无法正常关闭。
问题仅出现在第二个按钮,和点击顺序无关,第一、第三个按钮的Modal打开/关闭都正常。怀疑是事件处理或Modal初始化代码的问题,但找不到具体原因。
相关代码片段
按钮代码
<tr> <td> <label for="candidate_sign">Tandatangan</label> <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#signatureModal" data-user="candidate">Add Signature</button> <input type="hidden" name="candidate_signature" id="candidate_signature" value="<?= htmlspecialchars($form_data['candidate_signature'] ?? '') ?>"> <div id="candidate-signature-container" class="signature-container"> <!-- The signature that been saved will be placed here --> <?php if (!empty($form_data['candidate_signature'])): ?> <img src="<?= htmlspecialchars($form_data['candidate_signature']); ?>" alt="Candidate Signature" class="signature-img"> <?php endif; ?> </div> </td> <td> <label for="hr_sign">Tandatangan</label> <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#signatureModal" data-user="hr">Add Signature</button> <input type="hidden" name="hr_signature" id="hr_signature" value="<?= htmlspecialchars($form_data['hr_signature'] ?? '') ?>"> <div id="hr-signature-container" class="signature-container"> <!-- HR signature will be displayed here --> <?php if (!empty($form_data['hr_signature'])): ?> <img src="<?= htmlspecialchars($form_data['hr_signature']); ?>" alt="HR Signature" class="signature-img"> <?php endif; ?> </div> </td> <td> <label for="comben_sign">Tandatangan</label> <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#signatureModal" data-user="comben">Add Signature</button> <input type="hidden" name="comben_signature" id="comben_signature" value="<?= htmlspecialchars($form_data['comben_signature'] ?? '') ?>"> <div id="comben-signature-container" class="signature-container"> <!-- Comben signature will be displayed here --> <?php if (!empty($form_data['comben_signature'])): ?> <img src="<?= htmlspecialchars($form_data['comben_signature']); ?>" alt="Comben Signature" class="signature-img"> <?php endif; ?> </div> </td> </tr>
Modal代码
<div class="modal" id="signatureModal" tabindex="-1" role="dialog" aria-labelledby="signature" aria-hidden="true"> <div class="modal-dialog modal-xl"> <div class="modal-content" style="width: 100%; max-width:100%;"> <div class="modal-header"> <h4 class="modal-title" style="display: inline-block; vertical-align: middle;">Electronic Signature</h4> <button type="button" class="close" data-dismiss="modal" aria-label="Close" style="float: right; margin-top: -5px;"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <!-- Tab navigation --> <ul class="nav nav-tabs" id="signatureTab"> <li class="active"> <a href="#add-signature" data-toggle="tab">Add Signature</a> </li> </ul> <!-- Tab contents --> <div class="tab-content" id="signatureTabContent"> <!-- Add Signature Tab --> <div class="tab-pane active" id="add-signature"> <canvas id="signature-pad" class="signature-pad border bg-white" style="width: 100%; height: 500px; border: 1px solid #ccc;"></canvas> <p class="text-muted small">*Your signature will be saved in the database.</p> </div> </div> <div class="modal-footer"> <div class="pull-left"> <button type="button" class="btn btn-info" id="undo">Undo</button> <button type="button" class="btn btn-danger" id="clear">Clear</button> <button type="button" class="btn btn-primary" id="done">Done</button> </div> </div> </div> </div> </div> </div>
函数逻辑代码
var $j = jQuery.noConflict(); document.addEventListener("DOMContentLoaded", () => { const canvas = document.getElementById('signature-pad'); const clearButton = document.querySelector('#clear'); const undoButton = document.querySelector('#undo'); const doneButton = document.querySelector('#done'); let currentUserType = ''; // To track the current user type (candidate, hr, or comben) // jQuery-specific logic $j(() => { // Initialize tabs $j('.nav-tabs a').tab(); // Track which button triggered the modal $j('button[data-target="#signatureModal"]').on("click", function () { currentUserType = $j(this).data("user"); // Set current user type updateSignatureButtonText(currentUserType); // Update button text for the specific user $j('#signatureTab a[href="#add-signature"]').tab('show'); // Show the add signature tab }); // Resize canvas when modal is shown $j("#signatureModal").on("shown.bs.modal", resizeCanvas); $j(window).on("resize", resizeCanvas); }); const signaturePad = new SignaturePad(canvas, { backgroundColor: null, penColor: 'black', lineWidth: 10 }); function resizeCanvas() { const ratio = Math.max(window.devicePixelRatio || 1, 1); canvas.width = canvas.offsetWidth * ratio; canvas.height = canvas.offsetHeight * ratio; canvas.getContext("2d").scale(ratio, ratio); signaturePad.clear(); } function closeModal() { $j('#signatureModal').on('shown.bs.modal', function() { $j("#signatureModal").modal("hide"); }); } clearButton.addEventListener('click', () => signaturePad.clear()); undoButton.addEventListener('click', () => { const data = signaturePad.toData(); if (data.length > 0) { data.pop(); signaturePad.fromData(data); } }); doneButton.addEventListener("click", function () { if (signaturePad.isEmpty()) { alert("Please provide a signature first."); return; } const dataURL = signaturePad.toDataURL("image/svg+xml"); displayRecentSignature(dataURL, currentUserType); closeModal(); }); // Function to display the signature function displayRecentSignature(dataURL, userType) { const containerId = `${userType}-signature-container`; // Dynamically determine the container const recentSignatureContainer = document.getElementById(containerId); if (!recentSignatureContainer) { console.error(`Signature container for ${userType} not found`); return; // Exit if the container doesn't exist } // Clear previous signature in the specific container recentSignatureContainer.innerHTML = ""; const img = document.createElement("img"); img.src = dataURL; img.alt = "Recent Signature"; img.classList.add("recent-signature"); img.style.width = "150px"; img.style.height = "auto"; recentSignatureContainer.appendChild(img); const hiddenInput = document.getElementById(`${userType}_signature`); hiddenInput.value = dataURL; // Update button text to "Edit Signature" after signature is added updateSignatureButtonText(userType); } // Function to update the signature button text function updateSignatureButtonText(userType) { const button = document.querySelector(`button[data-user="${userType}"]`); if (!button) { console.error(`Button for ${userType} not found`); return; // Exit if the button doesn't exist } const signatureField = document.getElementById(`${userType}_signature`).value; // Check if the signature field has a value and update the button text accordingly if (signatureField && signatureField.trim() !== "") { button.textContent = "Edit Signature"; // Change button text if signature exists } else { button.textContent = "Add Signature"; // Default button text if no signature } } // Call the function on page load to set the initial button text ["candidate", "hr", "comben"].forEach(userType => { updateSignatureButtonText(userType); }); });
已尝试的解决方法
- 使用
.on('click')和.off('click')处理按钮点击与Modal事件; - 添加
console.log调试,日志显示按钮被点击、Modal正常打开关闭,但实际现象不符; - 添加超时强制隐藏Modal,未解决问题。
预期效果
- 任意按钮点击后Modal立即打开;
- 点击关闭按钮后Modal正常关闭。
实际现象
- 第二个按钮首次点击无法打开Modal,需第二次点击;
- 打开后Modal无法正常关闭。
问题分析与修复方案
1. 核心问题:closeModal函数逻辑错误
你的closeModal函数没有直接关闭Modal,而是给Modal绑定了一个shown.bs.modal事件监听,意思是当Modal显示出来后立即隐藏它。每次点击Done按钮都会调用这个函数,导致重复绑定事件:
- 第一次点击
Done后,绑定了一个事件,下次Modal打开时会自动关闭; - 多次点击后,事件叠加,导致Modal的显示/隐藏状态混乱,这就是第二个按钮首次点击没反应、关闭失效的根本原因。
修复:修改closeModal函数为直接关闭Modal
function closeModal() { // 直接调用Bootstrap的modal方法隐藏弹窗,无需绑定额外事件 $j("#signatureModal").modal("hide"); }
2. 优化按钮事件绑定(避免潜在冲突)
虽然Bootstrap的data-toggle可以自动打开Modal,但你的自定义点击事件和Bootstrap的内置事件可能存在时序冲突。建议将按钮的事件绑定改为事件委托,确保即使按钮文本修改(比如从"Add Signature"变成"Edit Signature"),事件依然有效:
// 替换原来的按钮点击事件绑定 $j(document).on("click", 'button[data-target="#signatureModal"]', function () { currentUserType = $j(this).data("user"); updateSignatureButtonText(currentUserType); $j('#signatureTab a[href="#add-signature"]').tab('show'); });
3. 移除重复的事件绑定(可选)
确保resizeCanvas只绑定一次,避免多次触发导致Canvas重复初始化:
// 将原来的Modal shown事件绑定改为一次性绑定(可选,避免重复绑定) $j("#signatureModal").one("shown.bs.modal", resizeCanvas); // 如果需要每次Modal显示都触发,保留on但确保只绑定一次,比如放在DOMContentLoaded里只执行一次
4. 验证Modal的关闭逻辑
确保Modal的关闭按钮data-dismiss="modal"属性正确,并且没有其他JS代码干扰它的功能。
修复后测试
- 点击第二个按钮,Modal应立即打开;
- 点击Modal的关闭按钮或
Done按钮,Modal应正常关闭; - 多次切换不同按钮,验证所有签名功能正常工作。
内容的提问来源于stack exchange,提问作者Oktavian
相关产品推荐
相关产品推荐

