Tuesday, September 11, 2007

Ajax & PHP without using the XmlHttpRequest Object

Introduction


Ajax is one of the biggest 'discoveries' in the past year, and it has become a real buzzword, just like Web 2.0 . Admittedly, Ajax can be used for a lot of things, and it really does speed up web applications . Already Ajax is used by many highly popular websites, most notably GMail, but other's like Ta-da List or Flickr also use it. Heck, even Microsoft has gotten wind of the Ajax buzz, and is actually moving towards web-based applications as well.


But there is one problem with most of the current implementations of Ajax: it has one dependency, and that is the XmlHttpRequest object . Most modern browser, like Firefox , have inbuilt support for this object, but older browsers, like Internet Explorer 6, don't have native support for this object. Luckily, IE 6 does support it, but it's built in as an ActiveX control , which means your visitors get an ugly warning message about the possible danger of an ActiveX control, or in some cases it just doesn't work at all.


In this tutorial, I will show you how to use Ajax without even having to use the XmlHttpRequest object.


The basics


If we can't use the XmlHttpRequest object, we must find some other way to include content from another page, without having to resort to other objects or non-standard things. A great candidate for this would be the <script> tag, which is used to include external JavaScript files. What if, instead of using a regular JS file, we point that tag to a PHP file, which outputs JavaScript. A PHP file which looks something like this:


<?php

$html = '<b>This content came from our Ajax Engine</b>' ;


?>


div = document.getElementById ( 'contentdiv' ) ;

div.innerHTML = '<?php echo $html; ?>' ;


When this file is used referenced in a script tag, it will try to set the innerHTML of a div with ID 'contentdiv'. But there's one problem; this file shouldn't be included when the page loads , but only when a button is clicked or some other action. To do this, we must somehow dynamically add a new script tag, which is possible using JavaScript. Something like the following would do the trick:


// Get base url

url = document. location . href ;

xend = url. lastIndexOf ( "/" ) + 1 ;

var base_url = url. substring ( 0 , xend ) ;

function ajax_do ( url ) {

// Does URL begin with http?

if ( url. substring ( 0 , 4 ) != 'http' ) {

                url = base_url + url;

}


// Create new JS element

var jsel = document. createElement ( 'SCRIPT' ) ;

        jsel. type = 'text/javascript' ;

        jsel. src = url;


// Append JS element (therefore executing the 'AJAX' call)

        document. body . appendChild ( jsel ) ;

}


This code first gets the current directory of the url, so we have a base url. The 'ajax_do' function is the thing that does all the work. It first checks whether the url passed to the function points to another domain , or is a relative file.


It then creates a new script element, using the createElement() function. After that it sets the src attribute of the script element, and adds the script element to the body, effectively 'loading' the file that is referenced by the script element.


All we need now is a simple page that triggers the Ajax call, i.e.


<html>

<head>

<title> Demo 1 - The Basic's </title>

<script type = "text/javascript" src = "engine.js" > </script>

</head>


<body>

<div id = "contentdiv" >


</div>


<input type = "button" onclick = "ajax_do ('page1.php');" value = "Get content" / >

</body>

</html>


( View Live Demo )


If you view the live demo, or setup it up yourself, you will notice that it works just like Ajax, and probably even better in IE (no more ActiveX warnings).


Please note that for this to work in IE, the security level can't be at 'High'. It must be at 'Medium' or lower.


Getting a page's content


In the previous example, I had to use some JavaScript in page1.php to set the innerHTML of the content div. But what if you don't want to do that, and simply want to include the content of another page?


That's also possible with our method, except we need to create a PHP script that can help us slightly. This PHP script needs to get the contents of a page, and then output the correct JavaScript to set the innerHTML of an element. My 'getfile.php' looks like this:


<?php

// Get URL and div

if ( ! isset ( $_GET [ 'url' ] ) ) { die ( ) ; } else { $url = $_GET [ 'url' ] ; }

if ( ! isset ( $_GET [ 'el' ] ) ) { die ( ) ; } else { $el = $_GET [ 'el' ] ; }


// Make sure url starts with http


if ( substr ( $url , 0 , 4 ) != 'http' ) {

// Set error

echo 'alert( \' Security error; incorrect URL! \' );' ;

die ( ) ;

}


// Try and get contents

$data = @ file_get_contents ( $url ) ;


if ( $data === false ) {

// Set error

echo 'alert( \' Unable to retrieve "' . $url . '" \' );' ;

die ( ) ;

}


