Uploading Attachments to SharePoint Lists Using REST

In a previous post, I walked through Uploading Attachments to SharePoint Lists Using SPServices. Well, in the “modern” world, I want to use REST whenever I can, especially with SharePoint Online.

Add an attachmentI ran into a challenge figuring out how to make an attachment upload work in an AngularJS project. There are dozens of blog posts and articles out there about uploading files to SharePoint, and some of them even mention attachments. But I found that not a single one of the worked for me. It was driving me nuts. I could upload a file, but it was corrupt when it got there. Images didn’t look like images, Excel couldn’t open XLSX files, etc.

I reached out to Julie Turner (@jfj1997) and asked for help, but as is often the case, when you’re not as immersed in something it’s tough to get the context right. She gave me some excellent pointers, but I ended up traversing some of the same unfruitful ground with her.

Finally I decided to break things down into the simplest example possible, much like I did in my earlier post with SOAP. I created a test page called UploadTEST with a Content Editor Web Part pointing to UploadText.html, below:

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.js"></script>

<input id="my-attachments" type="file" fileread="run.AttachmentData" fileinfo="run.AttachmentInfo" />

<script type="text/javascript" src="/sites/MySiteCollection/_catalogs/masterpage/_MyProject/js/UploadTEST.js"></script>

Like I said, I wanted to keep it simple. Here’s what I’m doing:

  • In line 1, I load a recent version of jQuery from cdnjs. On the theory that Angular just adds another layer of complexity, I wanted to try just jQuery, which is useful for its $.ajax function, among many other things.
  • In line 3, I’ve got an input field with type=”file”. In “modern” browsers, this gives us a familiar file picker.
  • In line 5, I’m loading my script file called UploadTest.js, below:
$(document).ready(function() {

 var ID = 1;
 var listname = "UploadTEST";

 $("#my-attachments").change(function() {

  var file = $(this)[0].files[0];

  var getFileBuffer = function(file) {

   var deferred = $.Deferred();
   var reader = new FileReader();

   reader.onload = function(e) {
    deferred.resolve(e.target.result);
   }

   reader.onerror = function(e) {
    deferred.reject(e.target.error);
   }

   reader.readAsArrayBuffer(file);

   return deferred.promise();
  };

  getFileBuffer(file).then(function(buffer) {

   $.ajax({
    url: _spPageContextInfo.webAbsoluteUrl +
     "/_api/web/lists/getbytitle('" + listname + "')/items(" + ID + ")/AttachmentFiles/add(FileName='" + file.name + "')",
    method: 'POST',
    data: buffer,
    processData: false,
    headers: {
     "Accept": "application/json; odata=verbose",
     "content-type": "application/json; odata=verbose",
     "X-RequestDigest": document.getElementById("__REQUESTDIGEST").value,
     "content-length": buffer.byteLength
    }
   });

  });

 });
});

Here’s how this works – and yes, it does work!

  • I’ve got everything wrapped in a $(document).ready() to ensure the page is fully loaded before my script runs.
  • Lines 3-4 just set up some variables I could use for testing to keep things simple.
  • In line 6, I bind to the change event for the input field. Whenever the user chooses a file in the dialog, this event will fire.
  • In line 8, I’m getting the information about the file selected from the input element.
  • Lines 10-26 is the same getFileBuffer function I used with SOAP; it’s where we use a FileReader to get the contents of the selected file into a buffer.
  • The function in lines 28-42 runs when the file contents have been fully read. The getFileBuffer function returns a promise, and that promise is resolved when the function has gotten all of the file contents. With a large file, this could take a little while, and by using a promise we avoid getting ahead of ourselves. Here we make the REST call that uploads the attachment.
    • The URL ends up looking something like: “/my/site/path/_api/web/lists/getbytitle(‘UploadTEST’)/items(1)/AttachmentFiles/add(FileName=’boo.txt’)”
    • The method is a POST because we’re writing data to SharePoint
    • The data parameter contains the “payload”, which is the buffer returned from the getFileBuffer function.
    • The headers basically tell the server what we’re up to:
      • Accept doesn’t really come into play here unless we get a result back, but it says “send me back json and be verbose”
      • content-type is similar, but tells the server what it is getting from us
      • X-RequestDigest is the magic string from the page that tells the server we are who it thinks we are
      • content-length tells the server how many bytes it should expect

