Category Archives: jquery

jQuery UI Autocomplete Widget with PHP and MySQL

You might also be interested in the Using jQuery Autocomplete to Populate Another Autocomplete post.

As a follow up to the jQuery UI Autocomplete Widget with ColdFusion post, I did one with PHP as the backend.

The jQuery UI folks have released an autocomplete widget that is pretty slick. This example uses the json_encode function in PHP 5. If you have an earlier version of PHP, you will have to roll your own JSON string.
autocomplete
This example will use US states and territories to populate the autocomplete. It will also demonstrate how to fill other fields with data returned from the database. This data can be used to fill a visible text box or a hidden form field. It also demonstrates the basic autocomplete functionality which may be fine for some applications.

Of course, you will need the jQuery core file, the jQuery UI core file, and the jQuery UI style sheet of choice. The style sheet comes from the themes available in the jQuery UI website and can be downloaded with the core file or you can link to the latest versions of both the core files and the css:

<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/redmond/jquery-ui.css">

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>

<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>

The HTML is straight forward and stripped down for the example:

<form action="<?php echo $PHP_SELF;?>"  method="post">
<fieldset>
<legend>jQuery UI Autocomplete Example - PHP Backend</legend>

<p>Start typing the name of a state or territory of the United States</p>

<p class="ui-widget">

<label for="state">State (abbreviation in separate field): </label>

<input type="text" id="state"  name="state" /> 

<input readonly="readonly" type="text" id="abbrev" name="abbrev" maxlength="2" size="2"/></p>

<input type="hidden" id="state_id" name="state_id" />

<p class="ui-widget">

<label for="state_abbrev">State (replaced with abbreviation): </label>

<input type="text" id="state_abbrev" name="state_abbrev" /></p>

<p><input type="submit" name="submitBtn" value="Submit" /></p>

</fieldset>
</form>

As a bonus, we dump out the form values to see what we have right underneath the form itself:

<?php
if (isset($_POST['submit'])) {
echo "<p>";
	while (list($key,$value) = each($_POST)){
	echo "<strong>" . $key . "</strong> = ".$value."<br />";
	}
echo "</p>";
}
?>

And the jQuery on the page is equally brief:

$(function() {

            $('#abbrev').val("");

            $("#state").autocomplete({
                source: "states.php",
                minLength: 2,
                select: function(event, ui) {
                    $('#state_id').val(ui.item.id);
                    $('#abbrev').val(ui.item.abbrev);
                }
            });

            $("#state_abbrev").autocomplete({
                source: "states_abbrev.php",
                minLength: 2
            });
        });

Notice that there are two autocomplete functions on the page, one for each example in the demo. Each function calls a different PHP file which return slightly different result sets.

The jquery autocomplete will append the text typed into the autocomplete field as the URL parameter ‘term.’ This URL parameter is used to query the database.

From the jquery documentation:

The request parameter “term” gets added to that URL.

Also, the minLength for autocomplete to return results is set to 2 to prevent too many rows from being returned.

Both PHP pages return the data after a few steps:

  1. It queries the database
  2. Loops an array of the query results adding each row to a return array
  3. Outputs the array as JSON data

The states.php file returns the id field, the state field as ‘value’, and the abbrev field. These values are placed in the appropriate text boxes by the autocomplete jQuery function.

A ‘value’ and/or a ‘label’ field is mandatory for the autocomplete to work. See explanation at bottom of post.

And, of course, you will have to make your own connection to your MySQL database before running the query.

PDO version:

/* Connection vars here for example only. Consider a more secure method. */
$dbhost = 'YOUR_SERVER';
$dbuser = 'YOUR_USERNAME';
$dbpass = 'YOUR_PASSWORD';
$dbname = 'YOUR_DATABASE_NAME';

try {
  $conn = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
}
catch(PDOException $e) {
    echo $e->getMessage();
}

$return_arr = array();

if ($conn)
{
	$ac_term = "%".$_GET['term']."%";
	$query = "SELECT * FROM states where state like :term";
	$result = $conn->prepare($query);
	$result->bindValue(":term",$ac_term);
	$result->execute(); 

	/* Retrieve and store in array the results of the query.*/
	while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
		$row_array['id'] = $row['id'];
		$row_array['value'] = $row['state'];
		$row_array['abbrev'] = $row['abbrev'];

        array_push($return_arr,$row_array);
    }

}
/* Free connection resources. */
$conn = null;
/* Toss back results as json encoded array. */
echo json_encode($return_arr);

