Building social applications with OpenSocial 0.9 JavaScript APIs


The majority of OpenSocial networks implement (or support) version 0.8 or version 0.9 of the specification. While version 1.0 is the upcoming release, version 0.9 will be the main focus of this post as it is the current standard employed by the Yahoo! Application Platform. We will specifically explore the lightweight JavaScript APIs introduced in OpenSocial 0.9, as well as some of the helper methods available.


What Is OpenSocial

OpenSocial is a set of common APIs for working with data from various social networks. The concept behind OpenSocial is to develop once and distribute broadly, meaning that if the OpenSocial specification is used to build out an application, it can be easily and quickly ported to another platform. This concept is important, because when building a social application, you want to reach the largest audience you can. Portability, along with the vast amount of user data that can be pulled, makes OpenSocial an ideal tool for quick and scalable development of social applications.

 

Many networks and companies implement the OpenSocial specification, such as the Yahoo! Application Platform, MySpace, Orkut, and a large number of others. The OpenSocial site offers a full list of networks implementing the OpenSocial specification.


A Few Core Concepts

Before diving further into making OpenSocial JavaScript requests, there are a few core concepts of what we will cover in this spec as well as request methods that should be reviewed.

 

Lightweight JavaScript APIs

With the implementation of OpenSocial 0.9 comes a new set of lightweight JavaScript APIs to help developers by providing simple JSON input/output through simple methods when making data requests to the REST APIs. This new feature significantly reduces the amount of code required to make requests and should reduce ramp-up time for developers. These APIs use a new namespace (osapi) so that the requests will be nicely sandboxed from your older code base.

 

Batching Requests

One of the important aspects to be aware of when making OpenSocial JavaScript requests is request batching. Instead of having to make multiple requests to capture profile information, connection details or updates, a user can tie all of those data fetches into a single AJAX request instead of three. This brings quite a performance boost to your application, for obvious reasons.

 

Additionally, batching requests will ensure that all data is obtained from the request prior to the callback being executed. In Listing 1, we set up a request to capture profile details for the owner of the application (labeled as ownerData) and then another request for the friends of the owner (labeled as ownerFriends).

var batch = osapi.newBatch().
    add("ownerData", osapi.people.getOwner({fields: ['name']})).
    add('ownerFriends', osapi.people.get({userId: '@owner', groupId: '@friends'}));

batch.execute(function(result){
    var ownerName = result.ownerData.name.formatted;
});

Listing 1 – Batching Data Requests

 

We’ll explore these requests in more detail in the following sections.


Capture User Profile Data to Personalize Your Application

Due to the rise of many social networks over the years, one realization about users has become blatantly obvious: the vast majority of people want everyone to know as much about them as they can cram onto their profile. While this is a concern from a privacy and “good sense” perspective, this wealth of knowledge provides the application developer with an extremely large warehouse of data that can be used to personalize applications.

 

This can be anything, from demographically targeting ads, all the way to gender personalization. With all of this information readily available for use, the benefit here is quite clear.

 

Let’s look at this from another angle. One thing that many users have become intolerant of is having to build multiple profiles for every application or social network they use. If you’re using an application on a popular social network like Facebook or MySpace, why would you want to have to type out the exact same information in that application that you’ve already spent a painstaking amount of time entering into your profile? The simple fact is you don’t have to — and that’s why capturing and using the profile information of a user to pre-customize their experience is vital.

 

This task can be accomplished quickly and easily using the getViewer or getOwner methods within OpenSocial (Listing 2).

//opensocial person data request
osapi.people.getViewer({fields: ['name']}).execute(function(result){
    if (!result.error){
        var name = result.name.formatted;
    }
});

Listing 2 – Capturing User Profile Information

 

