PDA

View Full Version : Calling Functions from Functions.



clowes
12-15-2009, 11:42 PM
I am playing around with Ajax in a chat script I am making, and have encountered some loading problems.

I have a function Init() which initiates on load.

This runs getMessages(); and sets an interval of 2 seconds, to continue running it. This gets the messages from the database.

I want to also get details of who is online at a given time from a separate database. I have done the exact same again. I run getOnline(), and then set it to run every 2 seconds using setInterval();.

When I introduce the getOnline();, getMessages(); no longer works... no messages are displayed on the page. As soon as I remove it, it is fine. Likewise if I remove getMessages(); getOnline(); works fine.

Why is this?

I attempted another idea. I had:


setInterval('getMessages()', 2000);
getMessages();

then rather than do the same for getOnline();, I called the function getOnline(); from getMessages();

I.E getMessages is run every 2 seconds, and when called it also runs getOnline();

Once again getOnline() works.. I can see who is online, but I cannot see any messages.

Have I missed something here, or am I doing something wrong.


This is not a code problem: the code works fine, simply they do not work together. Have I missed some intrinsic rule about how things can be called?

Cheers

djr33
12-16-2009, 12:48 AM
When you put something in quotes, then it is not code any more-- it is a string.

function() is a function. 'function()' is just characters-- text.

The first line you posted above won't work.

jscheuer1
12-16-2009, 02:02 AM
In a timeout or interval quotes are valid syntax for function execution, though not preferred. Without seeing the code involved in these two client side functions it would be hard to say for sure, but it sounds like there is a conflict between them, either on the client side, the server side, or both.

Please post a link to the page on your site that contains the problematic code so we can check it out.

That will help in large measure in determining if there is a conflict on the client side, perhaps even resolve this if that is the only issue. If the problem is on the server side, it will at best provide clues, perhaps nothing at all. For debugging the server side we would need to see the server side code involved. That cannot be determined from a link to the page.

djr33
12-16-2009, 05:29 AM
When quotes are used, is the syntax with () also used? In php there are a few functions that use string-names of functions to call them, but they don't use "function()", instead: "function". But, yes, getting off topic. Thanks for clearing that up.

jscheuer1
12-16-2009, 07:00 AM
In javascript it is the opposite of that:


onload = function(){
setTimeout("alert('Cowabunga!')", 3000);
};

and:


function cow(){
alert('Cowabunga!');
}
onload = function(){
setTimeout("cow()", 3000);
};

will work, but it is not encouraged because it uses the eval engine to turn the string into code. Errors can often creep in with that, and nested quotes can become confusing to the eye.

This will also work:


function cow(){
alert('Cowabunga!');
}
onload = function(){
setTimeout(cow, 3000);
};

No quotes, and no (). But it fires with an event for cow, which can become confusing if cow can at times accept parameters. So the preferred method is:


function cow(){
alert('Cowabunga!');
}
onload = function(){
setTimeout(function(){cow();}, 3000);
};

clowes
12-16-2009, 04:51 PM
Interesting posts, but not too useful for this particular problem.

I would post code if it were a code specific question. I will simplify it.

I have 4 functions.

checkWhoIsWriting();
getMessages();
checkRooms();
getRoomName();

Independent of what they do (although it is pretty obvious).

Basically If i use setInterval, or setTimeout to run any ONE of these functions every x seconds, it works fine. If in any capacity I try and have them all run every w,x,y, and z seconds respectively... none work.. the script goes sluggish, things that are meant to happen simply dont etc.

The general question is why with Ajax calling PHP scripts do multiple setInterval/setTimeout calls not work, and what is the workaround?
Thanks

jscheuer1
12-16-2009, 05:45 PM
They probably conflict, we need to see the code.

clowes
12-16-2009, 06:24 PM
function whoiswriting(){
//document.getElementById("state").innerHTML = document.chatform.guestname.value + " is Writing....";

req.open("GET", "writing.php?page=who");
req.onreadystatechange = handlewhowritResp;
req.send(null);

setTimeout('whoiswriting()', 1500);
}

function handlewhowritResp(){
if(req.readyState == 4 && req.status == 200){
document.getElementById("state").innerHTML = req.responseText;
//document.chatform.chatresp.value = req.responseText;
//var txt = document.createTextNode(req.responseText+"\n----------------------------------------\n");
//document.chatform.chatresp.appendChild(txt);
var elem = document.getElementById("state");
elem.scrollTop = elem.scrollHeight;
}
}

That is the whoiswriting() function. The other 3 functions are of the exact same form. I literally copied and pasted the above, changed the php script it calls, and changed the div the response goes to.

I am not quite sure HOW these could be conflicting...
Cheers

jscheuer1
12-16-2009, 06:39 PM
The req object looks to be global. It could be overwriting the same named ones from the other function(s). If that's what's happening, whichever one fires last will be the dominant one. It may not be easy to tell which one is firing last though.