Non-PDO version:

/* Connection vars here for example only. Consider a more secure method. */
$dbhost = 'YOUR_SERVER';
$dbuser = 'YOUR_USERNAME';
$dbpass = 'YOUR_PASSWORD';
$dbname = 'YOUR_DATABASE_NAME';

$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die ('Error connecting to mysql');
mysql_select_db($dbname);

$return_arr = array();

/* If connection to database, run sql statement. */
if ($conn)
{
	$fetch = mysql_query("SELECT * FROM states where state like '%" . mysql_real_escape_string($_GET['term']) . "%'"); 

	/* Retrieve and store in array the results of the query.*/
	while ($row = mysql_fetch_array($fetch, MYSQL_ASSOC)) {
		$row_array['id'] = $row['id'];
		$row_array['value'] = $row['state'];
		$row_array['abbrev'] = $row['abbrev'];

        array_push($return_arr,$row_array);
    }
}

/* Free connection resources. */
mysql_close($conn);

/* Toss back results as json encoded array. */
echo json_encode($return_arr);

Very important information below. Please read and understand before expecting the autocomplete to work properly.

The states_abbrev.php shows the basic functionality of the autocomplete function by just assigning results of the query to the ‘label’ and ‘value’ fields. Explanation on the ‘label’ and ‘value’ fields from the jQuery UI site:

The local data can be a simple Array of Strings, or it contains Objects for each item in the array, with either a label or value property or both. The label property is displayed in the suggestion menu. The value will be inserted into the input element after the user selected something from the menu. If just one property is specified, it will be used for both, eg. if you provide only value-properties, the value will also be used as the label.”

PDO version:

$return_arr = array();

if ($conn)
{
	$ac_term = "%".$_GET['term']."%";
	$query = "SELECT * FROM states where state like :term";
	$result = $conn->prepare($query);
	$result->bindValue(":term",$ac_term);
	$result->execute(); 

	/* Retrieve and store in array the results of the query.*/
	while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
		$row_array['label'] = $row['state'];
		$row_array['value'] = $row['abbrev'];

        array_push($return_arr,$row_array);
    }

}
/* Free connection resources. */
$conn = null;
/* Toss back results as json encoded array. */
echo json_encode($return_arr);

Non-PDO version:

$return_arr = array();

/* If connection to database, run sql statement. */
if ($conn)
{
	$fetch = mysql_query("SELECT * FROM states where state like '%" . mysql_real_escape_string($_GET['term']) . "%'"); 

	/* Retrieve and store in array the results of the query.*/
	while ($row = mysql_fetch_array($fetch, MYSQL_ASSOC)) {
		$row_array['label'] = $row['state'];
		$row_array['value'] = $row['abbrev'];

        array_push($return_arr,$row_array);
    }
}
/* Free connection resources. */
mysql_close($conn);

/* Toss back results as json encoded array. */
echo json_encode($return_arr);

Usual recommended jQuery and PHP reading:

Demo

jQuery UI Autocomplete Widget with ColdFusion

You may also be interested in the jQuery UI Autocomplete Widget with PHP and MySQL or the jQuery UI Autocomplete Widget with ASP.NET VB post. Also, check out the Using jQuery Autocomplete to Populate Another Autocomplete post.

The jQuery UI folks have released an autocomplete widget that is pretty slick. Using it with ColdFusion is a snap. This example uses the serializeJSON function in ColdFusion 8. If you have an earlier version of CF, you will have to find a cfc that can build a JSON string for you. There are several freely available cfc’s that do it for you.
autocomplete
This example will use US states and territories to populate the autocomplete. It will also demostrate how to fill other fields with data returned from the database. This data can be used to fill a visible text box or a hidden form field. It also demonstrates the basic autocomplete functionality which may be fine for some applications.

Of course, you will need the jQuery core file, the jQuery UI core file, and the jQuery UI style sheet of choice. The style sheet comes from the themes available in the jQuery UI website and can be downloaded with the core file or you can link to the latest versions of both the core files and the css:

<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/redmond/jquery-ui.css">

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>

The HTML is straight forward and stripped down for the example:

<form action="index.cfm"  method="post">
<fieldset>
<legend>jQuery UI Autocomplete Example - ColdFusion Backend</legend>
<p>Start typing the name of a state or territory of the United States</p>
<p class="ui-widget"><label for="state">State (abbreviation in separate field): </label>
	<input type="text" id="state"  name="state" /> <input readonly="readonly" type="text" id="abbrev" name="abbrev" maxlength="2" size="2"/></p>
    <input type="hidden" id="state_id" name="state_id" />
<p class="ui-widget"><label for="state_abbrev">State (replaced with abbreviation): </label>
<input type="text" id="state_abbrev" name="state_abbrev" /></p>
<p><input type="submit" name="submit" value="Submit" /></p>
</fieldset>
</form>

As a bonus, we dump out the form values to see what we have right underneath the form itself:

<cfdump var="#form#" label="Form Fields" />

And the jQuery on the page is equally brief:

$(function() {

            $('#abbrev').val("");

            $("#state").autocomplete({
                source: "states.cfm",
                minLength: 2,
                select: function(event, ui) {
                    $('#state_id').val(ui.item.id);
                    $('#abbrev').val(ui.item.abbrev);
                }
            });

            $("#state_abbrev").autocomplete({
                source: "states_abbrev.cfm",
                minLength: 2
            });
        });

Notice that there are two autocomplete functions on the page, one for each example in the demo. Each function calls a different ColdFusion file which return slightly different result sets.

Also, the minLength for autocomplete to return results is set to 2 to prevent too many rows from being returned.

The jquery autocomplete will append the text typed into the autocomplete field as the URL parameter ‘term.’ This URL parameter is used to query the database.

From the jquery documentation:

The request parameter “term” gets added to that URL.

Both ColdFusion pages return the data after a few steps:

  1. It queries the database
  2. Loops the query, adding each row to a structure that is appended to an array
  3. Outputs the array as JSON data

The states.cfm file returns the id field, the state field as ‘label’, and the abbrev field. These values are placed in the appropriate text boxes by the autocomplete jQuery function.

<cfset returnArray = ArrayNew(1) />

<cfquery name="qryStates" dataSource="autocomplete">
    Select * from states where state like '%<cfqueryparam value="#URL.term#" cfsqltype="cf_sql_varchar">%'
</cfquery>

<cfloop query="qryStates">
    <cfset statesStruct = StructNew() />
    <cfset statesStruct["id"] = id />
    <cfset statesStruct["label"] = state />
    <cfset statesStruct["abbrev"] = abbrev />

    <cfset ArrayAppend(returnArray,statesStruct) />
</cfloop>

<cfoutput>
#serializeJSON(returnArray)#
</cfoutput>

Unfortunately, the ColdFusion function serializeJSON does not put query results in the format that javascript likes. That’s why we have to do the hokey-pokey with the structure and array.

Very important information below. Please read and understand before expecting the autocomplete to work properly.

The states_abbrev.cfm shows the basic functionality of the autocomplete function by just assigning results of the query to the ‘label’ and ‘value’ fields. Explanation on the ‘label’ and ‘value’ fields from the jQuery UI site:

The local data can be a simple Array of Strings, or it contains Objects for each item in the array, with either a label or value property or both. The label property is displayed in the suggestion menu. The value will be inserted into the input element after the user selected something from the menu. If just one property is specified, it will be used for both, eg. if you provide only value-properties, the value will also be used as the label.”

<cfset returnArray = ArrayNew(1) />

<cfquery name="qryStates" dataSource="autocomplete">
    Select * from state where state like '%#URL.term#%'
</cfquery>

<cfloop query="qryStates">
    <cfset statesStruct = StructNew() />
    <cfset statesStruct["label"] = state />
    <cfset statesStruct["value"] = abbrev />

    <cfset ArrayAppend(returnArray,statesStruct) />
</cfloop>

<cfoutput>
#serializeJSON(returnArray)#
</cfoutput>

Usual recommended jQuery and CF reading:

Pop-up Survey with jQuery UI Dialog

I was tasked with creating a pop-up survey for a project. Fairly simple whether I used the jQuery dialog or not so I added a “Thank you” dialog that displays the survey results with a lightweight, css-based bar chart just for fun.

There is also an ASP.NET version of this post.

pop-up survey

Pop-up Behavior

  1. Pop-up survey opens when page loads. Pop-ups on window.unload or window close are being blocked by most browsers due to abuse and overuse.
  2. Cookie is set when survey submitted or user opts out (“No, thanks” click).
  3. Closing dialog will not set cookie