To create a request to get details about a person, you need to take a few steps (all chained nicely together though). Breaking down the request into its individual parts, we have three distinct sections.

  • osapi.people.getViewer(…) is the method which defines that we want to capture profile information from the viewer. The parameter that we pass into this method is a JSON structure, which defines the fields that we want to return from the profile. In this case, we want to capture the name of the viewer.
  • execute(…) will initialize the viewer data request.
  • the callback, listed as the parameter in the execute function, will be the callback that is hit when the viewer data request completes and will contain the person object returned (result parameter). From this result we can use standard dot notation to get at the data we requested.

 

Every network implements its own list of supported OpenSocial.Person fields, depending on what’s included in their profile system. The full list of OpenSocial 0.9 Person fields is described in the wiki.


Utilize User Connections to Build Your Social Graph

The connections a user creates generally builds out a social graph, which can be used to increase your user base and drive up activity. Connections are a two-way reciprocated link between two users, and when present, means that you are usually able to leverage off profile details of a wider range of people. These concepts work equally well in a follower model such as Twitter; you’re looking for a means of using a physical connection (like a friendship or work tie) to your advantage.

 

Many of us who are present on any of the popular social networks have either participated in (or at least received) annoying status updates from one of the virally popular games such as Mafia Wars, Vampires, or Zombies. Peering into the innards of Mafia Wars, I can see that one of the reasons it has become so virally successful is because of the vast relevant social graph that they’ve built up in their game.

 

The important word here is relevant — something that many networks struggle with at times. Most users of a social platform add many more people other than friends and family — coworkers, the postman, the cousin of the neighbor you say hi to while getting the paper in the morning — and these connections aren’t really what we can call relevant to the user.

 

This means that, for the social container itself, the link between non-relevant users has far less value than one where the users know each other or interact with each other on a regular basis. This same concept is true for your application: When you build the links between the people who use your application, they need to have a way to interact with each other on a regular basis to be of any use to you.

 

Mafia Wars does this by adding your application connections into your “mob,” effectively making them interact with each other in some form whenever they play the game.

 

Much like our previous person request, we can make a simple get request to capture owner friends (Listing 3).

//get owner friends
osapi.people.get({userId: '@owner', groupId: '@friends', count: 10}).execute(function(result){
    if (!result.error){
        var friends = result.list;
        var html = '';
        for (var i = 0; i < friends.length; i++){
            html += friends[i].name.formatted + '';
        }
    }
});

Listing 3 – Capturing User Connections

 

Again, this request can be split up into three individual sections:

  • osapi.people.get(…) is a standard get method that can be used to capture any people data. In this case, we denote within the JSON passed into this function that we would like to capture information from the “owner” of the application where the groupId is “friends”. These are simple strings defining that we would like to capture the owner’s friends.
  • execute(…) will initialize the owner friend data request.
  • the callback, listed as the parameter in the execute function, will be the callback that is hit when the owner friend data request completes and contains all friend person objects for the owner (friends parameter). From this result, we can get the number of friends returned using .totalResults or loop through the list of person objects for each friend.


Promote and Customize Your Application Using Updates

A powerful tool for social application developers is the ability to send updates to the stream of a user. The update stream is the central news area for the user and their connections.

 

Getting Updates

Earlier I mentioned that users will go to great lengths to add in every detail about themselves and their lives into their profiles. What I didn’t mention is that those details might not always be fully accurate, or they may be out of date.

 

The defining truth in any social network is the update stream of the user. When the user sends messages, who they’re contacting, what they’re doing, and what applications they’re using will all become accessible through this stream. This type of request will follow the same syntax as our previous requests but will use the activities API rather than the person API (Listing 4).

//capture viewer activities
osapi.activities.get({userId: '@viewer', count: 20}).execute(function(result){
    if (!result.error){
        var activities = result.list;
        var html = '';

        for (var i = 0; i < activities.length; i++){
            html += 'Activity Title: ' + activities[i].title +
                    'Activity URL: ' + activities[i].url;
        }
    }
});

Listing 4 – Capturing User Activities

 

Within this request we are making a call to osapi.activities.get(...), denoting that we wish to return someone’s activity stream. The JSON object provided as the parameter to this request denotes that we want the activities for a userid that matches that of the current application viewer and we would also like to return only 20 activities.

 