To tell any more I would probably need a link to the page.

clowes
12-16-2009, 07:12 PM
Thanks for the useful response John.

I am new to this, and have been copying/pasting from all over the place.

The start of the script contains:


function makeRequest(){
if(window.XMLHttpRequest){
return new XMLHttpRequest();
} else if(window.ActiveXObject){
return new ActiveXObject("Microsoft.XMLHTTP");
} else {
err("You browser doesn't support XMLHttpRequestObject");
}
}
var req = makeRequest();

Now I have a basic understanding of what this does in terms of what type of request to do dependent on browser.....

After your post, I changed the latter bit to:


var req = makeRequest();
var aeq = makeRequest();
var beq = makeRequest();
var ceq = makeRequest();
var deq = makeRequest();

I then used a different one for each function, and although not perfect... it is working significantly better.

Could you elaborate on why one might (and apparently was) overwriting the others, as I was under the impression that the first code snippet simply said 'we are using ajax' for any and all statements.

Thanks a lot !

jscheuer1
12-16-2009, 07:55 PM
Well the:


var req = makeRequest();

Places one new request object in the global scope, available to all functions. Each one of your:


checkWhoIsWriting();
getMessages();
checkRooms();
getRoomName();

functions took that one request object and overwrote it's properties to be the one's it required.

If you were to (and similar in the other functions):


function whoiswriting(){
//document.getElementById("state").innerHTML = document.chatform.guestname.value + " is Writing....";
var req = makeRequest();
req.open("GET", "writing.php?page=who");
req.onreadystatechange = handlewhowritResp;
req.send(null);

setTimeout('whoiswriting()', 1500);
}

Then req would be local and new in each function.

This all has primarily to do with what is known as scope in javascript and other programming languages. Making a separate version of everything is one way of dealing with the issue, but it is also usually the least efficient one.

You might not be ready for this yet, but you may, so have a look here:

http://www.dynamicdrive.com/forums/showpost.php?p=190183&postcount=23

It probably (with little or no modification) can handle all of your requests with little or no repetitious coding. It also is more robust, selecting the optimal request object for each browser, not just any that might be available, and covers more browsers. It also determines just once which request object that may be, instead of having to choose each time a request is made.

clowes
12-16-2009, 08:39 PM
Absolutely brilliant John. Hit the nail on the head. Sorted the problem, and managed to explain it to a novice with little to no information. Thank you ever so much.

I had a look at the post you linked, and although code wise it goes over the top of my head, I can see what it does, and will have a look at integrating it.

My intial thoughts on a quick look are that

new loadXmlHttp('requested_url', 'target_element_id')

essentially loads the requested_url and puts the results in the div with id target_element_id?

I then image you could do something like:


checkwhoiswriting = new loadXmlHttp('requested_url', 'target_element_id');

setTimeout(checkwhoiswriting,10000);

Is that right or have I missed the point :)
Thanks again.

jscheuer1
12-16-2009, 08:57 PM
That's very close, it would be more like so:


function checkwhoiswriting(){
new loadXmlHttp('requested_url', 'target_element_id');
setTimeout(checkwhoiswriting, 10000);
} ;
checkwhoiswriting();

Though even that could be modularized instead of having to be repeated for each one. However, it's a good start. Before we worry about modularizing it further, we need to make sure we are passing the correct value of 'who' to the request. This (going by the earlier code you were showing):


function checkwhoiswriting(){
new loadXmlHttp('writing.php?page=who', 'state');
setTimeout(checkwhoiswriting, 10000);
} ;
checkwhoiswriting();

is not going to do it.

clowes
12-16-2009, 09:44 PM
John,

Im not sure if your knowldege delves into PHP but I will assume so.

In reading your response, your first code snippet I completely understood.

Your second code snippet I assumed was an example, but you state it 'is not going to do it'

Could you elaborate on why?

Surely it gets the response from the respective part of the writing.php script, and returns it to the 'state' div?

jscheuer1
12-16-2009, 10:54 PM
Let's put it this way, I'm much better at javascript than I am at PHP. But this is still really mostly a question of javascript (about the 'who') only extremely basic PHP is involved. The PHP on writing.php is probably a bit beyond me or at the limits of what I can easily understand. The fact that you are passing writing.php a GET query value via javascript and AJAX though is not. It's obvious, to me at least, that the literal 'who' will not contain the value you want to send.

Now, it's not clear to me and I think you haven't shown/explained enough for me to know where the javascript value that should replace the literal 'who' will come from. But it has to come from somewhere. It can't always be the literal 'who', if it were, 'who' would always be writing and 'who' would be the only person who ever writes.

It would need to be (very loosely now, but still all javascript) something like:


function checkwhoiswriting(){
var who = something that is the value as a string;
new loadXmlHttp('writing.php?page=' + who, 'state');
setTimeout(checkwhoiswriting, 10000);
} ;
checkwhoiswriting();

clowes
12-16-2009, 11:10 PM
Ah, I understand what you are saying.