This worked for me on a basic Custom List (UploadTEST). So now I knew it was possible to upload an attachment using REST.

I took my logic and shoved it back into my AngularJS page, and it still worked! However, when I switched from $.ajax to AngularJS’s $http, I was back to corrupt files again. I’m still not sure why that is the case, but since I’m loading jQuery for some other things, anyway, I’ve decided to stick with $.ajax for now instead. If anyone reading this has an idea why $http would cause a problem, I’d love to hear about it.

Once again, I’m hoping my beating my head against this saves some folks some time. Ideally there would be actual documentation for the REST endpoints that would explain how to do things. Unfortunately, if there is for this, I can’t find it. I’d also love to know if /add allows any other parameters, particularly whether I can overwrote the attachment if one of the same name is already there. Maybe another day.

Caching SharePoint Data Locally with SPServices and HTML5’s Web Storage

The SharePoint SOAP Web Services are fast. In fact, I think they are as fast as the newer REST Web Services in many cases. The old, crufty SOAP Web Services even provide batching, something that the REST services don’t yet do. (Andrew Connell (@andrewconnell) has been beating the drum about this with Microsoft for months now, and we all hope they get this OData capability into SharePoint’s REST services sooner rather than later.)

Even though the SOAP services are fast, sometimes they just aren’t fast enough. In some of those cases, it may make sense to store some of your data in the browser’s Web storage so that it’s there on the client during a session or across sessions. Web storage is an HTML5 capability that is available in virtually all browsers these days, even Internet Explorer 8.

The best candidates for this type of storage (IMO) are list contents that are used as references and that don’t have a high number of changes. As an example, you might decide to store a list of countries in Web storage rather than loading them from the Countries list every time a page loads. Even though the read from the list is fast, it has to take *some* time. There’s the retrieval time, and then there is also any processing time on the client side. For instance, if you have dozens of fields per country and you need to load them into a complex JavaScript structure, that takes time, too. If those data chores are making your page loads seem laggy, then consider using local storage.

There are three main ways you can store data locally to improve performance. I’m not going to go into all of their intricacies, but I will give you some rules of thumb. There are a lot of nuances to this, so before you dive in, do some studying about how it all works.

Cookies

For small pieces of data, you should consider using cookies. Contrary to just about every article out there in the press, cookies are not bad. They can store up to 4k of data each for you, which you can read back when the user returns to the page. There’s a excellent little jQuery plugin I use to facilitate this called, aptly, jquery-cookie. You can download it (for free!) from GitHub here. Cookies persist across sessions.

Session Storage

Session storage is the flavor of Web storage that allows you to store data just for the duration of the session. Think of a session as a browser lifespan. Once you close the browser, the session storage is gone. Both session storage and local storage sizes are limited by the browser you are using. If you want to know if Web storage is available in your browser of choice, take a look at “Can I use“. The amount of storage each browser gives you is a moving target, but it’s per domain.

Local Storage

Local storage takes Web storage one step further. The data stored in local storage persists across browser sessions. In fact, it usually won’t go away until you explicitly delete it. (Consider this fact when you are gobbling up local storage in your development process.)

So What?

The trick with using these storage mechanisms is managing the data you’ve put in local storage as a cache. That data can go past its expiration date, either because some changes were made to the underlying data source or the cache itself has become corrupted. The latter is more difficult to deal with, so here I’ll focus on the former.

JavaScript – like most other programming languages – lends itself to building wrapper functions that add additional layers of abstraction on top of underlying functionality. Too many levels of abstraction can make things confusing, but with careful thought and smart code writing, you can build abstractions that serve you well.

In a recent client project, I found that as list data volumes were increasing, the pages in my SPServices- and KnockoutJS-driven application were loading more and more slowly. I’m building on top of SharePoint 2007 in this case, so even if I wanted to use REST, I couldn’t, nor do I believe that it would automatically make anything faster. If we had better servers running things, that might make a huge difference, but we have no control over that in the environment.

What I wanted was a reusable wrapper around SPGetListItemsJson (which itself is a wrapper around the SOAP List Web Service’s GetListItemChangesSinceToken and SPService’s SPXmlToJson) that would let me check local storage for a cached data source (list data), read either the entire data source or just the deltas from the SharePoint list, load the data into my application, and then update the cache appropriately.

The getDataSource function below is what I’ve come up with so far. There’s some setup to use it, so let me explain the parameters it takes:

  • ns – This is the namespace into which you want to load the data. In my applications these days, following the lead from the patterns Andrew and Scot Hillier (@scothillier) have published, I usually have a namespace defined that looks something like ProjectName.SubProjectName.DataSources. The “namespace” is simply a complex JavaScript object that contains most of my data and functions.
  • dataSourceName – The name that I want to give the specific data source within ns. In my example above with the Countries list I would use “Countries”.
  • params – This is the big magilla of the parameters. It contains all of the values that will make my call to SPGetListItemsJson work.
  • cacheItemName – This is the name of the item I want to store in Web storage. In the Countries example, I would use “ProjectName.SubProjectName.DataSources.Countries”.
  • storageType – Either “localStorage” or “sessionStorage”. If I expect the data to change regularly, I’d probably use sessionStorage (this gives me a clean data load for each session). If the data is highly static, I’d likely use localStorage.

And here’s the code:

/* Example:
getDataSource(ProjectName.SubProjectName.DataSources, "Countries", params: {
  webURL: "/",
  listName: "Countries",
  CAMLViewFields: "<ViewFields>" +
      "<FieldRef Name='ID'/>" +
      "<FieldRef Name='Title'/>" +
      "<FieldRef Name='Population'/>" +
      "<FieldRef Name='CapitalCity'/>" +
      "<FieldRef Name='Continent'/>" +
    "</ViewFields>",
  CAMLQuery: "<Query>" +
      "<OrderBy><FieldRef Name='ID'/></OrderBy>" +
    "</Query>",
  CAMLRowLimit: 0,
  changeToken: oldToken,
  mapping: {
      ows_ID:{"mappedName":"ID","objectType":"Counter"},
      ows_Title:{"mappedName":"Title","objectType":"Text"},
      ows_Population:{"mappedName":"Population","objectType":"Integer"},
      ows_CapitalCity:{"mappedName":"CapitalCity","objectType":"Text"},
      ows_Continent:{"mappedName":"Continent","objectType":"Lookup"},
    }
  }, "ProjectName.SubProjectName.DataSources.Countries"
)
*/

function getDataSource(ns, dataSourceName, params, cacheItemName, storageType) {

  var dataReady = $.Deferred();

  // Get the data from the cache if it's there
  ns[dataSourceName] = JSON.parse(window[storageType].getItem(cacheItemName)) || new DataSource();
  var oldToken = ns[dataSourceName].changeToken;
  params.changeToken = oldToken;

  // Read whatever we need from the dataSource
  var p = $().SPServices.SPGetListItemsJson(params);

  // Process the response
  p.done(function() {
    var updates = this.data;
    var deletedIds = this.deletedIds;
    var changeToken = this.changeToken;

    // Handle updates/new items
    if (oldToken !== "" && updates.length > 0) {
      for (var i = 0; i < updates.length; i++) {
        var thisIndex = ns[dataSourceName].data.binaryIndexOf(updates[i], "ID");
        // If the item is in the cache, replace it with the new data
        if (thisIndex > -1) {
          ns[dataSourceName].data[thisIndex] = updates[i];
          // Otherwise, add the new item to the cache
        } else {
          ns[dataSourceName].data.splice(-thisIndex, 0, updates[i]);
        }
      }
    } else if (oldToken === "") {
      ns[dataSourceName] = this;
    }
    // Handle deletes
    for (var i = 0; i < deletedIds.length; i++) {
      var thisIndex = ns[dataSourceName].data.binaryIndexOf({
        ID: deletedIds[i]
      }, "ID");
      ns[dataSourceName].data.splice(thisIndex, 1);
    }
    // Save the updated data back to the cache
    if (oldToken === "" || updates.length > 0 || deletedIds.length > 0) {
      // Save the new changeToken
      ns[dataSourceName].changeToken = changeToken;
      window[storageType].setItem(cacheItemName, JSON.stringify(ns[dataSourceName]));
    }
    dataReady.resolve();
  });
  return dataReady.promise();
}