Once this request completes, we can parse through each of the activities and use them however we’d like.

 

For a full list of activity fields defined within the spec, see Opensocial.Activity.

 

Setting Updates

One of the best methods that a developer can use to encourage user growth in an application is to promote the application through the user update stream. Sadly, networks are generally overly saturated with applications, and they sometimes place applications in undesirable locations.

 

The update stream is one of the few prime-time outlets to users that developers still have access to. When a developer taps into this by setting updates that draw in the users attention, they generally see a larger number of users coming to their application. When implementing this in your code, the syntax will look familiar to our previous GET request (Listing 5).

//insert new activity for the current viewer
osapi.activities.create({
    userId: '@viewer',
    activity: {
        title: 'This is my activity',
        url: 'http://www.yahoo.com/'
    }
}).execute();

Listing 5 – Setting User Updates Setup

 

To accomplish the update insert, we make a request to osapi.activities.create(...). As with our other requests, we will pass in a JSON payload defining what we’d like to insert. In the preceding example, we set the userId to viewer to denote that we would like to insert the update for the current application user. Within the activity definition, we create an object which contains the content of the activity. Here we define the title and URL that the title should link to.

 

There are numerous additional fields available for developers to use in order to further customize the update.


The Gadgets API Tool Set

The gadgets API specification within OpenSocial contains a number of helpful utilities to make your development life easier. When building out applications in a container that has heavy restrictions on front-end code, these helper functions will be a lifesaver. Even if the restrictions aren’t there, not having to build out this functionality yourself makes development quicker — no need to reinvent the wheel.

 

AJAX Requests with gadgets.io

One of the helpful utilities under gadgets.io is the makeRequest function for making AJAX requests. When creating social applications, it’s important that the user experience should be as seamless as possible. In this regard, running data requests in the background is an important implementation tactic.

var params = {};
var url = 'http://www.mysite.com/myfile.php';
var callback = callbackFunc;
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.
io.ContentType.TEXT;
params[gadgets.io.RequestParameters.METHOD] = gadgets.
io.MethodType.GET;
gadgets.io.makeRequest(url, callback, params);
function callbackFunc(response){
   if (response.text){
      //use data returned from server
   }
}

Listing 6 – Making AJAX Requests

 

In the makeRequest method, we define our parameter list to be the type and method of data request that we’re making. We set the content type as “TEXT” and the method as “GET”. The makeRequest call takes in three parameters; the URL to be called, the callback function to call once completed, and the optional parameter list defining what the content / method types are. When the callback is instantiated, we can pull our response object from response.text.

 

The complete gadgets.io spec can be found on OpenSocial gadgets.io.

 

Localization Support with gadgets.Prefs

Another helpful utility within the gadgets spec provides the ability to pull out user localization information for your application using gadgets.Prefs. When building applications, chances are that your app will be seen throughout numerous countries, with numerous languages. Being aware of these details will allow you to better gear your application towards the needs of the viewer.

 

Using gadgets.Prefs, we can pull out the country and language of preference for the user. These two values will go a long way towards customizing your application for your user base (Listing 7).

var prefs = new gadgets.Prefs();
var country = prefs.getCountry();
var language = prefs.getLang();

Listing 7 – Capturing User Localization Information

 

The complete gadgets.Prefs spec can be found on OpenSocial gadgets.Prefs.


Summary

OpenSocial seeks to bridge a large gap, providing common methods for accessing social data on a wide range of networks, allowing developers to build once and distribute broadly. This concept allows a developer to construct an application and deploy it across many supporting networks, providing maximum impact and the most views for a minimal effort.

 

- Jonathan LeBlanc

  • Share/Bookmark


An Exploration of HTML 5


HTML 5 Fist
Photo from justinsomnia
Original t-shirt version

 

At the Confoo.ca conference in Montreal, Quebec, Mark Pilgrim of Google gave an impressive keynote providing an overview of the current state of HTML 5. The information in this post was inspired by his comphehensive talk. Are you wondering what is currently available in HTML 5 and if you can start using it? Take a look at ishtml5readyyet.com or ishtml5ready.com to get your answer.

 