Basically the only reason it is writing.php?page=who as opposed to writing.php is because I want to be able to have multiple things in one single PHP script. The literal value 'who' for the variable page IS what I want, and does return the data I want each time.



if($_GET[page]=='who'){
//LOOKUP PEOPLE WHO ARE WRITING FROM DB
}

if($_GET[page]=='add'){
//INSERT SOMEONE INTO THE DATABASE
}



So essentially when the logged in user is typing into the message box (keyPress) a JS function is triggered which queries writing.php?page=add. I.E when a user is typing they are added to the database.

I then have another function checkwhoiswriting(); which gets a list of people already in the database.

So although they both query the same script, the data they return is different BECAUSE of the ?page=
Hope that makes sense, and that again I have understood what you are saying.

jscheuer1
12-16-2009, 11:28 PM
That actually makes sense, though as I imagined, it is at my limits of understanding as regards PHP. As long as it all works out, I'm not going to complain.

When I get more time, and if you are interested, I'll look into making this process (on the javascript side) more modular. At the very least, the more efficient setInterval can probably be used. But I'm still not sure whether or not I have enough information for more than that. If we get that far and I don't, I'll ask. However, just using the code we have been talking about so far (with the loadXmlHttp function) is a big improvement if it works out for you. Any questions on that, feel free to ask.

clowes
12-17-2009, 12:02 AM
Thanks a lot for all the help John.

Bed time for me now, but Ill have a play in the morning.. and see what I get.

Cheers

clowes
12-18-2009, 07:34 PM
John,

Finally had a chance to play with your function.



<script language="javascript" type="text/javascript">
<!--
function loadXmlHttp(url, id) {
var f = this;
if (loadXmlHttp.xmlHttp){
f.xmlHttp = loadXmlHttp.xmlHttp();
f.el = document.getElementById(id);
f.xmlHttp.open("GET", url, true);
f.xmlHttp.onreadystatechange = function(){f.stateChanged();};
f.xmlHttp.send(null);
}
else alert('Your browser does not support AJAX!'); // substitute your desired request object unsupported code here
}

loadXmlHttp.xmlHttp = null; loadXmlHttp.re = /^http/.test(window.location.href);
/*@cc_on @*/ // used here and below, limits try/catch to those IE browsers that both benefit from and support it
/*@if(@_jscript_version >= 5) // prevents errors in old browsers that barf on try/catch & problems in IE if Active X disabled
try {loadXmlHttp.ie = window.ActiveXObject}catch(e){};
@end @*/
if (window.XMLHttpRequest && (!loadXmlHttp.ie || loadXmlHttp.re))
loadXmlHttp.xmlHttp = function(){return new XMLHttpRequest();}; // Firefox, Opera 8.0+, Safari, others, IE 7+ when live - this is the standard method
else if (/(object)|(function)/.test(typeof createRequest))
loadXmlHttp.xmlHttp = createRequest; // ICEBrowser, perhaps others
else {
loadXmlHttp.xmlHttp = null;
// Internet Explorer 5 to 6, includes IE 7+ when local //
/*@if(@_jscript_version >= 5)
try{loadXmlHttp.xmlHttp = function(){return new ActiveXObject("Msxml2.XMLHTTP");};}
catch(e){try{loadXmlHttp.xmlHttp = function(){return new ActiveXObject("Microsoft.XMLHTTP");};}catch(e){}}
@end @*/
}

loadXmlHttp.prototype.stateChanged = function(){
if (this.xmlHttp.readyState == 4 && (this.xmlHttp.status == 200 || !loadXmlHttp.re))
this.el.innerHTML = this.xmlHttp.responseText;
}
</script>



<body>
<div id='ajaxDiv'>
<form>
<input type="submit" value="Who is online?" onClick="loadXmlHttp('onlinelist.php', 'ajaxDiv');return false;">
</form>
</div>
</body>



I may be being completely stupid, but the above I would have thought would be the simplest implementation of said function.

onlinelist.php simple says 'Hello 123'

On clicking the button, nothing happens.

Have I lost it?

