Accessible Ajax With Domino Forms [ROUGH CUT]
Ajax is great. Of that there's little doubt. However, it has to be used carefully in order to be most effective. In particular we need to make sure its use does not impeed users for whom it might not work.
A lot of the projects I've been involved in recently have required a degree of accessibility built in. Rather than deny the majority of users the funkiness that using Ajax can provide I've come up with a way to use a single Domino form to provide simple functionality to both Ajax actions as well as traditional methods. This article is going to look at how I did that.
Ajax is best and most often used for the simple little tasks. For example, something like adding a vote to a poll is an ideal situation to which Ajax is suited.
Imagine you were reading a document on which a vote is being taken. It might include a Vote Here link which looks something like this:
<a href="#" onclick="doVote('yes')">Vote Here</a>It looks innocent enough, but there's a problem with this link. Once the doVote() function has run the link will still be followed and the user will end up at reloading the page at the same URL but with an empty hash (#) at the end of it.
To prevent this the link should look like this:
<a href="#" onclick="doVote('yes'); return false;">Vote Here</a>Now the doVote() function runs and then the click of the link is effectively cancelled by returning a false statement.
If you're going to link to JavaScript calls in the onclick event of a link. Always make sure your return false after the function call!
The link is now less prone to error but there's still a problem with it. Imagine either the doVote() function causes an error — for whatever reason — or that the user has no JavaScript capability — again, for whatever reason. What do we do now?
What I've started doing and what I propose we all do from now on is cater for all cases with a link that looks something like this:
<a href="vote?CreateDocument&vote=yes" onclick="doVote('yes'); return false;">Vote Here</a>Now, when there's no JavaScript support or when an error occurs in the JavaScript it fails-over to a good old fashioned link. In effect the two methods do the same thing. All that the doVote() function does is POST to the "vote" form and pass same value to it. Here's what the code might look like (based on Mootools here, but would be the same when using most of the libraries):
function doVote(vote){
new Ajax(
'vote?CreateDocument',
{
method: 'post',
data: {'vote':vote, 'ajax':'true'},
onComplete:function(response){
$('vote_link').innerHTML=response;
}
}
).request();
}
We're still creating a vote "form" and passing it the vote. The only difference is that we tell the form that Ajax was used. Using the information the WQS agent of the Vote form knows whether to simply return some text to the browser or to perform a redirect.
The Vote form itself is just a place holder really. It doesn't store documents (SaveOptions="0"). All it does is capture the values passed to it and then make them easily available to the WebQuerySave agent that runs. We could pass the data straight to an agent but parsing it in LotusScript is a nightmare and it's much better to let a Form do it. As well as the data passed to it we can use the Form to compute other information such as the user name, path info etc.
Fields on the form to which data are passed are auto-filled by the POST action of the Ajax request. However, when we use a ?CreateDocument GET request we have to fill the fields ourselves. To do this we set their Default Values using @URLQueryString calls to grab data from the URL.
Taking Further
What next?
Demo Database
DEMO HERE -- Download link is on the right towards to the top.
To Do On This Article
- Talk about side-effect of web crawlers following links like this. Rel=nofollow?
Ajax With Domino Forms Rough Cut
Great article. You may want to correct the typo "impeed" to "impede"
Mike.
Reply
RE: Accessible Ajax With Domino Forms
another "simple yet useful" tip brought to you by Jake Howlett... :D
Reply
Good approach
Good approach Jake. Graceful fail over is so important to good UI design and this is a sensible pattern for achieving that.
As for crawlers following the link, most crawlers drop the values after the ? to avoid false submissions of data. You just need a graceful way to respond to GETs that come with no ?CreateDocument&vote=yes.
It's worth noting - what I am describing is GOOD practice for the more well behaved crawlers. Once upon a time a poorly written crawler trawled my blog and created about 300 bogus empty comments. Input validation eventually solved that problem for me. So, hindsight, even if we assume crawlers will drop the queryparms, there may need to be some other counter measure such as a bot rule or meta tag.
Reply
Close...
But I wouldn't want a GET request to cause an action like that - I would prefer the action only be taken with a POST, so I deally you'd be using a button to cause a submit. This can complicate things, but you've even posted about it before:
http://www.codestore.net/store.nsf/unid/BLOG-20051118?OpenDocument
Reply
Re: Close...
I know it's not ideal and I was aware of that post when I wrote this. However it's all horses for courses isn't it. It depends where you application is going to be used and if crawlers will ever visit.
I want to add to the demo/article a form as well as a link, which does the same thing. In the same way you can "return false" in the onclick of an <a> link you can in the onclick of an <input type="submit"> button as well. So we can use the same approach for a real form as well.
Jake
Reply
Ajax and Mac
Jake,
I've converted JSLister to a menu with some modifications to the the JavaScript library, the forms and the views. It works great in IE on my PC, however when I test it on my Mac using Safari it only returns 'undefined'.
Does Safari know Ajax? ;)
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
-1' OR 2+814-814-1
Reply
(select(0)from(select(sleep(15)))v)/*'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"*/
Reply
-1; waitfor delay '0:0:15' --
Reply
-1); waitfor delay '0:0:15' --
Reply
1 waitfor delay '0:0:15' --
Reply
hcFhl51J'; waitfor delay '0:0:15' --
Reply
555*DBMS_PIPE.RECEIVE_MESSAGE(CHR(99)||CHR(99)||CHR(99),15)
Reply
555'||DBMS_PIPE.RECEIVE_MESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'
Reply
1'"
Reply
@@i2plT
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply
555
Reply