Some of the nice things about this function:

  • It’s generic. I can call it for any list-based data source in SharePoint. (I started out building it for one data source and then generalized it.)
  • I call call it during a page life cycle to refresh the application data anytime I want or on a schedule, perhaps with setInterval.
  • I can set a lot of parameters to cover a lot of different use cases.
  • Each time I call it, it updates the cache (if it needs to) so that the next time I call it I get a “fresh” copy of the data.
  • It only loads the data that it needs to, by using the GetListItemChangesSinceToken capabilities.

And some downsides:

  • Since I know what data I’m working with in my application and that it will fit into the Web storage easily, I’m not worrying about failed saves.
  • If the cache does become corrupt (not something I expect, but there’s always Murphy), I’m not handling it at all.

If you decide to try this out, you’ll need a few auxiliary functions as well:

/* DataSource constructor */
function DataSource() {
  this.changeToken = "";
  this.mapping = {};
  this.data = [];
  this.deletedIds = [];
}

/** Adapted from http://oli.me.uk/2013/06/08/searching-javascript-arrays-with-a-binary-search/
 *
 * Performs a binary search on the host array.
 * @param {*} searchObj The object to search for within the array.
 * @param {*} searchElement The element in the object to compare. The objects in the array must be sorted by this element.
 * @return {Number} The index of the element. If the item is not found, the function returns a negative index where it should be inserted (if desired).
 */
Array.prototype.binaryIndexOf = function(searchObj, searchElement) {

  var minIndex = 0;
  var maxIndex = this.length - 1;
  var currentIndex;
  var currentElement;

  var searchValue = searchObj[searchElement];

  while (minIndex <= maxIndex) {
    currentIndex = (minIndex + maxIndex) / 2 | 0;
    currentElement = this[currentIndex];

    if (currentElement[searchElement] < searchValue) {
      minIndex = currentIndex + 1;
    } else if (currentElement[searchElement] > searchValue) {
      maxIndex = currentIndex - 1;
    } else {
      return currentIndex;
    }
  }

  return ~maxIndex;
}

Uploading Attachments to SharePoint Lists Using SPServices

Easy!For years people have been asking me how they could upload files using SPServices. I’ve dodged the questions every time because I simply didn’t know how.

On a current client project,  I’m building a slick Single Page Application (SPA) to manage tasks in SharePoint 2010. It’s basically a veneer over the clunky out-of-the-box task list experience. Rather than hopping from page to page, the team using the application can accomplish everything they need to do on a single page.

Every project has specific needs, and for this one I’m using KnockoutJS. But the code snippets I give below are generic enough that you should be able to use them in any context with a little thinking.

It’s been going well, but I had been putting off implementing adding attachments because…well, as I said, I didn’t know how.

One of the benefits of the shift from all server side development to a much more significant focus on client side techniques is that some of the bright minds in the SharePoint development community have turned their eyes toward solving these things, too. Usually it’s on SharePoint 2013 or SharePoint Online at Office 365. However, at least now when I search for “SharePoint JavaScript xxx”, there are likely to be some great hits I can learn from.

In this case, there were two excellent posts, one from James Glading and one from Scot Hillier. (See the Resources section below for links.)