jscheuer1
12-18-2009, 08:14 PM
Get rid of the highlighted (fine point, but it's invalid):


<script language="javascript" type="text/javascript">

The real problem is you need to use new:


onclick="new loadXmlHttp('onlinelist.php', 'ajaxDiv');return false;"

There could be something else, but that's probably it.

clowes
12-19-2009, 12:32 AM
John,

I did not want to reply having not tested it thoroughly. I now have.

Your suggested fix (as usual) worked.

I am finding the uses of this endless.

I wanted to create a paginated news page. A combination of PHP, and this has made it possible.

I have two pages.

news.php which contains a div called' NewsDiv
getnews.php which uses PHP to fetch all the news.

With some simple onclicks, getnews.php gets the news and puts it in the div on news.php



Prior to this conversation/chat, the pagination system I was using was one off dynamicdrive in fact: http://www.dynamicdrive.com/dynamicindex17/ajaxpaginate/index.htm


It was extremely complicated, really hard to integrate with PHP etc etc.

I was just curious as to the downsides of pretty much basing my website around your function. Is it really slow, or inefficient or something. Surely there must be a negative?

Thanks

jscheuer1
12-19-2009, 02:11 AM
The real negative with this and any other javascript is that if the user doesn't have javascript enabled, your site may be inaccessible to them. However, if you already had a pure PHP solution, that could be a fall back.

The function itself is about as efficient as these things can be. As stated before, it chooses the optimal method for making a request as the page loads so that each request made doesn't have to determine anything about that. It also creates a new instance not only of a request, but also of the final disposition of that request, so it is fairly bulletproof. That's to say you can have several requests going on at one time and they will not conflict unless you do something outside the loadXmlHttp function to cause them to do so.

From a coding point of view, the loadXmlHttp function could be improved to optionally allow POST requests and a designer defined callback (rather than simply always having to send the output to an element). It could also have more response callbacks, like for loading, failure, etc. Right now it only responds if the request is successful.

Slow servers and/or low bandwidth combined with a sluggish computer on the user's end will cause problems, but not much more than they would with an ordinary page. This plagues all AJAX routines, this one probably less so than others because of its efficiency. With AJAX you are imposing another layer to everything you do (not entirely unlike a frameset or iframe). Poor network conditions and/or slow response on the user end will affect it more than an ordinary page, but not much more.

clowes
12-19-2009, 01:53 PM
Well.. my thinking is that given the percentage of users with javascript disabled... I wont bother catering for them. The site looks really good with javascript enabled. It is designed for javascript, and if a user does not want to enable it, it is their loss.

One thing I have encountered is chucks of text.
For example a comments system... actually any user input really.

If a user inputs data into a textarea, how can you use loadXmlHttp to pass said data to a php script to insert it into a database, and then reload a page?

Cheers

jscheuer1
12-19-2009, 02:26 PM
I wouldn't use it for that. Ordinary PHP can do that. If the textarea is in a form that submits to the page itself:


<form action="<?php echo $PHP_SELF; ?>" method="post">
<textarea name="token_for_the_post_data" rows=25 cols=60></textarea><br>
<input type="submit" value="Update">
</form>

Then the page itself could check its post data when it loads for the "token_for_the_post_data" to see if its there and has a value, and to update whatever you want with that.

Refinements on this could include using PHP for setting the post token on the basis of previous actions so that one form could be used successively to update various things, or alternatively the textarea could be repopulated by PHP with the just submitted data.

clowes
12-19-2009, 03:03 PM
Fair enough.

My thinking here however is user experience. Yes PHP can easily do it, but it requires a page reload..

ATM I have used Ajax, so that on one page you have 3 most recent news posts. On clicking the title it uses Ajax to replace the contents of the div with any comments that have been made on said post. It also presents a textarea to add your own comment.

What I was wanting was the ability to post a comment without reloading the page... meaning that once news.php has loaded the user doesnt actually have to reload at all, and can do anything.

Thoughts?
Cheers

jscheuer1
12-19-2009, 05:09 PM
how can you use loadXmlHttp to pass said data to a php script to insert it into a database, and then reload a page?


Yes PHP can easily do it, but it requires a page reload..

Huh? That's what I thought you were asking at first, but then I actually read the post more carefully (see first quote).

Anyways, it cannot. Not in it's present form. Now, I've had no experience doing this, well some (very little) via an almost completely different method (using a form to GET to a PHP page in an unseen iframe that updates a Session variable used in an ongoing javascript process on the top page). I know in theory what to do, it's really not all that different. But I would need to study up on it a bit more before presenting a trial version.

However, certain types of post requests might not be able to be made using AJAX. For these (if any) the method outlined in my previous post or a modification on that where the post request is made to a PHP page in an unseen iframe could be used.

clowes
12-19-2009, 06:53 PM
I had a think about this... and it got me thinking about what I was using before in my chat script (before you showed me your function).


if(document.getElementById){
window.alert = function(text){
err(text);
}
}
function err(text){
document.getElementById("alert").innerHTML = text;
setTimeout('document.getElementById("alert").innerHTML = ""', 3000);
}
function makeRequest(){
if(window.XMLHttpRequest){
return new XMLHttpRequest();
} else if(window.ActiveXObject){
return new ActiveXObject("Microsoft.XMLHTTP");
} else {
err("You browser doesn't support XMLHttpRequestObject");
}
}
var req = makeRequest();
function send(){
if(req.readyState == 4 || req.readyState == 0){
if(document.chatform.guestname.value != "" && document.chatform.chatext.value != ""){
req.open("GET", "send.php?name="+document.chatform.guestname.value+"&msg="+escape(document.chatform.chatext.value));
req.send(null);

new loadXmlHttp('getnewspost.php','NewsDiv');
} else {
alert('Hey where is your name or message??');
}
}
document.chatform.chatext.value = '';
}


Is that not it right there? onclick="send(); return false;"

It would update the database using send.php and the variables pass by GET, then it would run a new loadXmlHttp to reload the news post content (with the new comment).

Surely?

jscheuer1
12-19-2009, 08:21 PM
That will do it via GET, yes, with some improvement though. The query string must be URI Component Encoded (which lengthens the string) in case it contains characters like & or + that otherwise would have a special meaning. The space character may have to be converted to a +. This encoding business isn't critical in all browsers/for all servers, but will be for some. I'm not certain if PHP then has to be told to decode the value(s) or if it does so automatically.

However, I thought your question was via POST not GET:


What I was wanting was the ability to post a comment

But now upon re-reading it I see that was probably not the case. Anyways, POST can handle more characters. GET is limited to, well I've seen different values, and it varies by browser. 2008 was mentioned as one limit in IE, no version number given.

Also, with a POST, headers must be sent and the data is sent as part of the req.send(data). Not as a query string. See how data replaces null? The data must be URI Component Encoded as well. This is all fairly easy to work out, except for the handling of radio and checkboxes. To combine it into one function that can perform both GET and POST requests is a little tricky too.

If you are serious about doing it right and want to use both GET and POST, I would suggest using a library designed for that. The jQuery library looks to be one of these, and is otherwise a very good script library. See:

http://docs.jquery.com/Ajax

I've always wanted to work out a POST request though. I have code now (been working on it off and on today), not completely finsihed, that can handle it, with the exception that it will not present radio and checkboxes in the same format as actually submitting the form would. Instead of an array, it will send the values of any that are checked.

I'm probably not going to work on this much further today, but when I have something decent I'll let you know. I think I will also work it out so it can do a GET like that as well. Not a real good idea for textarea though unless you can guarantee that the encoded length (including the name(s) of the textarea(s) will be under 2000 characters.

clowes
12-19-2009, 11:56 PM
Sorry,

I am not great at explaining myself. I want a user to be able to add a comment to a database via a form. I was not specifically referring to utilizing the POST function, although that is the norm when using forms.

I believe PHP can decode the values itself automatically.

jquery and the like go over the top of my head. Loads of files, loads of complex code, and loads of ne wknowledge. I much prefer to have just what I need, gradually understand it, and build it up.

On that basis, I'd love to be able to see your function on completion.
Cheers for your time.

jscheuer1
12-20-2009, 01:00 PM
Yes, as I looked at it further,I found some things that would probably just be transparent, like PHP understanding what to do with name/value pairs passed as POST or GET encoded URI Component data. There are now so many possible arguments, all of which are optional, that I've switched to using an Object to pass them to the function. This makes it a little trickier to use, but really simplifies its use for any particular purpose. See the comments at the beginning of the code for an explanation and example. But say you have a form you want to post to back to the page itself and see the post result in a div with an id of 'result', you could do:


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
#result {
width: 15em;
height: 1.75em;
border: 1px solid pink;
}
</style>
<script type="text/javascript" src="http_get_post.js"></script>
<script type="text/javascript">
function mcb(){
if (this.success()){
/<\/div>([^<]*)<\/b/.test(this.xmlHttp.responseText);
this.el.innerHTML = RegExp.$1;
}
}
</script>
</head>
<body>
<form action="#" onsubmit="new loadXmlHttp({bust: true, form: this, id: 'result', method: 'post', callback: mcb});return false;">
<input type="text" name="test" value="bob"><br>
<input type="submit" value="Go!">
</form>
<div id="result">

</div>
<?php echo $_POST['test']; ?>
</body>
</html>

Now, the right way to do this would be to add/attach the event to the form. So I will make a function for doing that as part of the overall function. But if you know how to do that, you may do it yourself. If you control the page and know what you are doing, it really doesn't hurt anything doing it this way. Also, knowing me, I'll probably come up with efficiencies and/or other improvements. For now though, here's the function:


/* loadXmlHttp Script 2009 John Davenport Scheuer
as first seen in http://www.dynamicdrive.com/forums/
username: jscheuer1 - This Notice Must Remain for Legal Use
*/

/* reqObject Properties - //all optional, comma delimit
url: page_to_fetch //defaults to this page
method: POST_or_GET //defaults to GET
el: element_to_populate //defaults to none, overrides id
id: id_of_element_to_populate //defaults to none, requires no el
form: form_submitting_data //defaults to none, requires no query
data: encoded_data_to_send_with_POST_request //defaults to none, requires POST and no form
query: encoded_data_to_send_with_GET_request //defaults to none, requires GET, overrides form
callback: function_to_run_on_success //defaults to populate, in default mode requires el or id
bust: true_OR_false //defaults to false, add cache busting time stamp to request?
*/

/* Usage:
new loadXmlHttp({reqObject Properties})
Example:
new loadXmlHttp({url: 'somePage.php', method: 'POST', form: document.forms.myform})
*/

function loadXmlHttp(req){
if(loadXmlHttp.xmlHttp){
var f = this, r;
for(r in req){
if((req.hasOwnProperty && req.hasOwnProperty(r)) || !req.hasOwnProperty){
f[r] = req[r];
}
}
f.xmlHttp = loadXmlHttp.xmlHttp();
f.el = f.el || (f.id? document.getElementById(f.id) : null);
f.method = f.method? f.method.toUpperCase() : 'GET';
f.url = f.url || location.href;
f.bustCache();
f.url += f.method === 'GET' && f.query? f.qa + f.query : '';
if(f.method === 'GET' && !f.query && f.form){
f.encodeData();
f.url += f.qa + f.data;
}
f.xmlHttp.open(f.method, f.url, true);
if(f.callback !== null){
f.xmlHttp.onreadystatechange = typeof f.callback === 'function'?
function(){f.callback.apply(f);} : function(){f.stateChanged();};
}
if(f.method === 'POST'){
if(f.form){
f.encodeData();
}
f.xmlHttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
f.xmlHttp.setRequestHeader('Content-length', f.data? f.data.length : 0);
f.xmlHttp.setRequestHeader('Connection', 'close');
}
f.xmlHttp.send(f.method === 'POST' && f.data? f.data : null);
}
else alert('Your browser does not support AJAX!'); // substitute your desired request object unsupported code here
}

//setup - while parsing:
loadXmlHttp.xmlHttp = null; loadXmlHttp.re = /^http/.test(location.href); loadXmlHttp.qa = /\?/;
/*@cc_on @*/ // used here and below, limits try/catch to those IE browsers that both benefit from and support it
/*@if(@_jscript_version >= 5) // prevents errors in old browsers that barf on try/catch & problems in IE if Active X disabled
try {loadXmlHttp.ie = window.ActiveXObject}catch(e){};
@end @*/
if (window.XMLHttpRequest && (!loadXmlHttp.ie || loadXmlHttp.re))
loadXmlHttp.xmlHttp = function(){return new XMLHttpRequest();}; // Firefox, Opera 8.0+, Safari, others, IE 7+ when live - this is the standard method
else if (/(object)|(function)/.test(typeof createRequest))
loadXmlHttp.xmlHttp = createRequest; // ICEBrowser, perhaps others
else {
loadXmlHttp.xmlHttp = null;
// Internet Explorer 5 to 6, includes IE 7+ when local //
/*@if(@_jscript_version >= 5)
try{loadXmlHttp.xmlHttp = function(){return new ActiveXObject("Msxml2.XMLHTTP");};}
catch(e){try{loadXmlHttp.xmlHttp = function(){return new ActiveXObject("Microsoft.XMLHTTP");};}catch(e){}}
@end @*/
}

loadXmlHttp.prototype = {
stateChanged: function(){
if (this.el && this.xmlHttp.readyState == 4 && (this.xmlHttp.status == 200 || !loadXmlHttp.re)){
this.el.innerHTML = this.xmlHttp.responseText;
}
},
encodeData: function(){
var els = this.form.elements; this.data = '';
for(var i = 0; i < els.length; ++i){
if(els[i].name){
this.data += (i? '&' : '') + els[i].name + '=' + encodeURIComponent(els[i].value);
}
}
},
bustCache: function(){
this.qa = loadXmlHttp.qa.test(this.url)? '&' : '?';
if(this.bust){
this.url += this.qa + 'bustacache=' + new Date().getTime();
this.qa = '&';
}
},
success: function(require_el){
return (this.el || require_el === false) && this.xmlHttp.readyState == 4 && (this.xmlHttp.status == 200 || !loadXmlHttp.re);
}
};
//end setup

I've tested this pretty thoroughly, though it may have some kinks in it yet. If you have questions, problems or suggestions, feel free.

clowes
12-20-2009, 03:36 PM
John,

I have created the neccesary file, and played with your demo. It does exactly what I need.. in some respects better than what I expected.

Utilizing PHP I can essentially have a page which gets all the CURRENT comments from a databse.

Then the user can add a comment and this NEW comment can then be displayed in the 'NewComment' div underneath, without any need to RE query the database for a full list of comments.

Sadly, were it not for your examples, I would never have got your script working.
Would it be possible for you to... when you have chance.. comment the script? I would even pay for you to do that.

I can use it, and it does what I need bur i dont really understand why. It would be awesome to appreciate the workings of the script, and then eventually... one day... maybe help make it better :)

