Archives for category: jquery

In this new cyberworld saturated with abundant mouse clicks, it has become more necessary to try to get your user’s attention before allowing them to perform an action that may result in a critical error.  One of these actions is leaving the application without first saving changes.  In some cases, a request to leave a page without first saving changes may be on purposes, but in many cases it may have been the result of an accidental mouse click.

Browsers have provided a built-in prompt that warns users before leaving certain pages.  The user then has the option of leaving the page or staying on the page.  This built-in prompt is not at all flexible, by design, to prevent rogue sites from actually preventing users from leaving their web sites.

The following is an example of how you might implement this prompt using the window.onbeforeunload event.

STEP 1:  Mechanism to determine if there are unsaved changes
Because there is no reason to warn your users from leaving if they have not made any changes, the first thing you want to do is create a hidden field or client-side variable that specifies whether your page contains unsaved changes.

In this example, I have created a hidden input to store my change status:
@Html.Hidden(“UnsavedChanges”, “0”)

I then created a function to update the change status:
function HandleFieldChange()
{
$(“#UnsavedChanges”).val(“1”);
}

Finally, I used a little jQuery to easily add the field change event:
$(“input”).change(function(){
HandleFieldChange();
});
$(“select”).change(function(){
HandleFieldChange();
});
$(“textarea”).change(function(){
HandleFieldChange();
});
$(“input:checkbox”).click(function(){
HandleFieldChange();
});
$(“input:radio”).click(function(){
HandleFieldChange();
});

STEP 2:  Use the browser’s built-in warning prompt before leaving a page.

Again, this cannot be customized.  If it could, that would open the door for this functionality to be used maliciously.  You don’t have any control over how the warning looks, but you can insert a custom message into the prompt.

This function checks to see if there are unsaved changes.  If there are, it calls the browser’s built-in warning and inserts a custom message.

window.onbeforeunload = function() {
if ($(“#UnsavedChanges”).val() == “1”)
{
var _message = “You currently have unsaved changes!!!\n\nAre you sure you want to exit without saving.\n\nChoose ‘Leave this page’ to exit without saving changes.\nChoose ‘Stay on this page’ to return to the billing profile.”;
return _message;
}
}

RESULT

It’s not as pretty as I would like, but it works!

ieprompt.png

Advertisements

Even though most browsers are configured to suppress Javascript errors, you want to ensure that you handle these errors effectively.

The best way to do this is to put all of your Javascript code within try/catch statements:

function HandleBillingCodeFieldChange(idx)
    {
        try
        {
            $(“#bc_changed_” + idx.toString()).val(“1”);
        }
        catch(Exception){}
    }

In addition to that, it is always a good idea to have an overall Javascript error suppressor.  You probably only want to enable this in production, however, because it makes it more difficult to detect and fix Javascript errors in development:

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

I recently ran into an issue in which I was loading a SharePoint view into an iFrame in my application.  It was loading just fine, but it was throwing out a random JS conflict error, even though I had taken the above precautions.

I even tried adding an onerror event to my iFrame in an attempt to suppress the error, but it didn’t work.

I was, however, able to find the workaround for this in IE by adding a security=”restricted” attribute to my iframe:

http://”MySource.aspx”

With the advent of AJAX and JSON, we rarely need to keep large amounts of data on the client-side, but there are still occasions when a small amount of dynamic changes are extremely helpful, are not too taxing on client resources, and help improve performance.
I recently discovered that using client-side Array Objects rather than just your typically multi-dimensional arrays in Javascript really brings your client-side code out of the dark ages!

Creating a Client-Side Multi-Dimensional Array (old school)

function PopulateEmployeeArray() {
// initialize variables
   var arEmployees = new Array();
   var $tds = $(“#EmployeesTable”).find(“#EmployeeID”);
   var counter = 0;
   // iterate items
   $tds.each(function () {
      // retrieve values from employee table indexed field values
      var itemIndex = this.getAttribute(“EmployeeIndex”);
      var employeeID = $(“#EmployeeID_” + itemIndex).val();
      var fullName = $(“#EmployeeName_” + itemIndex).val();
      var jobTitle = $(“#EmployeeTitle_” + itemIndex).val();
      var department = $(“#EmployeeDepartment_” + itemIndex).val();
      // populate array
      arEmployees[counter] = new Array(5);
      arEmployees [counter][0] = itemIndex;
      arEmployees[counter][1] = employeeID;
      arEmployees[counter][2] = fullName;
      arEmployees[counter][3] = jobTitle;
      arEmployees[counter][4] = department;
      // increment counter
      _count++;
   });
   // return array
   return arEmployees;
}

Creating a Client-Side Multi-Dimensional Array Object (better)

function PopulateEmployeeArray() {
// initialize variables
   var arEmployees = new Array();
   var $tds = $(“#EmployeesTable”).find(“#EmployeeID”);
   // iterate items
   $tds.each(function () {
      // retrieve values from employee table indexed field values
      var itemIndex = this.getAttribute(“EmployeeIndex”);
      var employeeId = $(“#EmployeeID_” + itemIndex).val();
      var fullName = $(“#EmployeeName_” + itemIndex).val();
      var jobTitle = $(“#EmployeeTitle_” + itemIndex).val();
      var department = $(“#EmployeeDepartment_” + itemIndex).val();
      // populate array
      arEmployees[arEmployees.length] = { idx: itemIndex, id: employeeId, name: fullName, title: jobTitle, dept: department };
   });
   // return array
   return arEmployees;
}
Definitely better looking, but look how much nicer it is to access a value. No more remembering index numbers or fear of adding new items. And much more readable!

Retrieving Client-Side Multi-Dimensional Array Value (old school)

var employeeName = arEmployees[0][2];

Retrieving Client-Side Multi-Dimensional Array Object Value (better)

var employeeName = arEmployees[0][‘name’];
Finally, thanks to jQuery, searching multidimensional array has never been easier. Check out the new jQuery grep function.

Searching Client-Side Multi-Dimensional Array Value (old school)

function FindEmployee(employeeName) {
// initialize variables
   var arEmployees = PopulateEmployeeArray();
   // iterate items
   for(var i = 0; i< arEmployees.length; i++) {
      if (arEmployees[i][2] == employeeName)
      {
         return arEmployees[i];
      }
   }
}

Retrieving Client-Side Multi-Dimensional Array Object Value (better)

function FindEmployee(employeeName) {
// initialize variables
   var arEmployees = PopulateEmployeeArray();
   return jQuery.grep(arEmployees, function (a) { return a[‘name’] == employeeName; });
}
With jQuery grep, you can return multiple records. For example, if you wanted to search for a subset of records based on department.
function FindDepartmentEmployees(department) {

   // initialize variables
   var arEmployees = PopulateEmployeeArray();
   return jQuery.grep(arEmployees, function (a) { return a[‘dept’] == department; });
}
You can also search on multiple attributes.
function FindEmployeeDepartmentRoles(department, jobTitle) {

   // initialize variables
   var arEmployees = PopulateEmployeeArray();
   return jQuery.grep(arEmployees, function (a) { return a[‘dept’] == department && a[‘title’] == jobTitle; });
}
I also use it to make sure there are no duplicate records.

function IsEmployeeDuplicate(employeeID) {

   // initialize variables
   var arEmployees = PopulateEmployeeArray();
   arEmployees = jQuery.grep(arEmployees, function (a) { return a[‘id’] == employeeID; });
   return (arEmployees.length > 1);
}
Powerful AND cool. J
I love using jquery.tablesorter to add quick sorting to my reports. It is fast and super easy and doesn’t require a postback or AJAX calls. But every once in a while, I run into a limitation. For example, it seems to be sorting my currency values as if they were text.
For example:
Instead of sorting them like this –
$ 50.00
$ 75.00
$ 300.00
$ 1,000.00
It is sorting them like this –
$ 1,000.00
$ 300.00
$ 50.00
$ 75.00
To resolve this issue, you can add a custom parser like so:
$.tablesorter.addParser({
// set a unique id
  id: ‘currency’,
is: function (s) {
    // return false so this parser is not auto detected
    return false;
  },
  format: function (s) {
    // format your data for normalization
return s.replace(‘$’, ”).replace(/,/g, ”);
  },
  // set type, either numeric or text
  type: ‘decimal’
});
$(“#MyReport”).tablesorter({
  // sort on the first column and third column, order asc
  sortList: [[1, 0], [2, 0]],
  // add custom sort (zero-based index) – this will sort the 7th column
  headers: {
    6: {
      sorter: ‘currency’
    } ,
    7: {
      sorter: ‘currency’
    }
  }
});
<table id=”MyReport” class=”tablesorter”>
. . .
</table>
I love using jQuery tablesorter for quick sorting of my report tables. But sometimes it is not able to sort some of my cells properly. For instance, when I use KPI images to reflect Green, Yellow, and Red states. But it is super easy to extend the tablesorter logic to add custom sort functionality by using the addParser method.
In this example, we will convert our KPI images of Green, Yellow and Red to the following values: 1, 2, 3. That way they will sort Green, Yellow, Red for ascending and Red, Yellow, Green for descending sort.
This defines the custom parser to handle KPI data and assigns it an ID of kpi.
$.tablesorter.addParser({
  // set a unique id
  id: ‘kpi’,
  is: function (s) {
    // return false so this parser is not auto detected
    return false;
    },
  format: function (s) {
    // format your data for normalization
    if (s.toLowerCase().indexOf(“green”) != -1) {
      return 1;
    }
    else if (s.toLowerCase().indexOf(“yellow”) != -1) {
      return 2;
    }
    else if (s.toLowerCase().indexOf(“red”) != -1) {
      return 3;
    }
    else {
      return -1;
    }
  },
  // set type, either numeric or text
  type: ‘numeric’
});
Once the custom parser has been defined, we can tell the tablesorter which column we want to use this logic on. My report table has an id of UnforecastedRevenueSummary.  Also, I have to add a text extraction item here.  Otherwise, the tablesorter tends to ignore markup and would not be able to sort on what is contained in an image.
$(“#UnforecastedRevenueSummary”).tablesorter({
  // text extraction to handle image, otherwise markup tends to be ignored
   textExtraction:function(s){
var $el = $(s),
$img = $e1.find(‘img’);
return $img.length ? $img[0].src : $e1.text();
},
 
  // here i am assigning the default sort
  sortList: [[1, 0], [2, 0], [3, 0]],
  // here i am stating that the very first column will use the custom kpi sort (columns contain a zero index)
  headers: {
    0: {
      sorter: ‘kpi’
    }
  }
});
<tableid=”UnforecastedRevenueSummary”class=”report tablesorter”style=”table-layout:fixed;”>
. . .
</table>

The following is a very simple example of using jQuery to remove HTML Tags from your content:

<!DOCTYPEhtmlPUBLIC”-//W3C//DTD XHTML 1.0 Strict//EN””http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”&gt;

<html>

<head>

<title>Strip HTML</title>

$(document).ready(function () {

var htmlString = $(“#textbox1”).val();

/* replace escaped brackets with real ones */

var htmlString2 = htmlString.replace(/&(lt|gt);/g, function (strMatch, p1) {

return (p1 == “lt”) ? “”;

});

$(“#textbox2”).val(htmlString2);

/* strip text */

var htmlString3 = htmlString2.replace(/]+(>|$)/g, “”);

$(“#textbox3”).val(htmlString3);

});

</head>

<body>

    <inputtype=”text”id=”textbox1″name=”textbox1″style=”width:350px;height:40px;”value=”<br><strong>This is a test</strong><p>”/><br/>

    <inputtype=”text”id=”textbox2″name=”textbox2″style=”width:350px;height:40px;”value=””/><br/>

    <inputtype=”text”id=”textbox3″name=”textbox3″style=”width:350px;height:40px;”value=””/>

</body>

</html>

And here is the result: