You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

第二个触发Modal的按钮首次点击无响应且无法正常关闭

第二个签名按钮点击异常:首次需双击打开Modal,打开后无法关闭

我开发了一个表单签名功能,需要用户通过签名板签署三个独立的签名,为此做了三个按钮打开同一个签名Modal。但第二个按钮存在两个异常:

  1. 首次点击无法打开Modal,必须点击第二次才生效;
  2. 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">&times;</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代码干扰它的功能。

修复后测试

  1. 点击第二个按钮,Modal应立即打开;
  2. 点击Modal的关闭按钮或Done按钮,Modal应正常关闭;
  3. 多次切换不同按钮,验证所有签名功能正常工作。

内容的提问来源于stack exchange,提问作者Oktavian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 23:42:03