Cheers, awesome work.

jscheuer1
12-20-2009, 09:31 PM
I'm still working on it. I suppose I will get around to commenting it more fully. But for now I just want to add a bit more functionality and document the use of that added functionality. One thing I forgot to mention though is that even in its current form as published here, it could handle radio buttons and checkboxes just fine, except for the fact that I'm unclear what the server is normally sent onsubmit of a form that tells it that a particular radio or checkbox is checked. If you have any insight on that, it would be appreciated. I know that like with a set of radio buttons, one can give them all the same name appended with [] and the server makes an array out of them, but how does it know which one is checked? I'm imagining the GET data would look something like:


?gender[]=male:checked&gender[]=female

I guess I can play around with it. From what I can tell POST and GET data are basically the same, only GET data has the ? at its start and is passed with the address.

jscheuer1
12-21-2009, 05:43 AM
This business with the [] and radio buttons appears to be a red herring, or a holdover form some previous syntax no longer required. When a form submits, if there is a radio group, only the one (if any) that is checkd is passed, regardless of whether or not the [] is appended to the name used by the group. With a checkbox, its name/value pair is only sent if it is checked. I've updated the script to act like that.

Anyways, I'm almost done (never really done). I just have to add callbacks for various results/readyStates other than success, and perhaps a hook for optional use of json in interpreting the responseText. I've added some new features. Most notable is the loadXmlHttp.attachForm() function. It allows you to attach loadXmlHttp to a form with a single call in or linked to the head.

