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

ctl00_ctl33_g_3782ce51_9259_4854_80a0_e6355e54b690_FormControl0_V1_I1_T6

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

 

Similar Posts

27 Comments

  1. Hello Marc
    Thanks for the post.
    I tried using the procedure explained in Mark Rackley’s blog http://www.paitgroup.com/easy-digital-signatures-in-sharepoint/. It worked well except when I customize the InfoPath form. So I tried your procedure but so far nothing appears:
    I created a section with the tooltip == Signature ==, then I added a multiline field in plain text, its tooltip is : signatureSignature.
    Finally I added a CEWP that references your script in a .js file.
    Still no success

    1. @BAMBA:

      As with any code you find on the Web, you should consider this a starting point. This approach relies on some very specific elements and attributes in the page, you you’ll need to do some debugging to get it right for your situation.

      M.

  2. Hello Marc,
    I know its an old post but we want to implement this exact idea on our SharePoint server.
    I didnt manage to get it to work.
    Here is what I have done, kindly let me know where things went wrong:

    1- created a SharePoint list with a multi-line text field (plain text).
    2- Added some InfoPath changes to the form. named the ScreenTip for the section that has the multi-line field to “==Signature==”
    3- Edited the “new form” by adding Content Editor Web Part and called the following scripts : jquery.signaturepad.css, flashcanvas.js, jquery.js, jquery.signaturepad.js, json2.js (downloaded from https://github.com/thread-pond/signature-pad/ )
    4- After that in the same Content Editor Web Part, added the code you provided above.

    Appreciate your effort in this blog post.

    -Hosam

    1. @Hosam:

      My guess is you haven’t got the right selectors for the objects in the DOM. This is a bolted together solution, so getting the selectors right and debugging in the console to ensure the code is running properly are required skillls.

      M.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.