GreenShift Tips and Tutorials

Signature Form JS and CSS

Ivan
Ivan
 · 
This JS snippet complements the work done with Interaction Layers to show/hide various helper icons (cookie notices, chat widgets, back to top buttons, etc.) so that they do not cover the footer area.
Enque the signature_pad script
<script  src='https://cdnjs.cloudflare.com/ajax/libs/signature_pad/5.1.4/signature_pad.umd.min.js'></script>
HTML
Run the custom JS which deploys the signature pad component, collects the drawing, and packages it as a file upload.
(function() {
    // Compact Base64 DataURL -> File converter
    const dataURLtoFile = d => {
        const [h, b] = d.split(','), mime = h.match(/:(.*?);/)[1], bin = atob(b), u8 = new Uint8Array(bin.length);
        for (let i = 0; i < bin.length; i++) u8[i] = bin.charCodeAt(i);
        return new File([u8], 'signature.png', { type: mime });
    };

    function init() {
        if (typeof SignaturePad === 'undefined') return false;
        const wrap = document.querySelector('.cf-signature-target');
        const form = wrap?.closest('craftforms-form') || document.querySelector('craftforms-form');
        if (!wrap || !form || wrap.querySelector('.cf-sig-box')) return wrap && form;

        const input = wrap.querySelector('input[name="signature"], input[type="file"]');
        const name = input?.getAttribute('name') || 'signature';

        // Hide native upload field component
        wrap.querySelector('craftforms-file-upload')?.style.setProperty('display', 'none');

        // Disarm native input.value string assignment crash on file inputs
        if (input?.type === 'file') {
            Object.defineProperty(input, 'value', { set: () => {}, get: () => '', configurable: true });
        }

        // Render Signature Pad UI
        wrap.insertAdjacentHTML('beforeend', `
            <div class="cf-sig-box" style="margin-top:8px">
                <canvas width="400" height="180" style="border:1px solid #ccc;border-radius:6px;touch-action:none;background:#fff;width:100%;max-width:400px;display:block"></canvas>
                <button type="button" class="cf-sig-clear" style="margin-top:6px;padding:3px 8px;font-size:12px;cursor:pointer">Clear Signature</button>
            </div>
        `);

        const pad = new SignaturePad(wrap.querySelector('canvas'), { backgroundColor: 'rgba(255,255,255,0)', penColor: '#000' });

        // Unified State Sync & Clear handler
        const sync = (clear = false) => {
            if (clear) pad.clear();
            const file = pad.isEmpty() ? null : dataURLtoFile(pad.toDataURL());

            form.pendingFiles = form.pendingFiles || {};
            if (file) form.pendingFiles[name] = file;
            else delete form.pendingFiles[name];

            // Prevent duplicate string key collision in FormData
            if (form.formState) delete form.formState[name];

            if (input) {
                const dt = new DataTransfer();
                if (file) dt.items.add(file);
                input.files = dt.files;
                input.required = false;
                input.removeAttribute('required');
            }
        };

        // Event bindings
        pad.addEventListener('endStroke', () => sync());
        wrap.querySelector('.cf-sig-clear').addEventListener('click', () => sync(true));

        document.querySelectorAll('.wp-block-craftforms-submit-button, button[type="submit"], craftforms-form button')
            .forEach(btn => btn.addEventListener('pointerdown', () => sync(), { capture: true }));

        return true;
    }

    if (!init()) {
        const timer = setInterval(() => init() && clearInterval(timer), 100);
    }
})();
PHP
Hide the file upload form fields.
.craftforms-file-dropzone, .craftforms-file-hint, .craftforms-file-preview-list {
    display: none ;
}
CSS

This code snippet is used in the following tutorial(s):

This website uses cookies to enhance your browsing experience and ensure the site functions properly. By continuing to use this site, you acknowledge and accept our use of cookies.

Accept All Accept Required Only