Another thing worthy of mention, is that if using the default callback, you may now choose to have your responseText appended (optionally with HTML or a string added in front of it, use an empty string '' if nothing should be added) to the target element, rather than replace it's content.

All changes are documented at the beginning of the script. There are also expanded descriptions of some of the features already included:


/* loadXmlHttp Script 2009 John Davenport Scheuer
as first seen in http://www.dynamicdrive.com/forums/
username: jscheuer1 - This Notice Must Remain for Legal Use
*/

/* reqObject Properties - //all optional, comma delimit
url: page_to_fetch //defaults to this page
method: POST_or_GET //defaults to GET
el: element_to_populate //defaults to none, overrides id
id: id_of_element_to_populate //defaults to none, requires no el
append: quoted_HTML_or_String //if populating via the default callback, this will be appended along with the responseText
form: form_or_name_of_or_number_of_form_submitting_data //defaults to none, requires method: 'POST' or no query
data: encoded_data_to_send_with_POST_request //defaults to none, requires POST and no form
query: encoded_data_to_send_with_GET_request //defaults to none, requires GET, overrides form
callback: function_to_run_on_readyStateChange //defaults to populate on success, in default mode requires el or id
bust: true_OR_false //defaults to false, add cache busting time stamp to the request?
*/

/* Usage:
new loadXmlHttp({reqObject Properties})
Example:
new loadXmlHttp({url: 'somePage.php', method: 'POST', form: document.forms.myform})
*/

