Posts Tagged ‘Unobtrusive Javascript’
Unobtrusive Javascript: Self-labeling text inputs
Some web sites have self-labeling text input boxes; for example, see the text box at the top right of the page on memberhub. When these self-labeling form fields are empty, they contain helpful text that labels or further explains their purpose, such as “Search” or “Enter your favorite color.” As soon as you click on these fields, the help text vanishes and you can type in a value.
Using unobtrusive Javascript (see introduction), we can add behavior to a text input element to automatically label it with this sort of help text contained within the element. We will take advantage of the title attribute of input elements to do this. The title element is already widely used in many browsers to provide help text in a tool-tip when you hover over an element with your mouse, and we will steal this title text from any input text field to use for self-labeling purposes. Here is a script that accomplishes that:
autolabel.js
Event.onReady(function() { $$('input[type="text"][title]').each(function(inputElement) { var e = inputElement; var color = e.getStyle('color'); var fontStyle = e.getStyle('fontStyle'); if(e.value == e.title) { // FF reload behavior. e.value = ''; } var blank = !$F(e); var blurHandler = function(ev) { blank = !$F(e); if(blank) { e.setStyle({ 'color' : 'darkgray', 'fontStyle' : 'italic' }); e.value = e.title; } } e.observe('focus', function(ev) { if(blank) { if($F(e) == e.title) { e.value = ''; } e.setStyle({ 'color' : color, 'fontStyle' : fontStyle }); } }); e.observe('blur', blurHandler); blurHandler(null); Event.observe(e.form, 'submit', function(ev) { if(blank) { e.value = ''; } }); }); });
Here’s how to use it. Note that you need to include the Prototype and Low Pro Javascript libraries:
example.html
<script src="/js/prototype.js" type="text/javascript"></script> <script src="/js/lowpro.js" type="text/javascript"></script> <script src="/js/autolabel.js" type="text/javascript"></script> . . . <input type="text" name="color" title="Enter your favorite color" />
How it works
The script first searches for all input elements in the document that are of type “text” and also have the title attribute. For each of these, it executes a function to add the self-labeling behavior to the element. This function does a number of things:
- When you refresh a page in Firefox, as long as you don’t do a full reload, Firefox preserves whatever values were previously in form fields. The function tests if the current value of the form field is equal to the title attribute (meaning that someone refreshed the page while the self-labeling description was present in the field, and if so, the function clears the form field.
- For all other purposes, the function uses the variable blank to track whether the field is blank and should have the label inserted. We do this rather than comparing the field’s content to the title tag, in case the user actually types in the value of the title tag (for something like “Search”).
- The function adds a handler for the focus event (cursor enters field). If the field is blank, it clears the label text so the user can enter their own text, and restores the field’s color and font style to their original values. Otherwise, the field contains user text so it is left unchanged.
- The function adds a handler for the blur event (cursor leaves field). If the field is blank, it remembers the fact that it is blank, sets the style of the field so that the text is italic and dark gray (you may modify this as you wish), and then inserts the title text.
- The function adds a handler for the submit event on the field’s form. If the form is submitted and the field is blank, it will be cleared so that the correct value is submitted for the form contents.
Unobtrusive Javascript: Introduction
Unobtrusive Javascript is a way of using Javascript that cleanly separates your Javascript from your HTML code. Instead of coding Javascript actions within your document (e.g., using an onchange=”…” handler), your Javascript actions are contained within an external script that inserts actions into your document based on CSS class names or element ids that you have pre-determined.
For example, you might have a link that toggles the presence of another element:
<script src="/js/prototype.js" type="text/javascript"></script> . . . <a href="#" onclick="$('toggle').toggle(); return false;">Toggle</a> <div id="toggle" style="display:none">Some content here</div>
There are some problems with this, however. First, the code is cluttered with messy Javascript. Second, our code doesn’t fail gracefully if Javascript is not enabled — the “display:none” style will cause our div not to be displayed, but it would be better if the default were for it to be displayed when Javascript is not active. Of course, we could create a body onload action to workaround this problem, but that clutters our HTML with even more Javascript and also causes the toggle behavior to be spread across several elements.
Unobtrusive Javascript solves these problems by defining and collecting the dynamic behavior in a single separate Javascript file. The HTML document now contains only HTML, plus a few additional CSS class selectors that our Unobtrusive Javascript uses to locate elements that it should take action on. Using the above example, we might create an Unobtrusive Javascript solution that looks like this:
autotoggle.js
Event.onReady(function() { $$('.auto_toggle').each(function(elem) { var element = elem; var anchor = $(elem.id + "_anchor"); element.hide() anchor.observe('click', function(ev) { element.toggle(); ev.stop(); }); }); });
example.html
<script src="/js/prototype.js" type="text/javascript"></script> <script src="/js/lowpro.js" type="text/javascript"></script> <script src="/js/autotoggle.js" type="text/javascript"></script> . . . <a href="#" id="toggle_anchor">Toggle</a> <div id="toggle" class="auto_toggle">Some content here</div>
Before we defined an onclick action on our anchor, but now we set a special CSS class on the div. By convention, we give the anchor an id based on the id for the div. Our external Javascript code then finds all divs with the class “auto_toggle”, and inserts an onclick action into the anchor whose id matches the div’s id, with the suffix “_anchor”. If Javascript is not active, the div will be displayed.
Going into more detail, here is what our unobtrusive toggle handler does:
- For DOM help, it uses the Prototype and Low Pro libraries (although it is possible to write unobtrusive Javascript using other libraries like jQuery).
- The call to Low Pro’s Event.onReady() registers a function to be called when the page’s DOM is loaded. We’ve provided an anonymous function to this call that performs our unobtrusive action.
- Our function calls Prototype’s $$() function to locate the list of all elements matching the CSS selector specification “.auto_toggle”; i.e., any element having a class named “auto_toggle”. It then iterates over this list using the Prototype Array.each() method, providing an anonymous element handler function to be called for each element.
- Our element handler creates variables referencing the matching element, and also locating the anchor element that has the id matching the current element’s id with the added suffix “_anchor”. It then calls the Prototype Element.hide() method to ensure that the toggle element starts out hidden.
- Our element handler then calls the Prototype Element.observe() method to register a handler for whenever the toggle anchor is clicked. For our click handler we provide an anonymous function that calls the Prototype Element.toggle() method to toggle the visibility of the target element and then stop the event by calling Event.stop() (stopping the event is the same as returning false in an onclick event; this causes the anchor target URL not to be visited). You may notice that our click handler function is a Javascript closure — it references variables in the calling function, and these variables will be available to it even when the calling function has ended.
This is a simple example, but we could take it further. For example, we could define the anchor as a span element, and our unobtrusive code could replace it with an anchor element only if Javascript is enabled. That way, users without Javascript won’t see anything clickable on the toggle action. Or, if we knew that every toggle-able div had the text “Toggle” for its anchor, we could remove the toggle anchor altogether and have our unobtrusive code insert it.
Apart from keeping our HTML code clean and simple, one of the great advantages of Unobtrusive Javascript is that we can build up a library of powerful unobtrusive actions that we can reuse with minimal changes to our HTML. Growing more complex than the example we’ve considered here, we could insert dynamic pop-up calendars for every form input element that has a “date_calendar” CSS class. Or we could create code to find every textarea element in the document and cause these textareas to be dynamically resized as users type text into them. We’ll start to build up our Unobtrusive Javascript library by considering examples like these in subsequent posts.