If you take a look at ishtml5ready.com in a browser that supports HTML 5 you’ll see that HTML 5 is somewhat ready. That is, some browsers provide some current support for HTML 5. But that shouldn’t stop us from playing around with it right?

 

HTML5 is a container term used to describe a technology bundle that makes up the new standard. What HTML 5 really encapsulates is HTML 5, CSS 3, and JavaScript all thrown together into the pleasantly marketeering term that is HTML 5. Did you look at those links above in Internet Explorer? Well I’m afraid you’re out of luck for the time being. The A-grade browsers that are currently supporting some of the features of HTML 5 include Safari, Chrome, Firefox and Opera. Still want to use HTML 5 in IE? If you simply put the term “HTML5 elements in IE” (linked directly to Yahoo! Search because I know you all use it right?) into your favorite search engine you’ll get a nice wrapper on how to do this.

 

Let’s explore a few of the great improvements coming in HTML 5:

 

Grouping Tags
There are a number of new tags available for all of your tag grouping needs. <hgroup> is used for defining logical groups of tags or sections on your page, <nav> is present to define navigation systems – great for search engines to easily define your site structure. Tags such as <section>, <article> and <aside> can be used for data grouping. Properly using these tags can help you easily define the sections and most important content of your page. This helps search engine crawlers pick up the structure of your website.

 

Form Elements and Attributes
Forms have received quite a nice overhaul in HTML 5 with new features and methods for defining what data truly is. Let’s take a look at these changes:

  • Sliders: Creating a simple slider within a form is as simple as defining <input type=”range” …> – no more JavaScript needed.
  • Auto Focus: If you need to autofocus the user directly on a part of your form when they first come to your site (plea: please don’t) then the autofocus parameter of an input field is ready to do the dirty work. Simply use the format <input autofocus …> and you’re ready to go. This standardizes the approach so that websites no longer have to use a level of JavaScript interaction to accomplish the same task.
  • Placeholder Text: In an input field, it’s sometimes neccessary to guide your users to input the values you expect, using the value attribute (e.g., add mm/dd/yyyy for them to define a date). This usually involves adding a click event with JavaScript such as this.value = “” to purge the input when the user clicks on it. Then you need some alternate logic to put the placeholder back when the focus is no longer on the input and the user didn’t type anything. This is where the placeholder attribute comes in. Using the form of <input placeholder=”some text” …> the input box will take care of this for you. You have a populated value of “some text.” When the user clicks on the input it disappears, then reappears when the user clicks off of the input and there is no text. Simple yet brilliant. (Currently works in 2 browsers.)
  • Calendar Picker: Prior to HTML 5, calendar pickers in forms were non-standard and usually involved some fairly hefty JavaScript components or libraries to build the functionality. In HTML 5, we have a new input type called datetime, used in the form of <input type=”datetime” …>. This generates a standard input form for the user. When a user clicks on it, it pops up a calendar picker widget automatically. Less code is always a win.
  • Multiple File Uploader: Extending upon the file type of an input field, a new “multiple” property implementation allows you to specify multiple files for upload and takes the form of <input type=’file’ multiple …>. When selecting multiple files, you should display “N files” in the input box, instead of the single filename. (Currently works only in Safari.)
  • Yet More Input Types: There are even more input types that you can logically define. type=”email” and type=”url” are definitions you can explore to define the data that users will input into your forms.

 

Simply put, forms make sense now. No need to heavily augment a form with layers of visualization and JavaScript just to allow data to be defined as it should be in the first place, or to allow base levels of functionality that everyone uses anyway. If you want to see what your current browser supports, take a look at www.miketaylr.com/code/input-type-attr.html.

 