The first step is to enable HTML5 capabilities in IE10. This requires two small changes to the master page. This seems simple, but it can have effects that you don’t expect. In other words, don’t consider this just a “no brainer”. You need to plan for the change across your Site Collection and any impacts it may have.

The first change is to switch from using “XHTML 1.0” to “HTML” as the DOCTYPE. This is what “turns on” HTML5.

<!DOCTYPE html>

Then, in this project we set the content meta tag to IE=10 because we want to aim for IE10.

<meta http-equiv="X-UA-Compatible" content="IE=10"/>

If your browser base is different, you can, of course, set this differently. In this project, we will require all users of the application to be running IE10 or greater. Firefox and Chrome have supported the bits of HTML5 we need for so long, that there’s little concern about people who choose to use those browsers.

Once we have those two small changes in the master page (we are using a copy of v4.master with no other customizations at the moment), we “have HTML5”. It’s most obvious because some of the branding flourishes I’ve put in like rounded corners show up in IE10 now, rather than just square boxes.

Once we have HTML5 enabled, we can start to use the File API, a.k.a. the FileReader. This is such a simple little thing, that it’s hard to believe it gives us the capability it does.

<input type="file" id="attachment-file-name"/>

That’s it. That simple HTML gives us a file picker on the page. Depending on your browser, it will look something like this:

File Picker

When you make a file selection with this very familiar widget, you can query the element using jQuery.

var file = $("#attachment-file-name").files[0];

Note that we’re after a single file, so we grab the first object in the file list array. We get an object that looks something like this screen shot from Firebug:

File object from the File Picker

Once we have the file object, we can look at what the Lists SOAP Web Service needs to get in order to upload the file. Again, it’s pretty simple. Here is the list of inputs and output from the MSDN documentation page for the Lists.AddAttachment Method.

Parameters

listName
A string that contains either the title or the GUID for the list.
listItemID
A string that contains the ID of the item to which attachments are added. This value does not correspond to the index of the item within the collection of list items.
fileName
A string that contains the name of the file to add as an attachment.
attachment
A byte array that contains the file to attach by using base-64 encoding.

Return Value

A string that contains the URL for the attachment, which can subsequently be used to reference the attachment.

The first three inputs are straightforward. We pass in the name of the SharePoint list, the ID of the list item, and the name of the file we want to attach. It’s that last input parameter called “attachment” that’s a bit tricky.

When we upload a file via an HTTP POST operation – which is what all of the SOAP Web Services use – we have to pass text. But the files we want to upload are as likely as not to be binary files. That’s where the Base64 format comes in. Believe me, you don’t need to know exactly what the format actually is, but you should probably understand that it enables us to send binary files as text bytes. Those bytes are then decoded on the server end so that the file can be stored in all of its original, pristine glory.

Here’s the code I ended up with, skinnied down as much as possible to make it a little clearer to follow. I draw heavily from Scot and James’ posts for this, but nuanced for SPServices. I’ve also stripped out all of the error handling, etc.

  • First, the getFileBuffer function reads the contents of the file into the FileReader buffer. readAsArrayBuffer is an asynchronous method, so we use a jQuery Deferred (promise) to inform the calling function that the processing is done.
  • The contents of the buffer are then converted from an ArrayBuffer – which is what the FileReader gives us – into a Base64EncodedByteArray. This works by passing the buffer through a Uint8Array along the way.
  • Finally, we use the toBase64String method to convert the SP.Base64EncodedByteArray to a Base64String.

Yeah, that’s a lot to swallow, but again, you don’t really need to understand how it works. You just need to know that it does work.

Finally, we call Lists.AddAttachment using SPServices to do the actual upload.

/* From Scot Hillier's post:
http://www.shillier.com/archive/2013/03/26/uploading-files-in-sharepoint-2013-using-csom-and-rest.aspx */
var getFileBuffer = function(file) {

  var deferred = $.Deferred();
  var reader = new FileReader();

  reader.onload = function(e) {
    deferred.resolve(e.target.result);
  }

  reader.onerror = function(e) {
    deferred.reject(e.target.error);
  }

  reader.readAsArrayBuffer(file);

  return deferred.promise();
};

getFileBuffer(file).then(function(buffer) {
  var bytes = new Uint8Array(buffer);
  var content = new SP.Base64EncodedByteArray(); //base64 encoding
  for (var b = 0; b < bytes.length; b++) {
    content.append(bytes[b]);
  }

  $().SPServices({
    operation: "AddAttachment",
    listName: "Tasks",
    listItemID: taskID,
    fileName: file.name,
    attachment: content.toBase64String()
  });

});

Very cool! And as I tweeted yesterday, far easier than I ever would have expected. Yes, it took me a good chunk of a day to figure out, but it definitely works, and pretty fast, too.

If you use this example as a base, you could fairly easily build out some other file uploading functions. Combined with the other attachment-oriented methods in the Lists Web Services, you can also build the other bits of the attachment experience:

  • GetAttachmentCollection – Returns a list of the lit item’s attachments, providing the full path to each that you can use in a list of links.
  • DeleteAttachment – Once you’ve uploaded an attachment and realized it was the wrong one, this method will allow you to delete it.

Moral of the story: Fear not what you do not know. Figure it out.

Resources

What’s the Story for HTML5 with SharePoint 2010? by Joe Li

Uploading Files Using the REST API and Client Side Techniques by James Glading

Uploading Files in SharePoint 2013 using CSOM and REST by Scot Hillier

Lists.AddAttachment Method

This article was also published on IT Unity on Jun 02, 2014. Visit the post there to read additional comments.

Update 2014-05-28 11:55 GMT-5

Hugh Wood (@HughAJWood) took one look at my code above and gave me some optimizations. Hugh could optimize just about anything; he is *very* good at it. I *think* I can even see the difference with larger files.

getFileBuffer(file).then(function(buffer) {
  var binary = "";
  var bytes = new Uint8Array(buffer);
  var i = bytes.byteLength;
  while (i--) {
    binary = String.fromCharCode(bytes[i]) + binary;
  }
  $().SPServices({
    operation: "AddAttachment",
    listName: "Tasks",
    listItemID: taskID,
    fileName: file.name,
    attachment: btoa(binary)
  });
});

Making Sense of HTML5 with SharePoint: Internet Explorer 9 Capabilities

Let’s take as a sort of given that the majority of people who use SharePoint, at least for Intranets, are using Internet Explorer. I know that’s a big assumption, and in most organizations it’s not fully true. Many people choose to go rogue from the standards and use other browsers, most front end developers refuse to use IE, etc.

So why make the assumption, you might ask? Well, I want to focus first on the HTML5 capabilities that organizations that use SharePoint can reasonably expect to use. Knowing that the majority of real users (maybe not the people reading this article) use IE as their browser means that our palette is somewhat limited. The fact that many users out there in the real world are still using IE8, IE7, or yes, even IE6 makes it even worse.

So let’s go with the using IE assumption, but allow that the version is IE9. Without that, this HTML5 stuff is all pipe dreams and smoke. When I I use the IE Developer Tools to set IE9 to IE7 or IE8 modes, I get a measly 42 / 500 points at the HTML5Test site. It’s hardly worth bothering with HTML5 goodness at that point. The HTML5Please site takes a different slant and gives a good,. clear overview of the capabilities of the different levels of IE. Without IE9, not so much. When can I use… shows every single HTML5 capability in tables for each browser level. The point is, there’s lots of information out there about what we should use, use with assistance like shims or shivs, use with fallbacks, or avoid. There’s no real excuse for developing something that won’t work with a known browser base.

So let’s look at the major cool HTML5 stuff that we can do if our users are on IE9, taking the majority of the information from looking across the sites mentioned above. I captured the images (yes, they are just images – no interactivity) from the HTML5Rocks Playground.

Elements