/* Example callback:
function(){
if(this.success(false)){
alert(this.xmlHttp.responseText);
}
}

All native and set properties of the loadXmlHttp instance are availbale to this function as:

this.propertyName

Explanation of this.success():
If the request is successful, and there is an element or an id of an element to populate
set in the original call for this instance of loadXmlHttp and that element exists, this.success()
will return true. If there is no element to populate with the responseText or that element
doesn't exist, and you use:

this.success(false)

It will still return true, as long as the request was successful.
*/

/* How to use the loadXmlHttp.attachForm() function:
This function will allow you to set a form to use loadXmlHttp with only one consolidated call.
Example:

loadXmlHttp.attachForm({form: 'myform', bust: true, id: 'result', method: 'post', callback: function(){
if (this.success()){
/>([^<]*)<\/sp/i.test(this.xmlHttp.responseText);
this.el.innerHTML = RegExp.$1;
}
}
});

Note: When using this method, if a populate element is desired, the el property must be skipped
in favor of the id property, and the form must be represented either by its number in the
document forms collection or by its quoted name.
*/

function loadXmlHttp(req){
if(loadXmlHttp.xmlHttp){
var f = this, r;
for(r in req){
if((req.hasOwnProperty && req.hasOwnProperty(r)) || !req.hasOwnProperty){
f[r] = req[r];
}
}
f.xmlHttp = loadXmlHttp.xmlHttp();
f.el = f.el || (f.id? document.getElementById(f.id) : null);
f.form = typeof f.form === 'object'? f.form : document.forms[f.form];
f.method = f.method? f.method.toUpperCase() : 'GET';
f.url = f.url || location.href;
f.bustCache();
f.url += f.method === 'GET' && f.query? f.qa + f.query : '';
if(f.method === 'GET' && !f.query && f.form){
f.encodeData();
f.url += f.qa + f.data;
}
f.xmlHttp.open(f.method, f.url, true);
if(f.callback !== null){
f.xmlHttp.onreadystatechange = typeof f.callback === 'function'?
function(){f.callback.apply(f);} : function(){f.stateChanged();};
}
if(f.method === 'POST'){
if(f.form){
f.encodeData();
}
f.xmlHttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
f.xmlHttp.setRequestHeader('Content-length', f.data? f.data.length : 0);
f.xmlHttp.setRequestHeader('Connection', 'close');
}
f.xmlHttp.send(f.method === 'POST' && f.data? f.data : null);
}
else alert('Your browser does not support AJAX!'); // substitute your desired request object unsupported code here
}

