If you do SharePoint development, at some point you may find yourself in a situation where you need to create a custom behavior or interaction using JavaScript for your application. The first time this happened to me, I was quite perplexed because I couldn't get event handlers to work. Somehow SharePoint's JavaScript API was preventing my event handlers from working. Since SharePoint's JavaScript API is large and has no comments or documentation, I was unable to determine what in the API was blocking me. I needed to get some JavaScript working for just one page, so in the end I came up with this workaround.
All of SharePoint's masterpages have placeholders. As it turns out there is one called PlaceHolderAdditionalBody. You can put it anywhere inside the body of a SharePoint aspx to add additional body content for a page.
<asp:Content ContentPlaceHolderId="PlaceHolderAdditionalBody" runat="server">
<!--Your special content here -->;
<asp:Content>
Using this tag in the aspx allows us to inject content on one particular page. My next problem was how to fire an event. I decided to try Simon Willison's excellet addLoadEvent function:
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(myFunction1);
addLoadEvent(myFunction2);
addLoadEvent(myFunction3);
Basically this function uses a JavaScript closure to chain multiple onload events. This is a solution because the window object can have only one onload event. With this function you can queue your onload events. That way you can fire your events after whatever SharePoint already has attached to the winow.onload event.
With that taken care of, you should be aware of other dangers doing custom JavaScript in SharePoint: SharePoint's JavaScript API is quite complex and resembles the JavaScript libraries from 1998. In other words, everything is in the global namespace and there are no objects, just hundreds upon hundreds of functions. If you're going to write custom JavaScript for SharePoint, avoid possible conflicts by creating custom objects. And even better, encapsulate those objects in a namespace. Since JavaScript doesn't have support for true namespaces, you'll need to fake it. This can be done by creating an object for the namespace and then attaching your objects to the parent namespace object. Here's how to do this. For a namespace, we'll use Fabricam, which is a fictional company Microsoft uses for tutorials. To this name space we're going to add four objects: tree, picker, sorter and util, which will each contain the properties and methods which they need. In this way our script encapsulates everything to avoid collisions with SharePoint's API.
For this example, I'm going to use JavaScript Object Literal Notation (JSOLN), which is pronounced like the name "Jocelyn". An easy way to remember is that Jocelyn (JSOLN) is the mother of Jason (JSON).
<script type="text/javascript">
// If Fabricam is not instantiated, instantiate it:
if (typeof Fabricam === "undefined") {
// This is shorthand for: var Fabricam = new Object();
var Fabricam = {};
}
// Attach tree object to Fabricam:
Fabricam.tree = {
list : whatever,
selectList : function () {
var listItems = document.getElementsByTagName("li");
// Do stuff here
},
openNode : function () {
// Do stuff here
},
closeNode : function () {
// Do stuff here
}
};
// Attach picker object to Fabricam:
Fabricam.picker = {
pickerInfo : false,
pickerData : "",
moveData : function () {
var data = document.getElementById("treeStruc");
// Do stuff here
}
};
// Attach sorter object to Fabricam
Fabricam.sorter = {
selectorId : document.getElementByTagName("select"),
// Since on my page I have only one select box,
// I reassign the subscript reference back to the
// variable to avoid having to use array notation
// for a single reference.
selectorId = selectorId[0],
moveUp = function () {
// Do stuff here
},
moveDown = function () {
// Do stuff here
}
};
// Attach util object to Fabricam:
Fabricam.util = {
addLoadEvent : function (func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
};
// Now let's load those methods.
// Remember, you only need to load the methods
// which have to do something at window.onload,
// otherwise you can call them as you normally would.
Fabricam.util.addLoadEvent(Fabricam.tree.selectList);
Fabricam.util.addLoadEvent(Fabricam.tree.openNode);
Fabricam.util.addLoadEvent(Fabricam.tree.closeNode);
Fabricam.util.addLoadEvent(Fabricam.picker.moveData);
// etc.
</script>
Following a format like that above will allow you to add sophisticated and professional JavaScript customization to SharePoint.
As a side note, in the above example I used JSOLN (JavaScript Object Literal Notation) to create my objects. In this particular case I just wanted to add some functionality for some widgets. I therefore only needed to instantiate one object. An Object Literal is basically a singleton pattern where all you want is a one-off. If you are going to need to instantiate multiple instances of an object, you would want to modify my approach above to suit your purposes.