1,468 articles and 10,109 comments as of Tuesday, March 30th, 2010

Tuesday, February 24, 2009

Anatomy of a Content Editor Web Part: jQuery and AJAX

I want to share my ideas about adding jQuery to your site with the Content Editor Web Part (CEWP). My goals for any project include: respect the user, respect the server, and respect myself.

Respect the User

I started including this segment in all of my new projects:

<script type="text/javascript">
if(typeof jQuery=='undefined'){
	var jQPath = 'http://ajax.googleapis.com/ajax/libs/jquery/1.3/';
	document.write('<script src="',jQPath,'jquery.min.js" type="text/javascript"><\/script>');
}
</script>

By placing the path to jQuery as a variable near the top of the code, Power Users deploying this script will have an easier time modifying it for their environments.

End users should have a similar experience on any platform. While I can’t test every scenario, I always test on IE and FF. IE has the annoying habit of throwing an error every time I use jQuery’s AJAX functions for the first time on a page. After a lot of testing and troubleshooting, I decided that since the script worked, the problem is IE so I suppress the error. Any of my code dealing with AJAX (.get, .load, etc.) uses this snippet:

function handleError() {
	return true;
}
$(function() {
	window.onerror = handleError;
});

I always place that code right after the block that calls jQuery to the page so I can find it and comment it out if I need to troubleshoot a different error.

Users usually want to trigger the AJAX load quickly, but not too quickly. I typically use a 500 millisecond delay. This means that when someone performs the action to start an AJAX load (like mouseenter), I create a timer that counts 500 milliseconds before actually executing the AJAX command. This allows me to cancel that timer when the user performs another action (like mouseleave).

Imagine the shuttle is about to launch in “T-minus 500 milliseconds… 499… 498… then something stops the countdown. The shuttle never launched. The jQuery website has great documentation about each of these event “helpers” and how to use them.