//setup - while parsing:
loadXmlHttp.xmlHttp = null; loadXmlHttp.re = /^http/.test(location.href);
loadXmlHttp.qa = /\?/; loadXmlHttp.chkbl = /^(radio)|(checkbox)$/i;
/*@cc_on @*/ // used here and below, limits try/catch to those IE browsers that both benefit from and support it
/*@if(@_jscript_version >= 5) // prevents errors in old browsers that barf on try/catch & problems in IE if Active X disabled
try {loadXmlHttp.ie = window.ActiveXObject}catch(e){};
@end @*/
if (window.XMLHttpRequest && (!loadXmlHttp.ie || loadXmlHttp.re))
loadXmlHttp.xmlHttp = function(){return new XMLHttpRequest();}; // Firefox, Opera 8.0+, Safari, others, IE 7+ when live - this is the standard method
else if (/(object)|(function)/.test(typeof createRequest))
loadXmlHttp.xmlHttp = createRequest; // ICEBrowser, perhaps others
else {
loadXmlHttp.xmlHttp = null;
// Internet Explorer 5 to 6, includes IE 7+ when local //
/*@if(@_jscript_version >= 5)
try{loadXmlHttp.xmlHttp = function(){return new ActiveXObject("Msxml2.XMLHTTP");};}
catch(e){try{loadXmlHttp.xmlHttp = function(){return new ActiveXObject("Microsoft.XMLHTTP");};}catch(e){}}
@end @*/
}

loadXmlHttp.prototype = {
encodeData: function(){
var els = this.form.elements, chkbl = loadXmlHttp.chkbl; this.data = '';
for(var i = 0; i < els.length; ++i){
if(els[i].name && ((chkbl.test(els[i].type) && els[i].checked) || !chkbl.test(els[i].type))){
this.data += (i? '&' : '') + els[i].name + '=' + encodeURIComponent(els[i].value);
}
}
},
bustCache: function(){
this.qa = loadXmlHttp.qa.test(this.url)? '&' : '?';
if(this.bust){
this.url += this.qa + 'bustacache=' + new Date().getTime();
this.qa = '&';
}
},
success: function(require_el){
return (this.el || require_el === false) && this.xmlHttp.readyState == 4 && (this.xmlHttp.status == 200 || !loadXmlHttp.re);
},
attach: (function(){
return window.addEventListener? function(el, evt, func){el.addEventListener(evt, func, false);}:
window.attachEvent? function(el, evt, func){el.attachEvent('on' + evt, func);} : function(){return;};
})()
};
loadXmlHttp.prototype.stateChanged = function(){
if (this.success()){
if(typeof this.append === 'string'){
this.el.innerHTML += this.append + this.xmlHttp.responseText;
}else{
this.el.innerHTML = this.xmlHttp.responseText;
}
}
};
loadXmlHttp.attachForm = function(req){
var attach = loadXmlHttp.prototype.attach;
attach(window, 'load', function(){
req.form = document.forms[req.form];
attach(req.form, 'submit', function(e){
new loadXmlHttp(req);
if(e && e.preventDefault){
e.preventDefault();
}
return false;
})
});
};
//end setup

Here's a demo taking advantage of some of the new features:


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
#result {
width: 28em;
border: 1px solid #ffc0cb;
padding: 0.25em;
min-height: 1em;
}
form {
margin-bottom: 0.75em;
}
#post {
display: none;
}
</style>
<script type="text/javascript" src="http_get_post.js"></script>
<script type="text/javascript">
loadXmlHttp.attachForm({form: 'myform', bust: true, id: 'result', method: 'post', append: '<br>', callback: function(){
if (this.success()){
/>([^<]*)<\/sp/i.test(this.xmlHttp.responseText);
this.el.innerHTML += (this.el.innerHTML !== ''? this.append : '') + RegExp.$1;
}
}
});
</script>
</head>
<body>
<div id="post"><span><?php echo $_POST['list'] . $_POST['name'] . ', ' . $_POST['gender'] . ', country: ' . $_POST['country']; ?></span></div>
<form action="#" name="myform">
<div>
<label>Name: <input type="text" name="name" value=""></label><br>
Gender:<br>
<label>Not Disclosed:<input type="radio" checked name="gender" value="gender not disclosed"></label><br>
<label>Female: <input type="radio" name="gender" value="female"></label><br>
<label>Male: <input type="radio" name="gender" value="male"></label><br>
<label>Add Me to Your List: <input type="checkbox" name="list" value="Added To List: "></label><br>
Country: <select name="country">
<option value="Kenya">Kenya</option>
<option value="UK">UK</option>
<option value="USA">USA</option>
<option value="n/a" selected>None of the Above</option>
</select><br>
<input type="submit" value="Go!">
</div>
</form>
<div id="result"></div>
</body>
</html>