Accessibility
HTML 5 has not forgotten about improving web accessibility. ARIA (Accessibly Rich Internet Applications) defines methods for building websites and web applications in a way that make them more accessible to people wth disabilities. One way HTML 5 endeavors to improve ARIA capabilities is through the use of the role=’group’ attribute, which will allow screenreaders to properly define group content. For a more comprehensive look at common ARIA attributes, take a look at dev.w3.org/html5/markup/aria/aria.html or search for “ARIA accessibility” in your favorite search engine.

 

Web Fonts
From the time I first started developing web applications I knew that font support was a joke and never functioned as it should have (great idea – bad implementation). Well, this fact is no longer true, I am happy to say. Web fonts finally work, allowing you to define your own font faces, even if all browsers have different implementations. For more information, take a look at Paul Irish’s post: Bulletproof @font-face syntax.

 

Video and Audio
HTML 5 video tag screenshot from YouTube
Video is now scriptable, folks. With the new <video> tag, you’ll be able to include a video player, implementing ogv & mp4 formats. You can now use DOM scripting to control video – mute, forward, stop – all through the DOM. The <audio> tag is there as well, to include audio files easily. If you want to see the video tag in action, go to youtube.com/html5 to opt in to HTML 5 standards beta. YouTube will not use the Flash player when video tag support is available in the browser and uses the HTML 5 video player instead. The video tag may be styled in exactly the same way as you would see with the standard Flash video players out there.

 

Freeform Drawing with Canvas
Have you ever wanted a blank canvas where you could programmatically draw anything within a website or web application? Well there are a number of server-side languages that do this – but have you ever wanted to do this through scripting (with a little bit of embedded JavaScript)? Well that’s where the <canvas> tag comes in. There are many canvas demos available for you to see exactly what can be done with the canvas tag by typing in “HTML5 canvas demos” in your favorite search engine. The code for creating a canvas and working with it is also very simple:

 

 

Using JavaScript, you can draw on the canvas:

 

 

More Information and Links
We’ve just scratched the surface of HTML 5 and all of the new functionality. There are some great resources available if you want to explore even further:

 

A big thanks to Mark Pilgrim for his talk at Confoo. I look forward to seeing the standard expanded and adopted by all the browsers out there.

 

- Jonathan LeBlanc

  • Share/Bookmark


Pushing an Application Update with OpenSocial 0.8


Within the OpenSocial 0.8 specification is a definition available for pushing out a notification to the user stream automatically from an application.

 

What are updates and why should you care?

 

The update (or status) stream of a user is a listing of what they are currently doing or what they have posted for their friends and connections to see. What’s important to note here is that using this stream can drive quite a bit of traffic to your application if you use it. Personally, I never look through the application gallery of a platform like the Yahoo! Application Platform or MySpace. Most of the time I will only ever add applications when I see an app notification through one of the updates produced by one of my friends.

 

I mean – sometimes I just need to find out what Disney princess I am right?

 

In any event, using this update stream is going to push users to your application. This is one of the many tools that we can use to drive more traffic to your application.

 

How does this work?

 

Using standard JavaScript, let’s take a look at the steps that we will need to implement to insert an update. I would suggest that this JavaScript be inserted in a function that is driven by a user event, such as a click action.

var params = {}, activity;
params[opensocial.Activity.Field.TITLE] = "title";
params[opensocial.Activity.Field.URL] = "http://www.myserver.com/index.php";
params[opensocial.Activity.Field.BODY] = "body text";
activity = opensocial.newActivity(params);

First we need to set up the parameters that will define what will be pushed out in this update. In the above example, we assign a title string, the URL that the title string will be linked to and the body (content) of the update. There are a whole series of options available within an activity request that can be added to the above. These are listed on the OpenSocial documentation here: http://wiki.opensocial.org/index.php?title=Opensocial.Activity_%28v0.8%29#Fields. We then call the newActivity() method to define the activity that we want to build. The parameter that needs to be present in the method is the parameter list that we defined right before that request.

opensocial.requestCreateActivity(
	activity,
	opensocial.CreateActivityPriority.LOW,
	callback);