HTML5 Elements

Elements give us ways to prepare our content to live out on the Semantic Web. For us SharePointilists building internally focused applications it may be of less interest, but over time as content gets wrapped in these elements, we’ll be able to more easily reuse and redisplay content in different contexts.

Canvas

HTML5 Canvas

Canvas may be the HTML5 capability that has the most people excited. Canvas gives us a container into which we can add graphics on the fly using script. ‘Nuff sed on this one for now. I’ll show some cool demos later.

Video

HTML5 Video

Everyone loves a good video player, and HTML5 makes it oh-so-easy. Well, sort of. There are some odd inconsistencies due to the different standard which different browsers support, but remember that we’re focused on IE here, at least for now. We get a nice video player without any plug-ins or add-ons. See HTML5Video.org for demos and examples.

Audio

HTML5 Audio

Similar to the video player, we get an easy to use audio player. It’s basically the same thing from the user standpoint – it looks like the video player, but without the video part – but the way we set things up is a little bit different (but still really easy).

Now What?

So, that looks like a pretty short list, doesn’t it? Unfortunately, it is. The more I dig into this HTML5 stuff, the more I feel that while HTML5 as a standard contains a lot of great stuff, the browsers just aren’t there yet. Or they are too inconsistent for us to reliably build HTML5-based capabilities without using a lot of shimming.

But it’s not all bad. In the next set of articles in the series, I’ll show you how to take advantage of the capabilities above and then get into some of the others that you can use with the appropriate shims and fallbacks.

This post also appeared at NothingButSharePoint.com on 2012-04-27. Visit the post there to read additional comments.

Making Sense of HTML5 with SharePoint: Hello World

If you’ve been following along with this series, you’ve probably been thinking “Great, but what can I do with HTML5 in SharePoint?” In other words ‘What’s in it for me?” Let’s take a look at a simple example.

First of all, as I mentioned in the last article, we need to make some modifications to the SharePoint master page so that we can take advantage of the HTML5 goodness. It actually gets a bit more complicated than just changing the doctype and a meta tag. Luckily, SharePoint design guru Kyle Schaeffer has done the hard work for us and has published v5, the Responsive HTML5 Master Page for SharePoint 2010. For this article (and probably many following articles), I’m going to use Kyle’s v5 master page to help leapfrog us into HTML5-land. You should be able to follow Kyle’s instructions pretty easily to get v5.master up and running in your environment. Remember that you should only use it in a test environment for this exercise, as there are many reasons why using the v5 master may not be a good idea for your production sites today.

Once you get v5.master installed, the home page of your site ought to look something like this:

image

I’m keeping the look that Kyle’s given us in v5.master as is because it’s distinctive looking and we can easily tell that we’re doing something different. In addition to getting a great HTML5 setup, v5.master also gives you a responsive design layout. As you start to think about how HTML5 and CSS3 might help you to provide SharePoint support for different screen sizes, responsive design is one approach you might consider. (We should really pause here to give Kyle a big hand; this is truly great work!)

So, we’ve got v5.master in place and we can now take advantage of HTML5. (I feel a little like Julia Child when she says “I just happen to have a completed soufflé right here in this other oven.”) In thinking about what a “Hello World” for HTML5 and SharePoint might be, I struggled a little. Regardless what I chose, there had to be some up front work (borrowing Kyle’s v5.master page gets me past that) and putting the words “Hello World” on the page doesn’t show any real HTML5 goodness. I think I’ve settled on something simple enough, yet useful enough, to prove that HTML5 can really make your pages sing, and more easily than using other methods.

One of the things we get with HTML5 is some new attributes for input elements. Input elements are the things we use in HTML to get input from the user. So, for instance, the Title column in any SharePoint list is rendered on the list forms as an input element that looks like this:

<input name="ctl00$m$g_3f366f7e_f974_479a_afcf_552d2de2688e$ctl00$ctl05$ctl00$ctl00$ctl00$ctl04$ctl00$ctl00$TextField" title="Title" class="ms-long ms-spellcheck-true" id="ctl00_m_g_3f366f7e_f974_479a_afcf_552d2de2688e_ctl00_ctl05_ctl00_ctl00_ctl00_ctl04_ctl00_ctl00_TextField" type="text" maxLength="255"/>

Once you get past the really long (and ugly) name and id attributes, the input element is pretty simple. It has a title attribute that contains the name of the column (‘Title”), a few CSS classes (“ms-long ms-spellcheck-true”), a type (“text”), and a maxLength (“255”). As far as HTML elements go, it’s about as straightforward as it gets.

Depending on your branding, the input element will render looking something like this. We’ve all seen it before.

Now, we’ve all visited sites where there is a helpful prompt inside the input element that tells us what we should plan to type into it.

It’s a great little mechanism to help a user to understand what they need to do next. We’ve all become very used to it and while we may not notice it after a while, it can lend a great assist. The nice thing about it is that the prompt text disappears when we put our cursor into the input element and it only reappears if we leave the element without typing a value.

SharePoint doesn’t give us this behavior out of the box, but we can make it happen with a little script.

var titleInput = $("input[title='Title']");
var promptText = "Please type a title for this item";

// When we click into the element, remove the prompt text if it is there
titleInput.click(function() {
  if($(this).val() == promptText) $(this).val("");
});

// When we leave the element, replace the prompt text if there is no value
titleInput.blur(function() {
  if($(this).val() == "") $(this).val(promptText);
});

// Set the initial prompt text if there's no value by triggering the blur event
titleInput.blur();

That’s not too complicated, yet it’s more complicated than it seems like it ought to be. I didn’t have an example lying around, so I decided to write it from scratch, which took me about ten minutes. I needed to think about the edge conditions (what if there’s a value but it’s not the promptText?)

So what does HTML5 gain us that makes this easier? Well, there’s a new attribute for the input element called “placeholder”. To get the behavior that I wanted above, I can simply do this:

$("input[title='Title']").prop("placeholder", "Please type a title for this item");

Now you may be saying “Wait a minute. You’re still writing script!” Indeed I am, but it’s only one line of script. The only reason I need to use script at all is that SharePoint doesn’t “know” anything about the placeholder attribute. (I could write managed code to modify the Title column across the board, but that would be tremendous overkill and may not even be what we want.) All the script is doing is adding the placeholder attribute to the input element, and HTML5 takes care of the rest and we get this in the active DOM:

<input name="ctl00$m$g_3f366f7e_f974_479a_afcf_552d2de2688e$ctl00$ctl05$ctl00$ctl00$ctl00$ctl04$ctl00$ctl00$TextField" title="Title" class="ms-long ms-spellcheck-true" id="ctl00_m_g_3f366f7e_f974_479a_afcf_552d2de2688e_ctl00_ctl05_ctl00_ctl00_ctl00_ctl04_ctl00_ctl00_TextField" type="text" maxLength="255" placeholder= "Please type a title for this item"/>

Well, HTML5 doesn’t actually do anything, but it tells the browser that it should do the prompt text thing. Unfortunately, Internet Explorer 9 doesn’t support the placeholder attribute yet (check the HTML5 Test again) so I had to run through this little exercise in Firefox.

And there’s the rub again. Since browsers don’t all behave the same yet, we have to consider which HTML5 capabilities we can use. Kyle’s v5.master loads HTML5 Shiv if the browser is IE < 9, but that doesn’t seem to fix it and I didn’t feel like monkeying with it to get it working in IE. In other words, there are still monsters here for those who are not wary.

So, there you go: a “Hello World” exercise, if you will. The real point (in case I didn’t make it well enough) is that HTML5 gives us easier ways to do some common tasks. In this case it’s a simple prompt in an input element, but there are more complex tasks (think playing videos, and more) that HTML5 will also make much easier. Stay tuned for more…

This post also appeared at NothingButSharePoint.com on 2012-04-18. Visit the post there to read additional comments.