Using the Signature Pad jQuery Plugin with SharePoint & InfoPath

Have you ever needed to capture signatures in SharePoint forms? What about InfoPath forms? I’m betting many people have seen a need to do this but have balked at the cost or complexity of the available solutions.

A client of mine named Cody Sellers (@codyjsellers), who works for Mercom Corporation had just such a need. Mercom is a smallish company that didn’t want to spend tens of thousands of dollars on a “real” eSig solution. Cody did some research and found a great jQuery plugin and turned to me for help implementing it. In this case we’re working with SharePoint 2013 on premises, but this should work with any version of SharePoint with some modification.

2014-06-18_13-38-20

The Signature Pad plugin comes from a clearly smart guy named Thomas J Bradley. Based on the work I’ve done with it so far, it’s well written with good documentation. Even better: it just plain works.

Signature Pad: A jQuery plugin for assisting in the creation of an HTML5 canvas based signature pad. Records the drawn signature in JSON for later regeneration.

The plugin allows you to capture signatures based on typed or drawn input and can be saved as JSON or a PNG image.

In our case, we wanted to be able to add a signature block at the bottom of multiple InfoPath forms. To make this work well, we decided that we would add the script into the master page. Yes, that may seem foolhardy, but a large majority of the work that is going to be happening in this Site Collection is filling out these forms.

Because of this, we needed a clear convention to follow in every form so that we could locate all of the signature fields (some forms have more than one) and add the signature pad capability reliably.

One of the hardest things about working with InfoPath forms client-side is that there are almost no sensible hooks in the emitted DOM. Everything is a .NET-like element id, like

Since those ids can change anytime you change the form, you really want to give yourself something more solid to hook into. (At least we know that InfoPath won’t change anymore and the basic form elements are constant!)

To ensure that we could easily add the signature pad to any existing signature fields, we wrapped each signature field in an InfoPath section with its ScreenTip set to “==Signature==”.

InfoPath section with its ScreenTip set to ==Signature==

InfoPath section with its ScreenTip set to ==Signature==

The logic works like this:

  • Check to see if there’s a div with its id ending in ‘XmlFormView’ – this is the container for an InfoPath form. If there isn’t one, do nothing.
  • Find sections in the form that have their ScreenTip set to “==Signature==”. Sections have a fieldset container, and the title is set to the ScreenTip value. In other words, any section with its ScreenTip set to “==Signature==” will be treated as a signature area by the code.
  • Find the input element inside that section. This is the field where we want to store the JSON representation of the signature.
  • If the input element has no value, render the signature pad for signing (new forms)
  • If the input element already has a value, show the signature it contains (edit and display forms)

We decided to store the JSON representation of the signature in a field in each InfoPath form. Since the JSON represents the vectors from an HTML5 canvas, it’s a nice, standard way to store it. Images would require some separate repository or further encoding to be stored in a text field. Since the plugin can both emit and reconstitute the JSON into a signature, it’s a good way to go.

We ended up with the code below for the simplest case. (It’ll get more complicated as we get into the business rules, but this post is about the technology to capture and display the signatures.) When we create a new item in the newifs.aspx form, the Signature column will be empty and thus we will show the signature pad. If there’s already a value in the field, then we’re on the edit or display form, and signing has already happened so we just display the signature we have.

I’ve added comments that hopefully make the code simple enough to follow. As with all code you read on the Web, this isn’t just a drop-it-in-and-it’ll-work thing – you’ll end up tailoring it for sure. (One would think I wouldn’t need these caveats, but…)

$(document).ready(function() {
    // Find the guts of the InfoPath form in the page
    var infoPathContainer = $("div[id$='XmlFormView']");
    // if there's no InfoPath form, then we have nothing to do here (escape early)
    if (infoPathContainer.length !== 0) {
        setupSignatures(infoPathContainer);
    }
});
// There's an InfoPath form in the page, so look for signature fields and set them up
function setupSignatures(infoPathContainer) {
    // The Signature "signature" in the form. This makes it easy to select the proper form elements
    var signatureSignature = "==Signature==";
    // The signature field should be inside a section with its tooltip=signatureSignature
    var signatureContainer = infoPathContainer.find("fieldset[title='" + signatureSignature + "']");
    // The Signature field is the lone input element in the section
    var signatureBox = signatureContainer.find("input");
    // We may have multiple signature fields in the form
    signatureBox.each(function() {
        var thisSignatureBox = $(this);
        // We need a reference to the fieldset which represents the section several times
        var thisSignatureFieldset = thisSignatureBox.closest("fieldset");
        // Clean up the display by hiding the field we're using to store the JSON
        thisSignatureBox.hide();
        // The signature data is the value of the signatureBox
        var signatureData = thisSignatureBox.val();
        // If there's no signature yet...
        if (signatureData === "") {
            // Add the appropriate markup to the page
            thisSignatureBox.before(buildSignatureBox());
            // Bind to the click event for the 'Ready to Sign' button
            thisSignatureFieldset.find(".signature-ready").click(function() {
                $(this).toggle();
                $(this).next("div").toggle();
            });
            // Activate the signature pad in drawOnly mode
            var signatureArea = thisSignatureFieldset.find(".signature-box").signaturePad({
                drawOnly: true,
                lineTop: 125,
                output: thisSignatureBox,
                onBeforeValidate: function(context, settings) {
                    thisSignatureBox.focus(); // Needed to fire the change events bound to the field
                    thisSignatureBox.blur(); // Needed to fire the change events bound to the field
                    thisSignatureBox.hide(); // In case it becomes visible again
                }
            });
            // When the user clicks the button below the signature pad, validate
            thisSignatureFieldset.find(".signature-done").click(function() {
                thisSignatureFieldset.find("p.error").remove();
                signatureArea.validateForm();
            });
            // If we already have signature data, just show the existing signature
        } else {
            // Add the appropriate markup to the page
            thisSignatureBox.before(buildSignatureDisplay());
            // Activate the signature pad in displayOnly mode
            thisSignatureFieldset.find(".sigPad").signaturePad({
                displayOnly: true
            }).regenerate(signatureData);
        }
    });
}
// Function to emit the markup for the signature pad in signing mode
function buildSignatureBox() {
    var signatureBox = "<div class='signature-box'>" +
        "<input class='signature-ready' type='button' value='Ready to sign'/>" +
        "<div style='display:none;'>" +
        "<ul class='sigNav'>" +
        "<li class='drawIt'><a href='#draw-it'>Sign Here</a></li>" +
        "<li class='clearButton'><a href='#clear'>Clear</a></li>" +
        "</ul>" +
        "<div class='sig sigWrapper'>" +
        "<canvas class='pad' width='700' height='150'></canvas>" +
        "<input type='hidden' name='output' class='output'>" +
        "</div>" +
        "<input class='signature-done' type='button' value='Capture signature'/>" +
        "</div>" +
        "</div>";
    return signatureBox;
}
// Function to emit the markup for the signature pad in display mode
function buildSignatureDisplay() {
    var signatureDisplay = "<div class='sigPad signed'>" +
        "<div class='sigWrapper'>" +
        "<canvas width='700' height='150' class='pad'></canvas>" +
        "</div>" +
        "</div>";
    return signatureDisplay;
}

Here are some screenshots from our proof of concept:

The form when it loads

The form when it loads

Ready to sign

Ready to sign

Signed

Signed

Signature displayed on the display form

Signature displayed on the display form

Snippet of the data that is stored for a signature

Snippet of the data that is stored for a signature

 

Interested in saving the signature as an image file? Check out my follow up post: Using the Signature Pad jQuery Plugin with SharePoint & InfoPath – Redux

 

21 Comments

  1. Thanks for this. This is exactly what I am after but i cannot get this to work based on the details given here. Are you able to break it down a bit more for those less able? Can the field in Infopath be just a text field? Do we register the js files on the master page as normal? Where do we place the code you submitted on the master page?
    Trying to make this work for a client so any further help would be great. Thanks.

    Reply
    • Brendan:

      Did you get things working? Generally speaking (and everything should always depend on your particular environment and needs), I’d reference jQuery in the master page. I’d do everything else in the specific page(s) where you want to use the signature pad.

      M.

      Reply
  2. This is a perfect solution! What would it cost for someone to help us with this? I just cannot seem to get it working whether I try it in a masterpage or web part page with an InfoPath form. It seems so straight-forward, but not working. I even tried his demo and I get everything but the signature pad. : / I can’t even seem to find a third party solution just like this for InfoPath without the digital signatures.

    Reply
  3. It looks like the code is Javascript, and I’ll obviously need to make changes to it to apply it.

    I already added jQuery to the Master Page, and I figured you converted the InfoPath form to HTML, and then added the Javascript code using the Script Editor web part to have the signature hook into the form, but that didn’t work for me– mostly because I had trouble converting the InfoPath XSN to HTML.

    Is there a step in between that I’m missing? Are you not using InfoPath Form Services?

    Reply
    • There’s no need to “convert the InfoPath form to HTML” – and I’m not even sure how you’d do that. When the form lands in the browser it is HTML, as Forms Services renders it as HTML.

      The script I show in this post simply alters the behavior of the form once it’s in the browser.

      M.

      Reply
      • Ok, so, I create the Form Library as usual. Then, go add the script to where? I thought it was supposed to be as a Content Editor web part, but that’s clearly not it, nor is it using the Visual Studio editor for the InfoPath form as an On Load Event for that Signature Field. Unless, I’m forgetting another obvious place to put the item?

        Reply
        • As I mentioned in the article, we added the scripts to the master page because the Site Collection was going to have a lot of forms. If you only want it to run in specific pages, you can add it into those pages with CEWPs. No visual Studio required whatsoever.

          The script finds the form in line 3, and then only does its magic if there’s actually a form on the page. You may well need to adapt the selectors here to match what happens in your environment. Like I said, consider it an example, not a solution.

          M.

          Reply
      • I display the NEW form on a wiki page so signature works fine by loading all the necessary script via content editor but when we are viewing/editing the form, xml version of the form is displayed on browser with URL something like …/…./…./FormServer.aspx?XmlLocation=/…./…./test.xml. How can we display signature properly on edit/view form ?

        Reply
        • @Mumtaz/MMomin:

          As I say in the post, this is pretty much a hack. It would be great if Infopath forms simply had a signature pad available in them; but that won’t ever happen.

          I’ve implemented this for a few clients now, and each time is basically unique. There are a lot of things that can be going on in the individual forms that may affect the signature pad, and I’ve had to build it in differently each time.

          M.

          Reply
          • Thanks Mark for your reply.

            I got set it up and it works great but now the only issue is my Info Path form also has people picker and when I select a person. page gets partially reloaded and signature pad get replaced by the original text box.

            Any advice ?

            Reply
  4. Wow!!! it looks great. Congratulations!

    Can it be adapted to replace the awful signature line in the Office 365 documents? Combined with the SP collecting signatures workflow* this app will be 8th marvel, ja!

    Please tell me if you soon will release something resemble what I said.

    Thanks
    A

    *or at least an aproval workflow

    Reply
  5. Okay that is what I did as well. So here is what I did. I took a sharepoint list, I edit the new form with info path. I added a CEWP I added the txt doc with the corresponding scripts. I can see that it fires. but it does turn the field into the signing. Its kind of a pain to debug a script with a script inside of a CEWP. Is the CEWP required after the form? I am not sure why it is not adding the overlay. I added the tooltip. I added your script with the required scripts prior. Any direction would be appreciated

    Reply

Have a thought or opinion?