Last week one of my customers wanted me to implement drag and drop to update a status field for one of their lists. So I found the Andrew Burgess’ article on how to implement drag and drop.
Creating the page
Table of Contents
The list that we looked at contained sales opportunities that moved through stages such a New Opportunity, Win, Unsuccessful and Work In Progress.

To create the list and tabs I’ve created a page with two web parts. The tabs are specified in a Content Editor Webpart and a list view web part is positioned straight underneath these tabs.
The html for the tabs is quite simple:
<div class="post-tabs">
<ul class="tabs-nav">
<li class="current">
< a href="./Recent Wins.aspx"> Recent Wins< /a>
</li>
<li class=""><a href="./Decision Pending.aspx"> Decisions Pending< /a> </li>
<li class=""><a href="./Action Required.aspx"> Work in Progress</a> </li>
<li class=""><a href="./NewOpportunities.aspx"> New Opportunities</a> </li>
<li class=""><a href="./Unsuccessful No Bid.aspx"> Unsuccessful</a> </li>
<li class=""><a href="./No Bid.aspx"> No Bid< /a> < /li>
</ul>
< /div>
For this post I’m going to ignore the CSS needed to make the tabs look like tabs as I’m focusing on the drag and drop functionality.
To implement drag and drop I’m going to add some JavaScript to the Content Editor Webpart.
Drop JavaScript
Looking at the above html we can now use the following JavaScript to select the tabs and make them available for dropping items into.
$('ul.tabs-nav li')
Drag JavaScript
Then within the DOM Explorer in IE I found that my listitems have a class of ms-draggable available. Nice and easy.

So the following JavaScript will get me the items in my list.
$(‘.ms-draggable’)
Binding the Events
I’m now creating a JavaScript function AttachDragDrop to make the drag and drop work.
To make the title of the item draggable, I don’t really need to do anything as Microsoft have already made the titles draggable by default. However I would like to collect the id of the list item and therefore I’m binding the dragstart event with the following lines.
$('.ms-draggable')
.bind('dragstart', function (evt) {
evt.dataTransfer = evt.originalEvent.dataTransfer;
evt.dataTransfer.setData('text', this.parentElement.id);
});
Note that this.parentElement.id gets me to the id of the div around the link. So in the above example in the DOM Explorer this is giving me 4159. Thanks you Microsoft for making this easy!
So now every time I’m dragging a title of an item I’m getting some data set with the list items id, which I can then use during the drop event.
Now I’m ready to implement the drop:
$('ul.tabs-nav li')
.bind('dragover', function (evt) {
evt.preventDefault();
})
.bind('dragenter', function (evt) {
evt.preventDefault();
})
.bind('drop', function (evt) {
// Here I’m going to implement the drop code.
}
Updating the list items
First I want to get the list item id that we stored during the dragstart event.
var id = evt.originalEvent.dataTransfer.getData(‘text’)
As I’ve got multiple tabs that I may drag my items into I need to identify which one I’ve dragged the item to.
var newStatus = this.textContent;
So now my newStatus will contain the statuses that I’ve implemented in my tabs.
So all I’ve now got left to do is update the item in SharePoint and then hide the item from my list once the item has been updated successfully.
So first I’m building my client context.
var siteUrl = "https:/ /mytenant.sharepoint.com/mylist";
var clientContext = new SP.ClientContext(siteUrl);
then I’ll get my list:
var oList = clientContext.get_web().get_lists().getByTitle('Pipeline');
Then I get the item that I need to update
this.oListItem = oList.getItemById(id);
Wow, this is easy!
Now I need to internal name of my choice field that I need to update. So I’m going to my list settings and I’m finding the internal field name in the Url:

Ahhh, people have been using spaces when creating the field. Always remember to create fields without spaces first and then rename the fields. In this case I’m using the spaced out field name and replace the %5F with and underscore ( _ ) resulting in Pipeline_x0020_Stage.
Then I checked out the choice values and unfortunately they don’t match my tabs.

No worries, I simple create a bit of JavaScript that matches my tabs with values in the list column and then update the list item followed by an Async call.
switch(newStatus) {
case "No Bid": this.oListItem.set_item('Pipeline_x0020_Stage', 'J - No Bid');
break;
case "Unsuccessful": this.oListItem.set_item('Pipeline_x0020_Stage', 'K - Unsuccessful');
break;
case "Decisions Pending": this.oListItem.set_item('Pipeline_x0020_Stage', 'F – Solution evaluation');
break;
case "Work in Progress": this.oListItem.set_item('Pipeline_x0020_Stage', 'E – Propose Solution');
break;
case "New Opportunity": this.oListItem.set_item('Pipeline_x0020_Stage', 'A - New Opportunity');
break;
default: break;
}
this.oListItem.update();
$("#" + id).parent().parent().hide();
clientContext.executeQueryAsync(onQuerySucceeded, onQueryFailed);
});
Then for completeness of this article I’m including an onQuerySucceeded and onQueryFaileded, where I’m returning some information to the console of the browser.
function onQuerySucceeded() {
console.log(‘Item updated!’);
}
function onQueryFailed(sender, args) {
console.log(‘Request failed. ‘ + args.get_message() + ‘\n’ + args.get_stackTrace());
}