// Escape data

$data = str_replace ( "'" , " \' " , $data ) ;

$data = str_replace ( '"' , "'+String.fromCharCode(34)+'" , $data ) ;

$data = str_replace ( " \r \n " , ' \n ' , $data ) ;

$data = str_replace ( " \r " , ' \n ' , $data ) ;

$data = str_replace ( " \n " , ' \n ' , $data ) ;

?>

el = document.getElementById ( '<?php echo $el; ?>' ) ;

el.innerHTML = '<?php echo $data; ?>' ;


As you can see it first gets the contents of a page, using the file_get_contents() function, and then outputs the right javascript to set the innerHTML of the element.


The Ajax Engine function that works together with this PHP script is the following:


function ajax_get ( url, el ) {

// Has element been passed as object or id-string?

if ( typeof ( el ) == 'string' ) {

                el = document. getElementById ( el ) ;

}

// Valid el?

if ( el == null ) { return false ; }


// Does URL begin with http?

if ( url. substring ( 0 , 4 ) != 'http' ) {

                url = base_url + url;

}


// Create getfile URL

        getfile_url = base_url + 'getfile.php?url=' + escape ( url ) + '&el=' + escape ( el. id ) ;


// Do Ajax

        ajax_do ( getfile_url ) ;


return true ;

}


What this function does is first check whether the element actually exists, then create the url to the getfile.php file, including the page to get the contents from and the id of the element, and then fires of the Ajax call using our previous ajax_do() function. Simply, easy and it just works! Click here to view a live demo of this in action .


An Example: Ajax-based Form


Let's use our Ajax Engine to create a simple form, and automatically validate all the fields on the server-side. My form looks something like this:


<html>

<head>

<title> Form Demo - Showing an Ajax Form </title>

<script type = "text/javascript" src = "engine.js" > </script>

</head>


<script type = "text/javascript" >

                function submit_form() {

                                // Get form values

                                var name = document.getElementById('name').value;

                                var email = document.getElementById('email').value;

                                var website = document.getElementById('website').value; 



                                // Construct URL

                                url = 'handle_form.php?name=' + escape(name) + ' &email=' + escape(email) + '&website=' + escape(website);


                                ajax_get (url, 'result');


                }

</script>


<body>

<div id = "result" >


</div>


<b> Name: </b> <input type = "text" name = "name" id = "name" / >

<b> Email: </b> <input type = "text" name = "email" id = "email" / >

<b> Website: </b> <input type = "text" name = "website" id = "website" / >


<input type = "button" onclick = "submit_form();" value = "Send Form" / >

</body>

</html>


And my form handler script looks like this:


<?php

// Check variables

if ( empty ( $_GET [ 'name' ] ) ) {

die ( '<span style="color:red;">Please fill in your name!</span>' ) ;

}


if ( empty ( $_GET [ 'email' ] ) ) {

die ( '<span style="color:red;">Please fill in your email address!</span>' ) ;

}


if ( empty ( $_GET [ 'website' ] ) ) {

die ( '<span style="color:red;">Please fill in your website!</span>' ) ;

}


echo 'Success! Your form has been submitted!' ;


?>


And that's all it takes. Click here to view the script in action , and leave one of the fields empty to get some result back.


Conclusion


In this tutorial I have shown you a different method of remote scripting, also known as Ajax. This method doesn't have the same disadvantage as the XmlHttpRequest object , which is great, but it does have a few disadvantages of its own. For example, it isn't possible to do a POST request using this method; only GET requests are possible. Another "disadvantage" is that you can't simply get the content of a page, and use it, but as I've shown you, it's possible to work around this with a little help from PHP.


Another great advantage of this method is that it's possible to use cross-domain remote scripting. Unlike the XmlHttpRequest object, which blocks this, you can point your Ajax calls to scripts and pages on other domains . This is often a (big) disadvantage of the XML object.


You can easily extend the Ajax Engine in this tutorial to a full engine that can handle almost any case, and works in every browser. I've tested the engine in Firefox , IE6 and Opera 7.1, and it worked in Firefox and IE6, but not Opera 7.1, although I'm sure that it's fairly easy to have it working in Opera as well.


If you want to discuss this article further, or want my feedback on anything, leave a comment below, or join the fantastic PHPit Forums !


 

No comments:

About Me

Ordinary People that spend much time in the box
Powered By Blogger