Prototype.js, onComplete and anonymous functions

This article is about Prototype.js it's Ajax.Updater and the property. We all know we can use something like oncomplete:someFunction to happily execute a function someFunction() defined somewhere in our Javascript code. But what if someFunction() likes to receive some arguments? onComplete.someOtherFunction(arg1, arg2) isn't going to make us happy as we're about to see. But dear browser... I really, REALLY need to be able to throw some arguments at the functions I call after the Ajax.Updater has finished loading! Anonymous functions to the rescue! In this article I'll explain what I learned about those recently and how they helped me solve the above problem. The article might or might not be usable for you. I'm not only writing it to push some fresh content onto my weblog but most importantly I'm writing it in order to consolidate my knowledge. Wanna join me? Read on...

In the past six months I've been digging deeper and deeper into and particularly . While on one hand a library such as Prototype.js is really great, it might sometimes obscure things a bit, resulting in a developer being able to use it but not fully understand all aspects of advanced Javascript programming. One thing I completely failed to understand was why using anything besides a reference to a function in on Prototype.js' Ajax.Updater failed so miserably. Let's take a look at the original problem first.

Example 1: Common use of Ajax.Updater

Let's take a look at the following snippet of code. Let's also forget about the evil of using inline Javascript just for a short while. We'll deal with that later.

<a id="clickme" href="#" οnclick="resetElement();
new Ajax.Updater($('updateme'), 'ajax.html',
{method:'get', onComplete:alertGreeting});
return false;">Click me!</a>
<div id="updateme">I am gonna get updated</div>

Besides Prototype.js there's some extra Javascript included in the header of the example document that contains the above HTML. These functions are relevant to the above example:

function beenUpdated()    {
if($('updateme').innerHTML ==
"<b>I'm some stuff loaded by Ajax.Updater</b>") {
alert('content has already been updated');
}
else {
alert('content has not been updated yet');
}
}
function resetElement() {
$('updateme').innerHTML = 'I am gonna get updated';
}
function alertGreeting() {
alert('Javascript says hi!');
beenUpdated();
}

view the entire example

When trying out the example we'll see that everything works as expected. Clicking the big red button causes the grey area below it to receive some new content through an (Ajax.Updater). On completion we call alertGreeting() which pops up a cheery greeting. Before doing that, it calls the function beenUpdated() which investigates what's inside our little grey area. We use it in order to determine whether our timing is right. If you try out example 1 you'll see everything's fine. After receiving the cheery greeting, beenUpdated() tells us that the content of our grey little area has already been updated. This is exactly what we expect because all of this needs to happen AFTER Ajax.Updater has changed the content of our grey little div. Ugly inline code but... so far so good!

Example 2: I'd like to have an argument!

I don't know about you, but after using Prototype.js for a while a nagging problem introduced itself to me. We're keeping the same Javascript but we're going to attempt to throw a function with an argument at Ajax.Updater's onComplete. Let's look at the following snippet of code:

<a id="clickme" href="#" οnclick="resetElement();
new Ajax.Updater($('updateme'), 'ajax.html',
{method:'get', onComplete:alertArgument('you clicked that thing!')});
return false;">Click me!</a>
<div id="updateme">I am gonna get updated</div>

view the example in action!

For completeness' sake here's the alertArgument() function. It's essentially alertGreeting() with a custom text by means of an argument.

function alertArgument(myString)	{
alert(myString);
beenUpdated();
}

This code poses some 'interesting problems' to people who are trying to grasp the nitty gritty on Prototype.js. At least, it did to me when I first encountered it. The code doesn't throw any errors at us when executed. It hower doesn't do what we expected it to do! We've tried to call alertArgument() with a little text string in our onComplete property. While not resulting in any syntax errors, the code isn't actually executed AFTER the Ajax.Updater has finished doing it's thing. It's executed IMMEDIATELY after clicking the red button. or test function beenUpdated() verifies this problem by telling us the content has NOT been updated yet. This wasn't what we wanted! How do we get around this problem?

Example 3: Anonymous functions to the rescue

Javascript has an interesting feature called . "Anonymous?", you may wonder. They're called anonymous because they don't have a name. Let's take a look at a third example.

<a id="clickme" href="#" οnclick="resetElement();
new Ajax.Updater($('updateme'), 'ajax.html',
{method:'get',
onComplete:function()
{alertArgument('you clicked that thing!');}});">Click me!</a>
<div id="updateme">I am gonna get updated</div>

view the example in action!

The interesting part is in the onComplete property. We've enclosed the stuff we wanted to do in our previous example inside a nameless function. When running the example we find that the anonymous function makes all the difference. Our beenUpdated() function reports that the content of the grey area has been updated, very much unlike example 2 did.

The solution turned out to be a lot easier than I expected. I just didn't know about the concept of anonymous functions until recently. Now we've got one more thing to do. Let's clean up the code and get rid of all the inline Javascript. There's an additional role reserved for the anonymous functions as we're about to discover.

Example 4: Cleaning up

Being serious developers we want to separate all Javascript from our (x)html code. Let's take a look at the last example. We've completely gotten rid of all Javascript in the xhtml.

<a id="clickme" href="#">click me</a>
<div id="updateme">I am gonna get updated</div>

I guess we can be proud. No Javascript to be found at any wrong place anymore. Let's look at our script as well!

// The right way to add load events. By Simon Willison
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
}
else {
window.onload = function() {
oldonload();
func();
}
}
}

// attach an event properly

function attachEvent(element, type, event) {
if(element.attachEvent) {
element.attachEvent('on' + type, event);
}
else {
element.addEventListener(type, event, false);
}
}

function alertArgument(myString) {
alert(myString);
}

function attachBehaviors() {
attachEvent($('clickme'), "click", function() {
new Ajax.Updater($('updateme'), 'ajax.html', {
method:'get', onComplete:function() {
alertArgument('You clicked that thing!')
}
})
});
}

addLoadEvent(attachBehaviors);

view the example in action!

In the final Javascript we're using Simon Willison's addLoadEvent() function to properly execute our attachBehaviors() function after the document has finished loading. Note that Simon's function is much nicer than simply setting window.onload because that would overwrite any other existing onload events. This one simply adds attachBehaviors() to whatever might already be there.

If we take a closer look at attachBehaviors() we see our newly discovered anonymous functions being used. We can even nest them! The function attachEvent() properly attaches an event to an xhtml element. We're attaching an anonymous function which is executed when clicking the element with ID clickme. Inside this anonymous function we're spawning an Ajax.Updater from Prototype.js. Just like in example 3, we're using an anonymous function that allows us to call a function that requires an argument.

Here we are! Problem solved. We've learned how to be able to call functions that require one or more arguments after completing a request done by Prototype.js' Ajax.Updater and in the process we've seen how to use the same mechanism to properly attach events to xhtml objects the proper way. Nice, clean and working mighty fine.

I hope you've enjoyed reading this little essay. It would be even nicer if you learned something. I for one definitely did while writing it. In case you spot mistakes or in case you've got anything useful to add to this, by all means use the comments below to discuss things.

A note on IE

I just found that IE (for some reason) doesn't know that the grey area has been updated even though you can see it has. This means that beenUpdated() doesn't report things right in IE. You can however still see the difference between example 2 and example 3. Just look at when the grey area is updated (before or after you get the JS popups)

Happy prototyping!




 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值