All survey data is stored in a database and jQuery .post is used to shuttle the information back and forth.

Code for the survey dialog:

<div id="survey" title="Pop-Up Survey">
	<p id="surveyDenied"><a href="#">No, thanks</a></p>
		<p>Pop-up survey that cookies browser on completion or on opt-out. Short 411 demo survey.</p>
		<form id="popup_survey" name="popup_survey" method="post">
        <p><strong>Pink or blue?</strong><br />
		<input id="pink" type="radio" name="radio_color" value="pink"  />Pink<br />
        <input id="blue" type="radio" name="radio_color" value="blue"  />Blue</p>
        <p><strong>Soccer or futbol?</strong><br />
		<input id="soccer" type="radio" name="radio_sport" value="soccer"  />Soccer<br />
        <input id="futbol" type="radio" name="radio_sport" value="futbol"  />Futbol</p>
        </form>
	<div id="error_message"></div>
</div>

It’s just a couple of radio buttons and a place for an error message. The submit is handled by the dialog button.

Here is the survey dialog code:

$(function(){
			$('#survey').dialog({
				bgiframe: true,
				autoOpen: false,
				modal: true,
				width: 500,
				resizable: false,
				buttons: {
					Submit: function(){
						if($("input[name='radio_color']:checked").val() !== undefined && $("input[name='radio_sport']:checked").val() !== undefined){
							setCookie('POPsurvey','POPsurvey',30);
							$.post("process_survey.php", $("#popup_survey").serialize(),
							function(data){
								if(data.db_check == 'fail'){
									$("#error_message").html("<p>Database not available. Please try again.</p>");
								} else {
									$("div.pink").css("width",data.perPink);
									$(".perPink").html(data.perPink + "% (" + data.totalPink + ")");

									$("div.blue").css("width",data.perBlue);
									$(".perBlue").html(data.perBlue + "% (" + data.totalBlue + ")");

									$("div.soccer").css("width",data.perSoccer);
									$(".perSoccer").html(data.perSoccer + "% (" + data.totalSoccer + ")");

									$("div.futbol").css("width",data.perFutbol);
									$(".perFutbol").html(data.perFutbol + "% (" + data.totalFutbol + ")");

									$(".totalRes").html(data.totalRes);

									$('#survey').dialog('close');
									$('#survey_thanks').dialog('open');
								}
								}, "json");
						}else{
							$("#error_message").html("<p>Please answer all questions.</p>");
						}
					}
				}
			});
		});

Lots of stuff but it does several things. First, on submit, it checks that the user answered both questions. Then it sets the cookie. And, finally, it sends the form data off via post and puts the response in elements on the page that are part of the “Thank you” dialog. Speaking of which…

The “Thank you” dialog code is thus:

<div id="survey_thanks" title="Pop-Up Survey - Thank You!">
    <p>Thank you for taking the time to answer our survey. Your input will help us improve the site.</p>
    <p>Responses: <span class="totalRes"></span></p>

    <div class="progress-container">
        pink <span class="perPink"></span>
        <div class="pink"></div>
        blue <span class="perBlue"></span>
        <div class="blue"></div>
    </div>

    <div class="progress-container">
        soccer <span class="perSoccer"></span>
        <div class="soccer"></div>
        futbol <span class="perFutbol"></span>
        <div class="futbol"></div>
    </div>
</div>
$(function(){
			$('#survey_thanks').dialog({
				bgiframe: true,
				autoOpen: false,
				modal: true,
				width: 500,
				resizable: false,
				buttons: {
					Close: function(){
						$(this).dialog('close');
						}
					}
			});
		});

Processing the Survey

The process_survey page adds the answers to the database and brings back the totals that are calculated by a view.

$dbhost = 'YOUR_SERVER';
$dbuser = 'YOUR_USERNAME';
$dbpass = 'YOUR_PASSWORD';
$dbname = 'YOUR_DATABASE_NAME';

$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die ('Error connecting to mysql');
mysql_select_db($dbname);

$db_check = 'OK';
$return_arr = array();

if (!$conn)
  {
  $db_check = 'fail';
  }
if ($db_check === 'OK'){
	$insert_sql = mysql_query("INSERT INTO survey (color,sport) VALUES ('".$_POST['radio_color']."','".$_POST['radio_sport']."')");
	if (!$insert_sql) {
		$db_check = 'fail';
	}
}
if ($db_check === 'OK'){
	$fetch = mysql_query("SELECT * FROM v_totals");
	/* Retrieve and store in array the results of the query.*/

	while ($row = mysql_fetch_array($fetch, MYSQL_ASSOC)) {
		$return_arr["totalPink"] = $row['Pink'];
		$return_arr["perPink"] = round($row['Pink']/($row['Pink']+$row['Blue']),2)*100;

		$return_arr["totalBlue"] = $row['Blue'];
		$return_arr["perBlue"] = round($row['Blue']/($row['Pink']+$row['Blue']),2)*100;

		$return_arr["totalFutbol"] = $row['Futbol'];
		$return_arr["perFutbol"] = round($row['Futbol']/($row['Futbol']+$row['Soccer']),2)*100;

		$return_arr["totalSoccer"] = $row['Soccer'];
		$return_arr["perSoccer"] = round($row['Soccer']/($row['Futbol']+$row['Soccer']),2)*100;

		$return_arr["totalRes"] = $row['Total'];
	}
	/* Retrieve and display the results of the query. */

}
else {
	$db_check = 'fail';
}
/* Free connection resources. */
mysql_close($conn);

$return_arr["db_check"] = $db_check;

echo json_encode($return_arr);

The totals for the return data are stored in a view to prevent separate and multiple calls to the database. The table and the view can be created in MySQL as such:

CREATE TABLE IF NOT EXISTS `survey` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `color` varchar(4) DEFAULT NULL,
  `sport` varchar(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

CREATE VIEW `v_totals` AS select (select count(`survey`.`color`) AS `Expr1` from `survey` where (`survey`.`color` = 'pink')) AS `Pink`,(select count(`survey_2`.`color`) AS `Expr2` from `survey` `survey_2` where (`survey_2`.`color` = 'blue')) AS `Blue`,(select count(`survey_2`.`sport`) AS `Expr3` from `survey` `survey_2` where (`survey_2`.`sport` = 'futbol')) AS `Futbol`,(select count(`survey_2`.`sport`) AS `Expr3` from `survey` `survey_2` where (`survey_2`.`sport` = 'soccer')) AS `Soccer`,(select count(`survey_2`.`id`) AS `Expr4` from `survey` `survey_2`) AS `Total` from `survey` limit 0,1;

Cookies

The code to handle setting, checking, and deleting cookies is standard javascript:

function setCookie(c_name,value,expiredays)
{
	var exdate=new Date();
	exdate.setDate(exdate.getDate()+expiredays);
	document.cookie=c_name+ "=" +escape(value)+((expiredays==null) ? "" : ";expires="+exdate.toGMTString());
} 

function getCookie(c_name)
{
	if (document.cookie.length>0)
	  {
	  c_start=document.cookie.indexOf(c_name + "=");
	  if (c_start!=-1)
		{
		c_start=c_start + c_name.length+1;
		c_end=document.cookie.indexOf(";",c_start);
		if (c_end==-1) c_end=document.cookie.length;
		return unescape(document.cookie.substring(c_start,c_end));
		}
	  }
	return "";
}

function checkCookie(c_name)
{
	cookie_value=getCookie(c_name);
	if (cookie_value=="") {
		$('#survey').dialog('open');
	}
}

function deleteCookie(c_name) {
	document.cookie = c_name +'=; expires=Thu, 01-Jan-70 00:00:01 GMT;';
}

The style for the “Thank you” graph:

div.progress-container {
  border: 1px solid #ccc;
  width: 150px;
  padding: 1px;
  margin-bottom: 5px;
  background: white;
}

div.progress-container > div {
  height: 12px;
  margin-bottom: 2px;
}
div.progress-container > div.pink {
  background-color:#CC6699;
}
div.progress-container > div.blue {
  background-color: #3366CC;
}
div.progress-container > div.soccer {
  background-color: #006633;
}
div.progress-container > div.futbol {
  background-color: #663333;
}

You’ll notice in the .post function that the width of the bar graph is set with the percent of responses for each answer that comes back from the database.

Demo

Download zip of all files

Using jQuery Datepicker and Dialog Box To Select Date Range

This post has been updated and moved to:
http://www.jensbits.com/2011/09/06/using-jquery-datepicker-and-dialog-box-to-select-date-range-2/

Positioning Multiple jQuery UI Dialogs

This post shows one way to position multiple jQuery UI dialogs on the same page. It positions the dialog over the calling button based on the button’s position in the viewport.

The dialog box contents can be placed anywhere in the body. For this example they look like this:

<div id="helpDialog1" title="Help >> Dialog 1">
        <p>In id imperdiet justo. Sed eu commodo erat. Nullam euismod dapibus magna a porttitor. Vestibulum odio turpis, ullamcorper eu ullamcorper sit amet, blandit id purus. Suspendisse commodo egestas augue, et fringilla lorem consequat et. Sed volutpat sapien at massa gravida sollicitudin. Nunc ut nibh orci.</p>
        <p>Nam quis odio vel arcu auctor tincidunt. Aenean vulputate, sapien id porttitor placerat, purus magna viverra ligula, eu tristique justo purus sit amet ligula. Etiam viverra, enim in luctus sollicitudin, arcu nulla accumsan quam, ut mattis orci eros at dui.</p>

    <p style="text-align:right;color: #cc0202;"><a id="closeHelp1" style="color:#cc0202;" href="#">Close</a> <strong>X</strong></p>
</div>

<div id="helpDialog2" title="Help >> Dialog 2">
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut aliquam orci eget enim vehicula volutpat. Cras eu tellus quis risus venenatis egestas vitae eget est. Donec nec lorem purus. Duis vitae tortor at lacus placerat rhoncus id sit amet lectus. Suspendisse potenti. Aliquam vitae tortor lorem.</p>
        <p>Proin ac sem magna. Donec interdum iaculis lacinia. Donec placerat malesuada mi, vitae malesuada sapien tincidunt ut. Mauris a nibh arcu. Etiam a magna a ipsum faucibus sollicitudin. Morbi nisl nunc, porttitor vitae suscipit in, rutrum at augue.</p>
        <p style="text-align:right;color:#cc0202;"><a id="closeHelp2" style="color:#cc0202;" href="#">Close</a> <strong>X</strong></p>
</div>

Not much. Just a little styling for sanity’s sake and lots of Latin. Take note of the id’s. They are helpDialog1 and helpDialog2. The number will be needed later.

Like, now…

$('[id^=helpDialog]').dialog({
                autoOpen: false,
                modal: false,
                width: 300,
                resizable: false,
                show: 'blind',
                hide: 'blind'
            });

The jQuery sets some options for the dialog boxes, and it does this by identifying the dialog boxes with the common part of the id. The ‘^’ means ‘starts with’ as in the id starts with helpDialog.

Just like that all the dialogs your heart desires are set and ready to go.

The click function on the help buttons does all the heavy lifting.

$('.help').click(function() {
              //strip off prefix of buttin id to get number for dialog
                var helpBtnIdPrefix = 'helpBtn';
                var helpBtnNum = $(this).attr('id').substring((helpBtnIdPrefix.length));

                //offset returns top and left of element
                var helpBtnOffset = $('#helpBtn' + helpBtnNum).offset();
                //width of element
                var helpBtnWidth = $('#helpBtn' + helpBtnNum).width();
                //width of dialog box
                var dialogWidth = $('#helpDialog' + helpBtnNum).dialog('option', 'width');

                //x and y positions of dialog box
                $('#helpDialog' + helpBtnNum).dialog('option','position',[helpBtnOffset.left+helpBtnWidth-dialogWidth,helpBtnOffset.top-$(window).scrollTop()]);
                $('#helpDialog' + helpBtnNum).dialog('open');
            });

The script starts by stripping off the prefix on the button id to get to the number of the button as it corresponds to the dialog box.

Then it gets the button offset or left and top position on the document. Next, it gets the button’s width. All this is used to properly place the dialog over the button.

The x position of the dialog are set using the button’s left position and width along with the dialog box width. The y position is set by using the button’s top position minus the window scroll position in case the user scrolled down.

After the math, the dialog is opened.

An extra close button was added at the bottom of the dialog box for user convenience.

//added close btn at bottom of dialog
            $('[id^=closeHelp]').click(function() {
                var closeHelpPrefix = 'closeHelp';
                var closeHelpNum = $(this).attr('id').substring((closeHelpPrefix.length));
                $('#helpDialog' + closeHelpNum).dialog('close');
            });

Usual recommended jQuery reading:

Demo

Download zip of all files