TBD - Add links to more in-depth articles about this, plenty to choose from.
Early on, "webmasters" tended to put the HTML, style information, and JavaScript for a page all interwoven together in one source file, something along these lines:
<!-- Excerpt from web page, circa 1996 --> <script language="JavaScript"> <!-- // Hide script from older browsers function checkTheForm() { var frm; frm = document.TheForm; if (frm.firstName.value == '') { alert('Please fill in your first name.'); frm.firstName.focus(); return false; } if (frm.lastName.value == '') { alert('Please fill in your last name.'); frm.lastName.focus(); return false; } return true; } // --> </script> <table border='0' cellpadding='2' cellspacing='1' bgcolor='black'> <form name='TheForm' action='someurl' method='post' onsubmit='return checkTheForm();'> <tr> <td bgcolor='white'>First Name:</td> <td bgcolor='white'><input type='text' name='firstName' size='15'></td> </tr> <tr> <td bgcolor='white'>Last Name:</td> <td bgcolor='white'><input type='text' name='lastName' size='15'></td> </tr> <tr> <td colspan='2' align='center' bgcolor='white'><input type='submit' value='Submit'></td> </tr> </form> </table>
With the rise of CSS, we saw more and more of the style information being moved out of the actual markup, but even through to today we see the JavaScript still interwoven in that way — via onsubmit, onclick, onload, and similar attributes on HTML elements, even when the body of the script has been made "modular" by putting it in its own .js file.
This is obtrusive JavaScript: It requires that the author of the page include JavaScript at appropriate points. This might have been fine for pages written and maintained by a single person, but the skillsets required for coding JavaScript and for designing great-looking, engaging web pages are distinct from one another and are only rarely found to a high standard in the same person. In larger shops, it's more efficient (and frequently more cost-effective) to have page design, graphic design, content development, and software development done by separate teams at separate times.
Hence the rise of unobtrusive JavaScript: JavaScript that isn't interwoven into the markup via onXYZ attributes; instead, events are hooked via Prototype's Event.observe function (which is a value-add on top of the browser's native attachEvent or addEventListener functions).
So okay, hooking things up via code later is all very well and good, but how does the script know what to hook up?
One approach, of course, is that it "just knows." E.g., the script code knows (because it's hardcoded into it) that when loading somepage.php via Ajax.Updater, it needs to hook up handlers on fields X, Y, and Z. This is unobtrusive in the sense that the JavaScript isn't embedded in the HTML, but requires that your script know a lot in advance, and strongly couples the script to the HTML. This means the script has to be done after the HTML (typically), which can be a problem, and makes the overall page fragile in that changes to the HTML can break the script. Still, though, for small teams or small projects, it can be a quick and effective way of getting things done in the short term.
But we can do better. Your JavaScript code can have rules for identifying the things it needs to observe in new content when it's loaded, without having specific knowledge of what's in the content. These rules have lots of thing they can work with (and Prototype puts the full power of CSS3 selectors at your disposal) but probably the most flexible — and familar to page designers — is the class attribute. You determine named behaviors your script will provide, and the page designers apply those as appropriate by assigning the appropriate class to elements.
At a technical level, supporting this is quite straight-forward: Any time content is loaded, locate the elements with the relevant classes (probably via Element.select) and hook up any handlers necessary. There are typically two ways content gets loaded: Via page load, and via Ajax.Updater. You'll want to have a function that looks within a given element for things to hook up, and then call that function on DOM load and from the onComplete callback on your Ajax.Updaters:
Your function to hook up "numbers-only" behavior might look like this:
function hookNumbersOnlyFields(element) { element = $(element); if (element) { element.observe('change', numbersOnlyChangeHandler); element.observe('keypress', numbersOnlyKeypressHandler); /* ...etc... */ } }
Your function to hook your various behaviors might look like this:
function hookBehaviors(element) { element = $(element); if (element) { // Hook up numbers-only validations element.select('.numfield').each(hookNumbersOnlyFields); /* ...other behaviors... */ } }
Here's one way you might call hookBehaviors for the whole document on DOM load:
document.observe('dom:loaded', pageInit); function pageInit() { hookBehaviors(document.body); /* .. other init ... */ }
And a way you might call it from an Ajax.Updater onComplete callback:
new Ajax.Updater('targetElement', 'someurl', { /* parameters, onFailure, etc. */ onComplete: function(response) { hookBehaviors.defer('targetElement'); } });
(Note that the onComplete callback is implemented by calling hookBehaviors via Function.defer — after an update you have to give the browser a moment to finish updating the DOM before you can use the new content.)
When the page designer wants a numbers-only input field, they simply do this:
<input type='text' class='numfield' id='myField' name='myfield' />
And there we have it. From the page designer's point of view, applying a "numbers-only" behavior to an input field becomes essentially the same thing as applying text-decoration changes on mouseover: It's done through classes. This is familiar to the designers and well-supported by the tools they use. The page design and the software development can be done separately at different times and can be amended separately without affecting one another.