function loadTipEvent(e,a,delay){
	$.data(e,"dispTarget",a);  //set the link target as data on the element
	$(e).mouseenter(function(event){ //create a function for mouseenter
...
//check if the element and the container have the same target
	if($.data(loadTip[0],"dispTarget")&&$.data(loadTip[0],"dispTarget")==$.data(e,"dispTarget")){
...
			loadTip.css({"top":y, "left":x}).show();
		}else{
			if(loadTip.timer)clearTimeout(loadTip.timer); //clear the timer
			loadTip.timer = setTimeout(function(){ //create the timer
				$("#tipContent").load(a+"&Force=1 .ms-formtable", function(){
					loadTip.find("td").removeAttr("width");
...
					loadTip.css({"top":y, "left":x}).show();
					$.data(loadTip[0],"dispTarget",a); //set the target of the container
				});
			},delay); //part of the setTimeout function
		}
	});
	$(e).mouseleave(function(event){ //create the function for the mouseleave event
		if(loadTip.timer)clearTimeout(loadTip.timer); //clear the timer
	});

I use two different ways to display dynamically loaded data. My preview pane code loads the content into the existing page structure where I made space for the content. This works best at the bottom of the page because if the content length varies, the content below the preview pane will “bounce” when you change preview content.

The loadTip code, on the other hand, delivers the content to a “floating” container that moves within the page’s context. With either technique, I try to make the user experience positive by limiting scrolling or clicking to see content.

Respect the Server

Certain capabilities of jQuery can cause significant server load, so we need to consider this during design. This requires two steps: delay (or abort) and reuse.

During a user’s visit to a page, they may move the mouse over several objects or the same object multiple times. If a mouseover triggers the AJAX portion of the script we need to delay the AJAX call to make sure the trigger was intentional and that triggers don’t pile up on the server. Users generally want a shorter delay while the server admins wants a longer delay. While striking this balance, place more weight on the server’s health. Server administrators should know the minimum delay their server will tolerate and make that threshold known to anyone with permissions that allow page editing.

By not loading the same thing multiple times, we can go that extra mile to keep our server happy. In my code, I learned to use jQuery’s $.data method to capture the reference of the last content loaded and reuse it. That way, if someone rolls over the same link or even a different link pointing to the same content, my server does not get another request.

Code that loads content with no delay or no way to cancel the request can run into queuing issues. While testing, you want to mimic the worst user behavior (maniacally rolling and clicking on everything) and see if your code starts queuing–requesting a second page before the first one loads.

The only technique I don’t use here is the XMLHttpRequest .abort() method. I don’t need to abort the request because the average load time on my server is 200 ms and I use a delay of 500 ms. If someone starts a load then moves to another element, the first load completes with ~ 300 ms to spare before another load can trigger. If your content takes longer to load and render, you may want to increase the delay to trigger a load request or use $.ajax so you can abort a request in process.

Respect Myself

Learning new techniques, finding new ideas, writing new solutions, and then testing and documenting everything takes a lot of time. I still have a family, a full-time job, and friends I like to spend time with. So, I started adding a comment block to my code that points to the MIT license.

/*
 * Copyright (c) 2008 Paul Grenier (endusersharepoint.com)
 * Licensed under the MIT (MIT-LICENSE.txt)
 */

I can’t promise to always perform regression testing of old code with new versions of jQuery, mash-up various code snippets, or troubleshoot third-party features (even codeplex ones). I simply don’t have the time. However, I encourage you all to do those things and report your findings–it may be something we want to publish. The spotlight on jQuery is wide, there is room enough for anyone who wants to get involved.

I also respect myself by making the most of my time. My timesavers are: Firefox3, Firebug, IETab, Notepad++, and my organization of previous projects. I keep folders of my “research” notes, the final product (as a webpart and as .js file), and screenshots. Over time, I’ve also developed templates that help jumpstart my projects so I don’t need to search for bits of code I use often. Here’s my current jQuery template I use when I start a new project:

<script type="text/javascript">
//?PageView=Shared&ToolPaneView=2
if(typeof jQuery=="undefined"){
	var jQPath="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/";
	document.write("<script src='",jQPath,"jquery.min.js' type='text/javascript'><\/script>");
}
</script>
<style type="text/css">
<!-- CSS goes here -->
</style>
<script type="text/javascript">
/*
 * Copyright (c) 2008 Paul Grenier (endusersharepoint.com)
 * Licensed under the MIT (MIT-LICENSE.txt)
 */
function handleError(){
	return true;
}

/*
//rerwite grouping function as needed
function ExpGroupRenderData(htmlToRender, groupName, isLoaded) {
	$("#tbod"+groupName+"_").attr("isloaded",isLoaded)
		.html(htmlToRender)
		.show("fast",___callback___);
}
*/

$(function(){
	window.onerror=handleError;
	//document ready code goes here
});
</script>

Paul Grenier

View all entries in this series: PaulGrenier-CEWPAndjQuery»
Entries in this series:
  1. Anatomy of a Content Editor Web Part: jQuery and AJAX
  2. Remove the 'Save' button from a SharePoint survey
 

Please Join the Discussion

13 Responses to “Anatomy of a Content Editor Web Part: jQuery and AJAX”
  1. JoyceMR says:

    Great concepts for quality programming, but my question is because you used as an example exactly what my users asked for today (the delay in responding to the mouseover) which wasn’t part of the sample code from the Super Star webcast. As I am just beginning to learn both javascript and jQuery, could you clarify for me how you set the value for the delay? Am I correct in asumming that “delay” in “loadTipEvent(e,a,delay)” is a variable you set elsewhere in your code? Thanks again for the valuable tools you have provided!

  2. AutoSponge says:

    @JoyceMR

    Thanks for your comments and questions.

    I used the same techniques before (in the original loadTip for Superstar), but I had hard-coded the delay at 200 milliseconds. You can customize the script by altering that number.

    With my recent upgrades, I tried to make more of the code configurable rather than customizable. Refer to the new version of loadTip and the fully documented code: http://www.endusersharepoint.com/?p=1333.

    Find the last function, that’s where I invoke loadTip. I gave 3 examples. Two of the examples use the new default of 500ms while one of them sets the delay to 1 second.

  3. JoyceMR says:

    Thanks so much. I just didn’t know how to identify where you made the setting. Maybe I can use the time you’ve saved me to learn more about the syntax… Thanks again!

  4. TIGOS says:

    Wow…. great job!

    and… thanks for sharing!

  5. Tracy says:

    I’ve been able to use your CQWP/JQuery techniques to facilitate a TON of SharePoint customizations so thanks for sharing!

    I’ve had some recent issues though. The following 2 commands seem to work great when I run them in Firebug, but when I add them into the source code of the CQWP – they don’t work. I’ve tested it in both Firefox and IE, and I can see the code in the CQWP, but it doesn’t render on the pages.

    $(”nobr:contains(’Status’)”).hide();
    $(”#part1>table:eq(2)>tbody>tr:eq(3)”).hide();

    Any ideas these would work in Firebug, but not on the regular page?
    Thanks!

  6. AutoSponge says:

    @Tracy,

    Those look like they should work, but you can always try some other strategies:

    $(”h3:contains(’Status’)”).parents(’tr:first’).hide();

    $(”[title='% Complete']“).parents(’tr:first’).hide();

    Happy debugging!

  7. Deepak says:

    Thank you very much for sharing a good article .

    I am using sharepoint 2007. I want to hide the ‘Save ‘and ‘Cancel’buttons in my survey, when iam pasting the Jquery code in the content editor webpart , its is hiding the whole survey webpart(The Qusetion,Textbtn,NExt,Save,Cancel).

    This is the JQuery i used in the content editor webpart

    if(typeof jQuery=='undefined'){
        var jQPath =
    'http://ajax.googleapis.com/ajax/libs/jquery/1.3/';
        document.write('');
    }
    
    $(function()
    { $(":inputs[value='Save']").hide();
    $(":inputs[value='Cancel']").hide();
    });
    

    Please , assist me , Thank you

  8. AutoSponge says:

    @Deepak,

    It looks like part of your document.write got erased, it should look like this:

    document.write("<script src='",jQPath,"jquery.min.js' type='text/javascript'><\/script>");
    

    For anyone else doing this, I blogged the process I go through here: http://www.endusersharepoint.com/2009/02/24/remove-the-save-button-from-a-sharepoint-survey/

  9. Deepak says:

    Thank you very much for your response ,
    I have seen the video and implemented the exact process .I tried in Firefox browser
    but still it is hiding the entire web part ,This is the exact jQuery script i used

    if(typeof jQuery=='undefined')
    {
        var jQPath ='http://ajax.googleapis.com/ajax/libs/jquery/1.3/';
        document.write("");
    }
    
    $(function()
    { $(":inputs[value='Save']").hide();
    $(":inputs[value='Cancel']").hide();
    
    }
    );
    

    I appreciate your help ,
    Thank you

  10. Deepak says:

    i do not know why its not displaying the document .write (”"”);”), i have included this too …
    Thank you

Trackbacks

Check out what others are saying about this post...
  1. [...] Anatomy of a Content Editor Web Part: jQuery and AJAX [...]

  2. [...] this point, the script displays my standard template for getting jQuery on the page through the CEWP. Of course, if you already have jQuery in your [...]

  3. [...] this point, the script displays my standard template for getting jQuery on the page through the CEWP. Of course, if you already have jQuery in your [...]




Notify me of comments to this article:


Speak and you will be heard.

We check comments hourly.
If you want a pic to show with your comment, go get a gravatar!