We then call requestCreateActivity to actually send the request and push the update out. There are three parameters present in this method:

  1. activity – The activity we defined in the first code sample
  2. priority – This is the priority that the update should use to send out. This can either be opensocial.CreateActivityPriority.HIGH (or the string ‘HIGH’) or opensocial.CreateActivityPriority.LOW (or the string ‘LOW’). The difference is in whether the user has granted your application permission to push updates out to their stream. If you define a HIGH priority and the user has not granted your application permission to push out updates, the application will attempt to load an authentication flow to allow the user to allow the update out. If you set a LOW priority, if the user has not granted your app permission, the update will be ignored and no authentication flow will be presented.
  3. callback – The function that will be executed when the update is inserted

With a few lines of code, you now have an application that can draw in more users using the update stream.

 

- Jonathan LeBlanc

  • Share/Bookmark


Fetching Viewer Social Data with OpenSocial 0.8


Profile information within a social network (e.g. MySpace, Orkut, YAP, Hi5) is something that users take painstaking amounts of time to input so that they can tell their friends and the world who they are. If a developer is building an application on one of these same platforms, that same profile detail is a gold mine of information that can be obtained to customize and personalize an application. Why would you ever ask a user to input their name, interests or gender if they already have this information freely available on their profile? Having the user re-enter this information is just asking for the user to drop off of your application. Using the knowledge wealth will both decrease user drop rates and make your applicatio more appealing to the masses.

 

Within a container that supports the OpenSocial 0.8 JavaScript specification, fetching the profile information of a person can be accomplished and customized with several steps.


The first thing we do is call newDataRequest to set up a new information request object within OpenSocial. Once this has been done we create our parameter list which will define what type of information we want to access within the information request. In our case here, we specify that we would like to capture “PeopleRequestFields”, namely a user’s “PROFILE_DETAILS”, and of those profile details we want to return the name and the thumbnail_url to their profile image. We can add in a whole series of profile information here, depending on what the container supports within the profile information specification. We then add to our request a newFetchPersonRequest and specify that we want the “VIEWER” profile (this could also be OWNER) and then input the params we set up to define what data we want from the viewer. Then we just name that request as “viewer_profile” so that we can access it later and send the request. The request function takes the name of the callback function as a parameter.


When the request completes and gets to our callback function, our data is returned to us with the profile information of the viewer presuming that everything completed correctly. In the callback we can call get on the returned data, inputting the name of the request, and then call getData on that. If we want a finer granularity of control over the data that is returned, we can then call the getField function with the information that we want (in this case the viewer name).

 

That’s all there is to it – with a few calls you now have all the information you need to personalize your application to every users that uses it.

 

Jonathan LeBlanc

  • Share/Bookmark


Browser MVC with YUI and YQL


 

In July of 2009 I gave a talk at a JavaScript meetup called Bayjax. This talk covered the topic of Browser MVC using YQL and YUI as the technology backbone and went into an overview of YQL.

 

URL’s for the tabs can be found at http://speakerrate.com/talks/1281-coupling-yql-and-yui-to-build-dynamic-visualization-widgets

 

A slide breakdown of the content discussed in this talk may be found at http://www.slideshare.net/jcleblanc/bayjax-july-2009-browser-mvc-with-yql-yui

  • Share/Bookmark


YAP: Capturing Node X/Y Coordinates and Adding Mouse Events Using Caja Safe JavaScript


One of the requests that has come up while working with JavaScript on the Yahoo! Application Platform is: How do you assign an event to a DOM node which then captures the X/Y coordinates of that node under caja?

 

I put together a sample that will display how to assign a mouseover and mouseout event to a DOM node which will call functions to display a tool tip to the immediate right of the node. To do this we need to capture the x/y coordinates and width of that node to calculate the positioning offset. First we create the CSS and HTML for the nodes that will act as our event and tool tip containers.

 

<style type="text/css">
div#tooltip{ position:absolute; top:0; left:0; width:100px;
    height:40px; display:none; }
div#padNode{ position:relative; margin:50px; }
div#myContainer{ border: 1px solid #000000; padding: 10px;
    position: absolute; top: 50px; left: 50px;
    width: 200px; }
</style>

<div id="tooltip"></div>
<div id="padNode">
    <div id="myContainer">Container</div>
</div>

There are a few items to note about the above HTML:

  1. The tooltip container is an absolute positioned node that starts out hidden. The tooltip is placed at a root location so that the offset can be properly applied. We don’t want to embed this node within a positioned / floated container because that will affect our positioning offset location.
  2. The “myContainer” div is placed within a relative container with a margin – this is done to simulate embedded positioned nodes on a page.

Now, let’s look at the JavaScript:

<script type="text/javascript">
//assign mouse over handler to container
var myDiv = document.getElementById('myContainer');
if (myDiv.addEventListener) {
    myDiv.addEventListener('mouseover', overCallback, false);
    myDiv.addEventListener('mouseout', outCallback, false);
} else if (myDiv.attachEvent) {
    myDiv.attachEvent('mouseover', overCallback);
    myDiv.attachEvent('mouseout', outCallback);
}

//callback for mouseover - sender is the triggering event
function overCallback(sender){
    var target;

    //capture the object that the mouse over event was from
    if (window.event) {
        target = window.event.srcElement;
    } else if (sender) {
        target = sender.target;
    } else { return; }

    //get the top and left x / y coords of the target
    var nodeTop = target.offsetTop;
    var nodeLeft = target.offsetLeft;

    //if the node is within a parent container, loop through each parent
    //and add their height / width offsets to the top / left
    var nodeParent = target.offsetParent;
    if (nodeParent != target) {
        while (nodeParent) {
            nodeLeft += nodeParent.offsetLeft;
            nodeTop += nodeParent.offsetTop;
            nodeParent = nodeParent.offsetParent;
        }
    }

    //set tooltip data and display
    var tooltip = document.getElementById('tooltip');
    tooltip.innerHTML = "This is a tooltip";
    tooltip.style.display = "block";

    //set the tooltip top positioning to the same as the target node
    tooltip.style.top = nodeTop + "px";

    //set the tooltip left positioning to the end of the target node
    //(left location + width of the container)
    tooltip.style.left = (nodeLeft + target.offsetWidth) + "px";
}

//callback for mouseout
function outCallback(sender){
    //hide the tooltip
    document.getElementById('tooltip').style.display = "none";
}
</script>

Going from top to bottom, here’s what everything does:

 

Attaching Events
The first bit of code will check if addEventListener or attachEvent are available methods (basically checking if you are using IE or every other browser known to man). This block will then set up two events: The mouseover event on the “myContainer” node will call the function “overCallback” and the mouseout event on the node will call the function “outCallback”.

 

The MouseOver callback
In the function “overCallback” we have a few things going on. The function takes in one parameter, the event that was enacted on the node (so our mouseover event). Using this event we determine the node that the event was performed on. This will be valuable if you have multiple nodes all calling the same mouseover function. Next we capture the x/y (or top/left) offsets of the node from its current container (so the number of pixels from the 0,0 location of the “padNode” div.

 

But wait…the “padNode” div has a 50px margin from the 0,0 location of the overall container…so won’t the offsets be off by 50px? Yes, yes they will.

 

This leads us to our next block of code. While there is still a parent node to calculate we add the parent to child offset to our overall offset values. Basically what we’re doing here is looping through every container that the “myContainer” div is located within to get the “true offset” from the 0,0 position of the container itself.

 

Finally, we capture the tooltip node, set its content and display it. We then set the “top” pixel CSS offset of the tooltip to our calculated top offset. After that we set the “left” pixel CSS offset of the tooltip to our calculated left offset plus the width of the “myContainer” node so that it will display to the immediate right.

 

The MouseOut callback
There isn’t much to the mouseout callback…all we have to do is hide the tooltip container.

 

There you have it…we attach events to a node which then capture the true x/y position of that node to display a tool tip.

 

– Jonathan LeBlanc

  • Share/Bookmark