Viewing Category: JavaScript  [clear category selection]

"For a Limited Time!!" Adding a CSS class for a short duration.

I recently had a reason to create a visual effect in which a bit of text pulsed with a bright color once. I wanted to use CSS3 animation to be kind to the CPU and provide a smooth transition from the text's normal color to the highlight color. I could have used jQuery's animation mechanism, which steps through states at 13 millisecond intervals by default. My plan was to create a callback function to remove the class after a pulse from normal to highlighted and back to normal. I came up with two ways to add the class to an element, the remove it a second later: using a native JavaScript setTimeout (jsFiddle), and using the jQuery animation queue (jsFiddle). I tried to get the jQuery UI Effects enhancements to work (see line 4321 in jQuery UI 1.8.16), but it didn't function properly for some reason.

Here's a little bit of HTML:

<span id="pulseable">Some Pulsating Text</span> <input type="button" id="run" value="Run Example"/>

Here's the stylesheet that creates the pulsating text:

#pulseable {   color: black; }   .pulse {   -moz-animation: pulse 1s infinite linear;   -webkit-animation: pulse 1s infinite linear; }   @-moz-keyframes pulse {   0% { color: black; }   50% { color: yellow; }   100% { color: black; } }   @-webkit-keyframes pulse {   0% { color: black; }   50% { color: yellow; }   100% { color: black; } }

Here's the old-school JavaScript:

var p = document.getElementById("pulseable");   document.getElementById("run").onclick = function () {   p.className = "pulse";   window.setTimeout(function () { p.className = ""; }, 1000);   return false; };

And here's a more convenient way using jQuery, which is nice because it stops any pending queued events on the callback:

$("#run").bind("click", function (event) {   $(this).prev().addClass("pulse").delay(1000).queue(function () {     $(this).stop().removeClass("pulse");   }); });

jQuery.data() and integer lists and strings and numbers. Oh my.

The jQuery Data feature is pretty awesome. However, there's a gotcha that has, um, gotten me several times when stuffing an HTML5 data attribute with a list of integers that I expect to parse as an array in my JavaScript. For example, consider the following:

<div class="person" data-id="1" data-badges="5,18,6,4">   <p>     <strong>A</strong>     <a href="">Select</a>   </p> </div>

I might have a bit of JavaScript that grabs the badges data and splits it into an array, like so:

$(".person").data("badges").split(",")

That is all well and good. However, when the person has just one badge, jQuery.data() returns a number, rather than a string. Obviously, there is no split() method on the number object. To protect against that case, I've started using the following defensive coding technique:

($(".person").data("badges") + "").split(",")

Another possibility would be to snag the split() method from the string object and give it the data as its this, as in the following:

"".split.call($(".person").data("badges"), ",")

I've created a code sample on jsFiddle to make this more of a real-world scenario. Player A (id=1) has four badges, and everything works as expected. Player B (id-=2) has just one badge, and the string concatenation trick works to solve the problem of calling split() on a number. Player C (id=3), however, has a problem -- splitting an empty string creates an array with one element of an empty string. We could deal with that by checking the length of the string first, like in this jsFiddle. It's ugly, and there's probably a more elegant solution. Perhaps I will create a jQuery Plugin that would wrap jQuery.data() in some logic that would take care of this attribute checking automatically.

User Interface Elements

The way that myOpenID does user feedback on pages with forms is understated and functional. In their HTML source, they give this content the “alert box” class, and have a link to dismiss the message at the upper right. This seems very Facebook-esque, which leads me to believe it's a widely seen/used/understood user interface element. Not too long ago, I coded a site that had UI messages very similar to this, with a jQuery timer that hid the message after a few seconds. I'd bet that no matter how many seconds it waited to remove the message, it was wrong. It also slid content up the page without a user input — another no-no.

I should note that the green trim around the main content area is clipped weirdly because of my screenshot dimensions, not due to their design. It looks much better with the rest of the site chrome.

Dynamic favicon Loading

It hadn't occurred to me previously, but today I wanted to dynamically the change the favicon file used on a site/page. It's been possible using JavaScript in Firefox and Opera for at least three years, and there is a really clean library by Michael Mahemoff that makes the task trivial. This method doesn't work in Microsoft Internet Explorer or Safari. There might be another mechanism to dynamically add and remove link elements to the document head, but I haven't seen it yet.

While searching for information, I happened upon DEFENDER of the favicon -- a classic arcade game played through a 16x16 pixel portal. Crazy.

jQuery Messaging Plugin Screencast

