Javascript is still required
As a follow on from something I mentioned in this post ( Creating Tag Clouds? The nuts and bolts.. ) I thought I'd share a method of achieving the "AJAX effect", without the AX bit. For those of you who don't know AJAX stands for Asynchronous Javascript And XML, you can find a full description of what it is and how it works on wikipedia ( wikipedia : Ajax (programming) ). However, there is another way to achieve the same effect without using the XMLHttp Request method ( wikipedia : XMLHttpRequest ) which has the advantage that it's able to make cross-(sub)domain requests, which is impossible to do with AJAX, and something that we require for version 3 of our blog software ( b2evolution ) so that it can still fulfil it's "multi-domain" aspect and we can add all the fancy bells and whistles that are in the pipeline as several of these need the ability to communicate with admin which can easily be on a separate (sub)domain from the frontend.
How does it work?
Creating a request is pretty simple, you just add a <script> tag to the pages <body> pointing to the url of your choice. This url can be on any (sub)domain you like as there are no cross-domain restrictions. In the example below the scripts "answer" is going to be pulled from my own blogs url ( innervisions.org.uk ) to show you that it really does work across domains The url can also contain any $_GET ( php.net : $_GET ) variables that you like. This leads us to the 2 limitations of this method :
1) The request can only use $_GET
2) The response must be a javascript response ( see example below )
Once the request hits the server it can do whatever it likes with the variables passed and then "reply" to the requesting page by imitating a javascript file. This basically means that it has to set the correct headers and reply using normal javascript syntax. It's not as hard as it sounds.
Example code
To show you how it works we'll do a really easy example that takes the text typed into a box, sends it to the server which will reverse it and send it back to the calling page. The code on the calling page looks like this :
Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-UK" lang="en-UK">
<head>
<title>test</title>
</head>
<body>
<label for="theText">Type in the text to be reversed : <input type="text" name="theText" id="theText" /></label> <button onclick="reverseMe();">Reverse</button>
<script type="text/javascript">
function reverseMe()
{
var theText = document.getElementById( 'theText' ).value; // grab the text
var the_call = document.createElement( 'script' ); // create script element
the_call.src = 'http://innervisions.org.uk/nonAjax.php?theString='+theText; // set the url
the_call.type = 'text/javascript'; // to be sure to be sure
document.body.appendChild( the_call ); // add script to body and let browser do the rest
}
function serversReply( theAnswer )
{
window.alert( 'The server said : '+theAnswer ); // stun the crowd with a reply from the server
}
</script>
</body>
</html>
The code for the page being called looks like this ( note : it's very basic and it can be "broken", but it's just for example huh? ) :
PHP Code:
<?php
$theText = ( empty( $_GET['theString' ] ) ? '' : $_GET['theString'] ); // grab the text to be reversed
if( $theText )
{ // we have some text, reverse it
$theAnswer = strrev( $theText );// reverse the text
}
else
{ // we don't have any text
$theAnswer = 'Gimme summat to do!'; // inform the user we're bored
}
$theAnswer = str_replace( "'", "\'", $theAnswer ); // single quotes would break the reply
header( 'Content-Type: text/javascript' );// set the correct mime type
echo 'serversReply( \''.$theAnswer.'\');';// trigger the function on the calling page
?>
Great stuff Yabba! You should write a book on this topic 'AJAX without the Ax' - I'm actually more inclined to learn using more advanced JS with appendChild n so forth... the last thing I did was creating dynamic form fields that you could add/remove from a form.
AJAX here I come!
__________________
Joey, DRUNKhooligan - visit blog
Quote:
Originally Posted by tcw987654321
...I mean what should I write in the if-else script? (I am a js-idiot)
Word of warning, if you try adding a radio element then you'll have fun and games with IE ( nothing new there huh? ). If you do hit that problem then you can find a solution here ( Internet Explorer - wasting production time worldwide )
¥
__________________
I may have opened the door, but you entered of your own free will
This seems great. (I tried to give you a bit of rep, but it told me I had to spread the love a little first).
I've been playing with your code. I've taken the javascript out of the body and changed the call a little so that it can be used with other scripts.
I hope this is interesting and it helps some-one.
HTML
HTML Code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html><head><title>test</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><style type="text/css">
* {
margin:0;
padding:0;
}
body {
background-color:white;
}
</style><script type="text/javascript">
function query(q,value) {
var the_call = document.createElement('script'); // create script element
the_call.src = 'nonAjax.php?'+q+'='+value; // set the url
the_call.type = 'text/javascript'; // to be sure to be sure
document.getElementsByTagName('head')[0].appendChild(the_call);
}
function reverseMe(id) {
var theText = document.getElementById(id).value; // grab the text
query('reverse',theText);
}
function reverseMeReply(reply) {
window.alert('The server said : '+reply); // stun the crowd with a reply from the server
}
function shuffleMe(id) {
var theText = document.getElementById(id).value; // grab the text
query('shuffle',theText);
}
function shuffleMeReply(reply) {
window.alert('The server said : '+reply); // stun the crowd with a reply from the server
}
window.onload=function () {
document.getElementById('reverseButton').onclick=function() {reverseMe('reverse')};
document.getElementById('shuffleButton').onclick=function() {shuffleMe('shuffle')};
}
</script></head><body><p><label for="reverse">Type in the text to be reversed : <input type="text" name="reverse" id="reverse" /></label><button id="reverseButton">Reverse</button></p><p><label for="shuffle">Type in the text to be shuffled : <input type="text" name="shuffle" id="shuffle" /></label><button id="shuffleButton">Shuffle</button></p></body></html>
PHP
PHP Code:
<?php
header( 'Content-Type: text/javascript' );// set the correct mime type
if(isset($_GET['reverse'])) {
$rText=$_GET['reverse'];
}
if (!empty($rText)) {
$reply = strrev($rText);// reverse the text
$reply = str_replace( "'", "\'", $reply ); // single quotes would break the reply
echo 'reverseMeReply( \''.$reply.'\');'; // trigger the function on the calling page
}
if(isset($_GET['shuffle'])) {
$sText=$_GET['shuffle'];
}
if (!empty($sText)) {
$reply = str_shuffle($sText);// reverse the text
$reply = str_replace( "'", "\'", $reply ); // single quotes would break the reply
echo 'shuffleMeReply( \''.$reply.'\');'; // trigger the function on the calling page
}
?>
To show you how it's used in the our blogs ( CVS version, so this file is liable to be re-written at any time ), this is our "communications.js", it uses jQuery as that's the js library that was chosen for inclusion in our core :
Code:
/**
* Server communication functions
*
* Ajax without the pain
*
* This file is part of the evoCore framework - {@link http://evocore.net/}
* See also {@link http://sourceforge.net/projects/evocms/}.
*
* {@internal License choice
* - If you have received this file as part of a package, please find the license.txt file in
* the same folder or the closest folder above for complete license terms.
* - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
* then you must choose one of the following licenses before using the file:
* - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
* - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
* }}
*
* @package main
*
* {@internal Below is a list of authors who have contributed to design/coding of this file: }}
* @author yabs {@link http://innervisions.org.uk/ }
*/
var postbacks_count = 0;
/**
* Init : adds required elements to the document tree
*
*/
jQuery(document).ready(function()
{
jQuery( '<div id="server_messages"></div>' ).prependTo( '.pblock' );// placeholder for error/success messages
});
/**
* Sends a javascript request to admin
*
* @param string ctrl Admin control to send request to
* @param string action Action to take
* @param string query_string Any extra data
*/
function SendAdminRequest( ctrl, action, query_string )
{
SendServerRequest( b2evo_dispatcher_url + '?ctrl='+ctrl+'&action='+action+( query_string ? '&'+query_string : '' ) );
}
/**
* Sends a javascript request to the server
*
* @param string the url to request
*/
function SendServerRequest( url )
{
if( url.indexOf( '?' ) )
{ // we already have a query string
url = url + '&';
}
var the_call = document.createElement( 'script' ); // create script element
the_call.src = url+'display_mode=js'; // add flag for js display mode
the_call.type = 'text/javascript'; // to be sure to be sure
document.body.appendChild( the_call ); // add script to body and let browser do the rest
}
/**
* Sends a forms request as javascript request
*
* @param string DOM ID of form to attach to
*/
function AttachServerRequest( whichForm )
{
postbacks_count += 1;
jQuery( '<input type="hidden" name="display_mode" value="js" /><input type="hidden" name="js_target" value="window.parent." /><input type="hidden" name="callback_ID" value="'+postbacks_count+'" />' ).appendTo( '#' + whichForm ); // add our inputs
jQuery( '<iframe id="server_postback_'+postbacks_count+'" name="server_postback_'+postbacks_count+'"></iframe>' ).appendTo( 'body' ); // used for POST requests
jQuery( '#server_postback_'+postbacks_count ).css( { position:'absolute',left:"-1000em",top:"-1000em" } );
jQuery( '#'+whichForm ).attr( 'target', 'server_postback_'+postbacks_count ); // redirect form via hidden iframe
}
/**
* Displays $Messages->display()
*
* @param string message The html to display
*/
function DisplayServerMessages( messages )
{ // display any server messages and highlight them
jQuery( '#server_messages' ).html( messages );
// highlight success message
jQuery( '#server_messages' ).find( '.log_success' ).animate({
backgroundColor: "#88ff88"
},"fast" ).animate({
backgroundColor: "#ffffff"
},"fast", "", function(){jQuery( this ).removeAttr( "style" );
});
// highlight error message
jQuery( '#server_messages' ).find('.log_error' ).animate({
backgroundColor: "#ff8888"
},"fast" ).animate({
backgroundColor: "#ffffff"
},"fast", "", function(){jQuery( this ).removeAttr( "style" );
});
}
function CallbackComplete( which )
{
jQuery( '#server_postback_'+which ).remove();
}
The main thing about it is we can attach the Ajax stuff to any form, which automatically adds a "please reply in javascript" hidden input and then redirects the postback via a hidden <iframe>. This means that all of our forms are automatically upgraded if javascript is available, and if not then they just continue to function as usual.
On the server end it struts it's stuff and then checks if it's been asked for a js reply. If so then it spits out the answer in js and exits. If not then it does the whole "full html page" stuff ... gotta love degradation
¥
Note to self : cure the bug you just noticed in the above code
__________________
I may have opened the door, but you entered of your own free will
For ASP, I tried and can see the result but I couldn't get the translate this line's syntax
Quote:
echo 'shuffleMeReply( \''.$reply.'\');';
anyone?
Anyway, for those who want to try in asp, just create an asp file (ie test.asp) with below code
Code:
<%
Response.ContentType = "text/javascript"
' reply = strReverse(request.querystring("reverse"))
' response.Write "reverseMeReply(reply);"
response.Write "reverseMeReply('This is a reply');"
%>
For the HTML page, I have reduced the code by taking out shuffleMe's stuff and changed the file name to asp.
Code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
* {
margin:0;
padding:0;
}
body {
background-color:white;
}
</style>
<script type="text/javascript">
function query(q,value) {
var the_call = document.createElement('script'); // create script element
the_call.src = 'test.asp?'+q+'='+value; // set the url
the_call.type = 'text/javascript'; // to be sure to be sure
document.getElementsByTagName('head')[0].appendChild(the_call);
}
function reverseMe(id) {
var theText = document.getElementById(id).value; // grab the text
query('reverse',theText);
}
function reverseMeReply(reply) {
window.alert('The server said : '+reply); // stun the crowd with a reply from the server
}
window.onload=function () {
document.getElementById('reverseButton').onclick=function() {reverseMe('reverse')};
}
</script>
</head>
<body>
<label for="reverse">Type in the text to be reversed : <input type="text" name="reverse" id="reverse" /></label> <button id="reverseButton">Reverse</button>
</body>
</html>
Does this idea have a name? ('AJAX without the Ax' is a bit of a mouthful)
Are there are any drawbacks to this?
1) I was trying to break the internet and needed to make cross-domain calls to do it
2) urm ... damn, you mean I need to think of a cool acronym now? ... urm, how about ASCUJ? Asyncronous Server Communication Utilising Javascript
3) As I mentioned on the other thread, there are a few drawbacks
. a) you can only make GET calls, not POST ( unless you use the hidden frame method )
. b) the reply has to be a valid javascript reply ( which helps with "c" )
. c) you really have to watch out for security, it's very simple to open an XSS/CSRF hole.
Quote:
Originally Posted by ngaisteve1
For ASP, I tried and can see the result but I couldn't get the translate this line's syntax
Hmmm... I think you'll have to do a bit better than that.
I did think of AWTAX .... but I hate any sentence that contains tax in it
Quote:
Originally Posted by BonRouge
Is that very different to AJAX? I mean, the AJAX results have to be handled by javascript anyway, don't they?
AJAX replies are meant to be XML, but can also just be normal HTML. Although they're handled by JS they don't have to be in written in JS
Quote:
Originally Posted by BonRouge
I had wondered about security. Maybe you'll need to check the referer... (?)
You have full access to cookies, so you can do your normal "is logged in" style of checks, you could also check the referrer ( some firewalls remove referrer data though, so you can't rely on that as your only defence ). Ideally you'll include a "key", based off your sessions table, and then check for that key during postbacks ... which is probably the route that we'll be taking ... ish.
Quote:
Originally Posted by BonRouge
Really though, I think this is a great idea and you should get a big cash prize or something for it.
Variations of this idea have been around for years, in days of old you used to use javascript to make one-way calls via images, even postbacks via hidden iframes isn't "original" .... but I wouldn't say no to a big cash prize
¥
__________________
I may have opened the door, but you entered of your own free will
AJAX replies are meant to be XML, but can also just be normal HTML. Although they're handled by JS they don't have to be in written in JS
Well... AJAX did once imply that - but there are many AJAX-like systems which don't use XML or even HTML as the transport, and you don't have to use JS (as you've noted) - and there are some who argue that other asynchronous systems (like in Flash or Java) can be grouped into something that a lot of people are just calling Ajax these days...