Using SharePoint and AJAX to Gather Website Feedback

When building intranet web applications, I recommend making it very easy to allow your users to tell you what they think.  If you are reading this blog, you know how important instant simple feedback is to web sites, their value, and the trust of their community.

 

I’ve been using SharePoint lists for this quite a bit.  I often put a “Click Here to Submit Feedback” link on all the portal pages I build.  However, about six months ago this AJAX rebirth inspired me to make that experience even less intrusive. 

Here is what I did.  I used a SharePoint issue list and some AJAX code and now users can provide feedback without disrupting their thought process. 

One neat thing about this approach is that it uses SharePoint's built in web services which support POST and GET verbs.  When done over an intranet it uses the current user's security context.

 

In a script block in a web page, set up the following variables:

 

var FeedbackHTTP;

var WSSSiteURL="http://server/site"; //do not terminate with a slash

var ListName="Sample Issue List"

var FeedbackTitle="Feedback Sample App";

 

Now, add a link to your web page to launch the feedback form:

 

<a id="linkShowFeedback" href="javascript:showFeedback();" class="Action">+ Provide Feedback</a>

 

Now we’ll need the showFeedback function and the feedback form.  First here is the showFeedback function:

 

function showFeedback()

{

    //This is a fancy dropshadow effect

       sectionFeedback.style.filter="progid:DXImageTransform.Microsoft.DropShadow(OffX=3, OffY=4, Positive='true', enabled=true, Color='Gray')";

      

       linkShowFeedback.style.display="none"; //This hides the "Provide Feedback" link while the user is providing feedback

       sectionFeedback.style.display=""; //This shows the feedback form;

       sectionFeedback.style.position="absolute";

      

       //Now we need to position the form in the middle of the screen so that it appears to float over the page.

       sectionFeedback.style.posLeft=(document.body.offsetWidth/2)-(sectionFeedback.offsetWidth/2);

       if (document.body.offsetHeight>sectionFeedback.offsetHeight)

       {

          sectionFeedback.style.posTop=(document.body.offsetHeight/2)-(sectionFeedback.offsetHeight/2);

       }

       else

       {

           sectionFeedback.style.posTop=5;

       }

       frmFeedback.txtFeedback.select();  //Just to speed things up, we select the feedback textbox so the user can start typing their thought right away.

}

 

Now, we’ll need the form.  Here is the HTML:

 

    <div id="sectionFeedback" class="FeedbackSection" style="display:none">

           <table border="2" class="FeedbackSection">

                  <tr>

                         <td align="center" valign="middle" class="FeedbackSection">

                         <form id="frmFeedback" onsubmit="return(provideFeedback());" onreset="return(CancelFeedback())">

                               Provide a comment, suggestion, issue, or praise:<br>

                               <input type="text" style="width:100%" id="txtFeedback"><br>

                                <input type="submit" value="Submit Feedback" style="FONT-SIZE: 8pt">

                               <input type="reset" value="Cancel" style="font-size:8pt" onclick="CancelFeedback()">

                         </form>

                         </td>

                  </tr>

           </table>

    </div>

 

Then we’ll need the functions for submitting and cancelling:

function CancelFeedback()

{

       sectionFeedback.style.display="none"

       linkShowFeedback.style.display="";

       frmFeedback.txtFeedback.value="";

       return false;

}

 

The function for submitting feedback has the “AJAX” code in it, so it also needs an asynchronous response handler:

 

function provideFeedback()

{

       var xmldoc;

       var batch;

      

       //The batch variable builds up the XML that we are going to submit to the SharePoint list via the SharePoint web service

       batch = "<Batch><Method ID='1' Cmd='New'><Field Name='Description'>"+ frmFeedback.txtFeedback.value +"</Field><Field Name='Title'>" + FeedbackTitle + ", Sample 1: " + SampleForm.txtSample1.value + ", Sample 2: " + SampleForm.txtSample2.value + "</Field></Method></Batch>      ";

       //Make sure you add a "Description" column to the issue list. It isn't there by default.

       xmldoc='<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><UpdateListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/">'

       xmldoc=xmldoc+'<listName>'+ListName+'</listName>';

       xmldoc=xmldoc+'<updates>' + batch +'</updates></UpdateListItems></soap:Body></soap:Envelope>';

       FeedbackHTTP=new ActiveXObject("MSXML2.XMLHTTP.3.0");

       FeedbackHTTP.Open("POST", WSSSiteURL + "/_vti_bin/Lists.asmx?op=UpdateListItems", true);

       FeedbackHTTP.setRequestHeader("Content-Type","text/xml; charset=utf-8");

       FeedbackHTTP.setRequestHeader("SOAPAction","http://schemas.microsoft.com/sharepoint/soap/UpdateListItems");

 

    FeedbackHTTP.onreadystatechange=provideFeedbackResponse;

    FeedbackHTTP.Send(xmldoc);

   

    sectionFeedback.style.display="none" //This hides the feedback form now that the user is done submitting feedback

    linkShowFeedback.style.display="";  //This displays the "Provide feedback" link now that the user is done submitting feedback

    frmFeedback.txtFeedback.value=""; //This clears the feedback text

   

       return false;  //We return false so that the form doesn't attempt to POST or GET

}

 

function provideFeedbackResponse()

{

       if (FeedbackHTTP.readyState==4){

              linkShowFeedback.style.display="";

              var xmlResponse=new ActiveXObject("MSXML2.DOMDocument.3.0");

              xmlResponse.async=false;

              var issueID;

              xmlResponse.loadXML(FeedbackHTTP.responseXML.xml);

             

              //This next bit you don't really need, but it is nice to provide the user with some subtle indication that their response was submitted.

              issueID=xmlResponse.selectSingleNode("/soap:Envelope/soap:Body/UpdateListItemsResponse/UpdateListItemsResult/Results/Result/z:row/@ows_SelectTitle").text;

              showstatus("<a target='_blank' href='"+ WSSSiteURL +"/Lists/" + ListName + "/DispForm.aspx?ID=" + issueID + "'>Feedback submitted</a>");

       }

}

 

You may notice that there is a showstatus function.  That causes a “toast” popup to display with a link to the item the user just submitted.  

In this example we also automatically capture some information about the application’s state and include that in the feedback submission.  In this way, we can automatically gather some context about the user’s situation without having to ask them to relay that information.  In this example, I include it in the description, but you could just as easily break that information out into list columns for sorting, grouping, and analyzing your feedback.  This sample doesn’t include any error handling and only works well on intranets.  One added bonus is that the user’s authentication goes straight through to the SharePoint list so their username is automatically associated with the feedback item.  Here is the whole page’s code:

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head>

<title>Feedback Submission Sample - Using SharePoint</title>

 

<style>

    BODY {background:navy}

    A.Action {text-decoration:none;color:white;filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=5, OffY=5, Color='black', Positive='true',enabled=true)}

       A.Action:HOVER {background:white;color:blue}

       .FeedbackSection {color:white}

       Table.FeedbackSection {background:highlight;border-color:indigo}

</style>

 

<script language="jscript" id="FeedbackScript">

 

var FeedbackHTTP;

var WSSSiteURL="http://server/site"; //do not terminate with a slash

var ListName="Sample Issue List"

var FeedbackTitle="Feedback Sample App";

 

function showFeedback()

{

    //This is a fancy dropshadow effect

       sectionFeedback.style.filter="progid:DXImageTransform.Microsoft.DropShadow(OffX=3, OffY=4, Positive='true', enabled=true, Color='Gray')";

      

       linkShowFeedback.style.display="none"; //This hides the "Provide Feedback" link while the user is providing feedback

       sectionFeedback.style.display=""; //This shows the feedback form;

       sectionFeedback.style.position="absolute";

      

       //Now we need to position the form in the middle of the screen so that it appears to float over the page.

       sectionFeedback.style.posLeft=(document.body.offsetWidth/2)-(sectionFeedback.offsetWidth/2);

       if (document.body.offsetHeight>sectionFeedback.offsetHeight)

       {

          sectionFeedback.style.posTop=(document.body.offsetHeight/2)-(sectionFeedback.offsetHeight/2);

       }

       else

       {

           sectionFeedback.style.posTop=5;

       }

       frmFeedback.txtFeedback.select();  //Just to speed things up, we select the feedback textbox so the user can start typing their thought right away.

}

 

function provideFeedback()

{

       var xmldoc;

       var batch;

      

       //The batch variable builds up the XML that we are going to submit to the SharePoint list via the SharePoint web service

       batch = "<Batch><Method ID='1' Cmd='New'><Field Name='Description'>"+ frmFeedback.txtFeedback.value +"</Field><Field Name='Title'>" + FeedbackTitle + ", Sample 1: " + SampleForm.txtSample1.value + ", Sample 2: " + SampleForm.txtSample2.value + "</Field></Method></Batch>      ";

       //Make sure you add a "Description" column to the issue list. It isn't there by default.

       xmldoc='<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><UpdateListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/">'

       xmldoc=xmldoc+'<listName>'+ListName+'</listName>';

       xmldoc=xmldoc+'<updates>' + batch +'</updates></UpdateListItems></soap:Body></soap:Envelope>';

       FeedbackHTTP=new ActiveXObject("MSXML2.XMLHTTP.3.0");

       FeedbackHTTP.Open("POST", WSSSiteURL + "/_vti_bin/Lists.asmx?op=UpdateListItems", true);

       FeedbackHTTP.setRequestHeader("Content-Type","text/xml; charset=utf-8");

       FeedbackHTTP.setRequestHeader("SOAPAction","http://schemas.microsoft.com/sharepoint/soap/UpdateListItems");

 

    FeedbackHTTP.onreadystatechange=provideFeedbackResponse;

    FeedbackHTTP.Send(xmldoc);

   

    sectionFeedback.style.display="none" //This hides the feedback form now that the user is done submitting feedback

    linkShowFeedback.style.display="";  //This displays the "Provide feedback" link now that the user is done submitting feedback

    frmFeedback.txtFeedback.value=""; //This clears the feedback text

   

       return false;  //We return false so that the form doesn't attempt to POST or GET

}

 

function provideFeedbackResponse()

{

       if (FeedbackHTTP.readyState==4){

              linkShowFeedback.style.display="";

              var xmlResponse=new ActiveXObject("MSXML2.DOMDocument.3.0");

              xmlResponse.async=false;

              var issueID;

              xmlResponse.loadXML(FeedbackHTTP.responseXML.xml);

             

              //This next bit you don't really need, but it is nice to provide the user with some subtle indication that their response was submitted.

              issueID=xmlResponse.selectSingleNode("/soap:Envelope/soap:Body/UpdateListItemsResponse/UpdateListItemsResult/Results/Result/z:row/@ows_SelectTitle").text;

              showstatus("<a target='_blank' href='"+ WSSSiteURL +"/Lists/" + ListName + "/DispForm.aspx?ID=" + issueID + "'>Feedback submitted</a>");

       }

}

 

function showstatus(message)

{

       sectionStatus.style.position="absolute";

       sectionStatus.style.posLeft=document.body.clientWidth-sectionStatus.width-10;

       if (document.body.clientHeight>sectionStatus.height)

           sectionStatus.style.posTop=document.body.clientHeight - sectionStatus.height - 5;

       else

           sectionStatus.style.posTop=document.body.offsetHeight-5;

       sectionStatus.filters[0].Apply();

       sectionStatusBody.innerHTML=message;

       sectionStatus.style.display="";

       sectionStatus.filters[0].Play();

       window.setTimeout("hidestatus()",4500);

}

function hidestatus()

{

       sectionStatus.filters[1].Apply();

       sectionStatus.style.display="none";

       sectionStatus.filters[1].Play();

}

function CancelFeedback()

{

       sectionFeedback.style.display="none"

       linkShowFeedback.style.display="";

       frmFeedback.txtFeedback.value="";

       return false;

}

</script>

</head>

 

<body>

<table bgcolor=ActiveCaption><tr><td>

This page demonstrates how to integrate a unobtrusive user feedback mechanism into your web application that leverages simple Windows SharePoint Services issue lists.  This example uses a basic SharePoint issue list and AJAX programming to allow the user to provide feedback about the application without disrupting their work.

</td></tr></table>

 

<table width="100%">

<tr height="100%">

<td>

    <font color=Menu>Actions</font><br />

    <nobr><a id="linkShowFeedback" href="javascript:showFeedback();" class="Action">+ Provide Feedback</a></nobr>

</td>

<td bgcolor="Window" height="100%">

 

    <div id="sectionFeedback" class="FeedbackSection" style="display:none">

           <table border="2" class="FeedbackSection">

                  <tr>

                         <td align="center" valign="middle" class="FeedbackSection">

                         <form id="frmFeedback" onsubmit="return(provideFeedback());" onreset="return(CancelFeedback())">

                               Provide a comment, suggestion, issue, or praise:<br>

                               <input type="text" style="width:100%" id="txtFeedback"><br>

                               <input type="submit" value="Submit Feedback" style="FONT-SIZE: 8pt">

                               <input type="reset" value="Cancel" style="font-size:8pt" onclick="CancelFeedback()">

                         </form>

                         </td>

                  </tr>

           </table>

    </div>

 

       <table id="sectionStatus" class="StatusSection" width="100" height="100" border="1" bordercolor="ActiveBorder" bgcolor="InactiveCaption" style="display:none;border-style:ridge; filter: progid:DXImageTransform.Microsoft.Blinds(direction='up', bands=1,duration=1) progid:DXImageTransform.Microsoft.Blinds(direction='down', bands=1);">

              <tr>

                     <td align="center">

                     <span id="sectionStatusBody" style="FONT-SIZE: 8pt; FONT-FAMILY: Verdana">

                     </span></td>

              </tr>

       </table>

      

       <form id="SampleForm" name="SampleForm">

       Sample Field 1: <input type=text id="txtSample1" name="txtSample1"/>

       <br />Sample Field 2: <input type=text id="txtSample2" name="txtSample2"/>

       </form>

</td>

</tr>

</table>

 

</body>

 

</html>

 

Note: All code is provided as is with no warranties expressed or implied.

 

 

3 Comments

  • Great article Westin. I think that as AJAX continues to gain popularity, we will see more of it within SharePoint. I took some screenshots after implementing your code, and put them on my blog. Feel free to download them to yours.

  • Hi Westin,

    Having a hard time with the code here, is this sharepoint 2007 ready or do you have a version that is?

    Best Regards
    Patrick


  • Great Tutorial for using AJAX to call SP Webservices. I've been looking for something of this calibur for some time now. I can find great use of this!

Comments have been disabled for this content.