I've been working on an enterprise information management application. One of the requirements on the form validation is to gracefully handle client-side and server-side validation. The form uses the jQuery Validate and Form plugins. The plugin I wrote handles the receipt of server-side validation messages, and updates the UI in the same was as the client-side validation. A screencast showing how it works is now available on Vimeo.

The plugin is short, so I'll just post it here:

(function($) { $.extend({ messaging : function(action, data) { var defaults = { feedback: "#feedback", status: "#status", form: "form", validator: "validator" }; if (action == undefined || action == "") { action = "config"; } if (action == "config") { this.settings = $.extend({}, defaults, data); var $status = $(this.settings.status).hide().removeClass("hidden"); var $feedback = $(this.settings.feedback); $().ajaxStart(function() { $status.show(); }).ajaxStop(function() { $status.hide(); }).ajaxError(function(ajaxError, XMLHttpRequest, ajaxOptions, errorText) { $status.hide(); $feedback.removeClass().addClass("error").text("An error occurred while posting the form."); }); } if (action == "receive") { var $message = $("message", data); var $feedback = $(this.settings.feedback); var $form = $(this.settings.form); var errors = new Object(); if ($message.children("status").text() == "success") { $feedback.removeClass().addClass("success").text($message.children("text").text()); } else { $feedback.removeClass().addClass("error").text($message.children("text").text()); $message.find("validationMessages message").each(function() { var $msg = $(this); errors[$msg.attr("fieldName")] = $msg.text(); }); if ($message.children("validationMessages").attr("count") > 0) { $form.data(this.settings.validator).showErrors(errors); } } } } }) })(jQuery);

Here is the call to configure the plugin from the main document:

$.messaging("config", { form: "#accountForm" });

The integration with the Validator seems a little kludgey, but after trying many ways, I settled on this.

$form.data("validator", $form.validate(validationOptions));

The callback from the AJAX submit passes the XML to the messaging plugin:

var validationOptions = { submitHandler: function(form) { $(form).ajaxSubmit({ dataType: "xml", success: function(data, status) { $.messaging("receive", data); } }); } };

I hope that's useful. I'll keep updating the plugin and post a link to its Subversion repository.

jQuery UI Dialog Widget Screencast

I just uploaded a screencast showing how I use jQuery UI to implement a simple “popup” window that allows the user to verfiy the password of an account in an LDAP server. While working in it, I didn't see many examples showing the best practice for passing data from a link in the page to the dynamically created dialog widget. View the screencast with the following link to Vimeo.

Consider the following link that might exist for each user in a group of records:

<a href="#username:fred" class="verify-password">Verify Password</a>

In the jQuery code executed when the page is loaded, it wires up the click event for the link:

$("a.verify-password").click( function() { var username = this.hash.split(':')[1]; $("#verifyPassword").dialog("open").data("username", username); return false; } );

Notice the call to the jQuery.data() method. This is the connection between the link and the dialog widget created by its click event. Here's the simple container that is wrapped up within the dialog widget:

<div id="verifyPassword"> <form name="passwordForm" onsubmit="return false;"> <input type="password" name="password"/> </form> <p class="result"></p> </div>

To put it all together, here is the code that configures the dialog widget. Notice that the Verify button makes use of the username data.

$("#verifyPassword").dialog({ title: "Verify Password", buttons: { "Verify": function() { $.ajax({ data: { var $v = $("#verifyPassword"); username: $v.data("username"), password: $v.find("input[name='password']").val() }, success: function(data, status) { // Do some interesting things with data } }); }, } });

The actual implementation has a whole lot more detail. I tried to strip out everything that wasn't necessary to communicate the dialog widget usage.

Offline Warning with JavaScript

The requirement is to check whether an HTML file has been loaded from a local source, say a CD-ROM, or from a website. It's possible to test the document.location.protocol value and act accordingly. This doesn't actually check to see if a valid network connection exists -- that would require a different test to attempt loading something from a remote site. The handleLinkClick function just evaluates the protocol to be used when a link from a local file is clicked.

<script> function handleLinkClick(link) { if (document.location.protocol == "file:" && link.protocol != "file:") { return confirm("This link requires Internet access.\nClick cancel if you're not online."); } return true; } </script>

In the body of the local document, the links would be updated with the onclick attribute. Technically, only the remote links need to be changed, but if a global search-and-replace didn't differentiate, that would be fine.

<a href="http://www.google.com" onclick="return handleLinkClick(this);">Remote Site</a><br/> <a href="file.html" onclick="return handleLinkClick(this);">Local File</a>

Unfortunately, if the user doesn't follow the instructions, and clicks OK when they don't actually have Internet access, they'll get an error from their web browser about being unable to access the page. Can't help you there...