Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » XmlHttpRequest and friends

Credits go to FMC, Jonny Cook, and miran for helping out!
This thread is locked; no one can reply to it. rss feed Print
 1   2 
XmlHttpRequest and friends
Marco Radaelli
Member #3,028
December 2002
avatar

If I understand it, XmlHttpRequest allows manipulating data after the page has loaded. E.g. I can show a button and when the user clicks it a javascript function can use the object to retrieve the MOTD and place it somewhere in the page.

How would I go having it working in different browsers? Does each one have it's own way to create the object and interact with it?

I found that for IE it's
var req = new ActiveXObject("Microsoft.XMLHTTP");

while for FF
var req = new XMLHttpRequest();

(according to this page).

What's for other browsers?

miran
Member #2,407
June 2002

Quote:

What's for other browsers?

Should be either the same as for FF or not supported at all.

--
sig used to be here

Marco Radaelli
Member #3,028
December 2002
avatar

Good.

What's best to check browser type? Client-side with JS or server-side looking at UserAgent (if someone is faking it I wouldn't care)

miran
Member #2,407
June 2002

I use code that looks like this in JS to asynchronously get data from the server with xmlhttprequest:

1var req = false;
2var waiting_for_data = false;
3var already_created_xmlhttprequest_object = false;
4 
5function request_data(url) {
6 if (!waiting_for_data) {
7 if (!already_created_xmlhttprequest_object) {
8 // for standard browsers
9 if (window.XMLHttpRequest) {
10 try {
11 req = new XMLHttpRequest();
12 already_created_xmlhttprequest_object = true;
13 }
14 catch (e) {
15 req = false;
16 }
17 }
18 // for IE
19 else if (window.ActiveXObject) {
20 try {
21 req = new ActiveXObject("Msxml2.XMLHTTP");
22 already_created_xmlhttprequest_object = true;
23 }
24 catch (e) {
25 try {
26 req = new ActiveXObject("Microsoft.XMLHTTP");
27 already_created_xmlhttprequest_object = true;
28 }
29 catch (e) {
30 req = false;
31 }
32 }
33 }
34 }
35
36 if (req) {
37 waiting_for_data = true;
38 
39 req.open("GET", url, true);
40 req.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
41 
42 req.onreadystatechange = receive_data;
43 req.send("");
44 }
45 }
46}
47 
48function receive_data() {
49 if (req && waiting_for_data) {
50 if (req.readyState == 4) {
51 if (req.status == 200) {
52 do_something_with(req.responseText);
53 waiting_for_data = false;
54 }
55 }
56 }
57}

--
sig used to be here

Marco Radaelli
Member #3,028
December 2002
avatar

Wow, thanks :D
I'll study it when I get home.

miran
Member #2,407
June 2002

Note that some of that code is a little specific for my project. I'm talking about that check that makes sure no other request can be made until data was received from the previous request. In your project you will probably want to change this...

--
sig used to be here

FMC
Member #4,431
March 2004
avatar

[FMC Studios] - [Caries Field] - [Ctris] - [Pman] - [Chess for allegroites]
Written laws are like spiders' webs, and will, like them, only entangle and hold the poor and weak, while the rich and powerful will easily break through them. -Anacharsis
Twenty years from now you will be more disappointed by the things that you didn't do than by the ones you did do. So throw off the bowlines. Sail away from the safe harbor. Catch the trade winds in your sails. Explore. Dream. Discover. -Mark Twain

Jonny Cook
Member #4,055
November 2003

See, now those are good tutorials. They just give you the facts, plain and simple. I really hate it when they pad the tutorial with a bunch useless crap to "ease" the learning process. It just makes it so you have to sift through it all to really get anything done... the whole world should think exactly like I do!>:(

[edit]
Although I must agree with the second tutorial that the first tutorial may not have had the best code examples.

The face of a child can say it all, especially the mouth part of the face.

Marco Radaelli
Member #3,028
December 2002
avatar

miran said:

Note that some of that code is a little specific for my project. I'm talking about that check that makes sure no other request can be made until data was received from the previous request. In your project you will probably want to change this...

I was in a hurry when I posted that reply, I didn't closely look at the code. Now I do, I'll keep the checking part.

FMC said:

This might help [rajshekhar.net]

[edit]and this http://aleembawany.com/weblog/webdev/000051_ajax_instant_tutorial.html

Thanks, but both do not seem to use XML in their example.

I finally found how to get the XML tree :)

Marcello
Member #1,860
January 2002
avatar

Marco Radaelli
Member #3,028
December 2002
avatar

50 websites... o_O

It looks like my troubles aren't finished :D

I managed to access that xml document I have and read its XML-tree.

Now the problem is that I'll add its childs to a xhtml element. While I can removeChild(), it doesn't let me appendChild(), I can't understand why.

xml file
<?xml version="1.0" ?>
<options>
  <option value="0">Zero</option>
  <option value="1">Uno</option>
  <option value="2">Due</option>
  <option value="3">Tre</option>
  <option value="4">Quattro</option>
</options>

xhtml page
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <script language="Javascript">
    
      function go()
        {
         /* Recupero file XML */
         
         xmlobj = new ActiveXObject("Msxml2.XMLHTTP.3.0");

         xmlobj.open("GET", "http://localhost/xml-test/x.xml", false);
         xmlobj.send();
         
         /* Lettura albero XML */
         
         xmldoc = xmlobj.responseXML;

         sel = document.getElementById("comuni");

         sel.removeChild(sel.lastChild);
         
         /* lastChild because it seems that firstChild is <xml>... */ 
         opt = xmldoc.lastChild.childNodes;
         //options = xmldoc.getElementsByTagName("option");

         for(i = 0; i < opt.length; i++)
           {
            sel.appendChild(opt.item(i));
           }
        }
        
    </script>
  </head>

  <body>
    <input type="button" value="o_O" onclick="go()" />
    <form>

    <select id="comuni" size="1" disabled="disabled">
      <option selected>Scegliere una provincia</option>
    </select>
    
    </form>
  </body>
</html>

In short, in the webpage there's a <select> with a single <option>, which says "Select a province", when the button is clicked I want that <option> to be deleted (which works) and then the five <option>s in the xml to be added there (which doesn't work, keeps saying 'interface not supported' on the appendChild() line)

I still have to integrate the browser check, will do it when it all works :)

Matthew Leverton
Supreme Loser
January 1999
avatar

You cannot add a node from one document into another. You need to import it (importNode ?), but of course IE6 doesn't support that.

The whole "AJAX" name is a joke. No one seriously uses XML with it. It's really "AJAJ" or "AJAH". Look for what's known as "JSON". It's basically a way to convert XML like data into a string that represents a JavaScript object.

AJAJ example:

server generates:
{options:[{value:0,text:"zero"},{value:1,text:"one"}]}

client evaluates it:
eval("var response = " + json);
alert(response.options[0].value);

Or alternatively use AJAH and just pass it as HTML, and use foo.innerHTML = HTML;.

miran
Member #2,407
June 2002

Yes, I was going to say the same thing as Matthew, but was too busy watching football. :P You will rarely want to handle XML in JS as JSON is so much easier and more convenient and has more support. Even if your source data is XML, it is much easier to convert it to JSON on the server and then just use eval() in JS than it is to manipulate XML in JS.

--
sig used to be here

Matthew Leverton
Supreme Loser
January 1999
avatar

I meant to comment on this:

Quote:

What's best to check browser type? Client-side with JS or server-side looking at UserAgent

Don't ever check the browser type to determine functionality. Always check browser features, starting with the standard ones first:

if (foo.addEventListner) {
  // W3C Model
}
else if (foo.attachEvent) {
  // IE Model
}

The only times you want to check the user agent string with JS is if a certain browser has a glitch that you need to work around. For example, I check for the literal string "Safari" on the dynamic combobox widget because Safari does not support CSS system colors, so I have to provide default colors.

That is the only future compatible method. If I were to check for features, I might get the detection wrong if another browser matches it or Safari changes functionality. (Obviously if they ever fix this problem, then I would need to add a version check as well.)

Marco Radaelli
Member #3,028
December 2002
avatar

Ok, I found how to manipulate the <select> childs:

Roughly

         opt = document.createElement("option");
         sel.options.add(opt);
         opt.innerHTML = "Prova";
         opt.value = 1;

I thought I'd go the easy way:

  • generate the js script server-side

  • load it with the nice object

  • eval() the code

Of course that doesn't work :P
I guess it's because the XmlHttpRequest object won't load non-xml data.

So? JSON requires more than five minutes to get it to work, but I still don't get how will I retrieve the js source code (either from a file or from a server-side script) :'(

miran
Member #2,407
June 2002

Quote:

I guess it's because the XmlHttpRequest object won't load non-xml data.

??? Of course it does. The server serves plain text. This text can be interpreted on client side as you wish, either as XML or JSON or anything else. On the server you just create a string containing JSON code, write it to the response object and that's it. I only know ASP, but I suppose it's similar in othe server side scripting languages. You just do something like

dim json as string
json = "{options:[{value:0,text:"zero"},{value:1,text:"one"}]}"
Response.Write(json)

and on the client in JS you do something like

var json = req.responseText;
eval("var response = " + json);

--
sig used to be here

Marco Radaelli
Member #3,028
December 2002
avatar

I said:

I guess it's because the XmlHttpRequest object won't load non-xml data.

Ok, call me dumb.

I replaced the text in the file and IE started to say "Permission denied" on xmlobj.open()

I thought it was due because the file didn't contain valid xml code, but now I have closed and reopened IE completely and it works perfectly ::)

Well, for now I'll just directly generate the js script, avoiding that JSON stuff (I'm a bit in a hurry for this project). I'll keep an eye on it tough :)

Too bad I was so optimistic to think that browser capabilities were the only trouble.

Consider a whole box of cookies for you all ^_^

Matthew Leverton
Supreme Loser
January 1999
avatar

There are also many JSON classes available for scripting languages. The one I use for PHP works like:

$response = array(
  'foo' => 'bar'
);

$json = new JSON;
echo $json->encode($response);

It may seem like overkill at first, but it becomes quite nice when you have a very dynamic response to build. Plus, it automatically handles converting UTF-8 encoded strings to proper JS escapes (eg, "\u00A9").

Marco Radaelli
Member #3,028
December 2002
avatar

Mmh... looks like the request ends before the whole code is retrieved, if the latter is too long.

Any way to wait for it? I'm thinking at onreadystate, but a busy-wait on it won't work without some sort of sleep()

Marcello
Member #1,860
January 2002
avatar

You're doing something wrong, then. The examples above will not do that.

Perhaps include some code?

Marcello

Matthew Leverton
Supreme Loser
January 1999
avatar

As long as you are waiting for readyState == 4, everything will be there.

Marco Radaelli
Member #3,028
December 2002
avatar

Quote:

You're doing something wrong, then. The examples above will not do that.

Perhaps include some code?

Here's it (I still didn't converted it to JSON)

Client-side, when the selected item of a <select> changes, this deletes all the current <option>s in it and loads a js from listacomuni.php

1function aggiornacomuni(prov)
2{
3 /* Recupero codice javascript */
4 
5 if (window.XMLHttpRequest)
6 {
7 req = new XMLHttpRequest();
8 req.open("GET",
9 '<?php echo "http://".$_SERVER['SERVER_NAME'].
10 dirname($_SERVER['PHP_SELF']).
11 "/listacomuni.php?p=" ?>' + prov,
12 false);
13 }
14 else if(window.ActiveXObject)
15 {
16 req = new ActiveXObject("Msxml2.XMLHTTP");
17 
18 req.open("GET",
19 '<?php echo "http://".$_SERVER['SERVER_NAME'].
20 dirname($_SERVER['PHP_SELF']).
21 "/listacomuni.php?p=" ?>' + prov,
22 false);
23 req.send();
24 }
25
26 js = req.responseTEXT;
27
28 select = document.getElementById('comune');
29
30 for(i = 0; i < select.options.length; i++)
31 select.remove(i);
32
33 eval(js);
34}

Server-side, generates js that will add the new <option>s to the <select>

1 db_query("SELECT codice, nome FROM comuni WHERE codprovincia={$provincia} ORDER BY nome");
2 
3 header("Content-type: text/javascript");
4
5 echo "options = new Array(";
6 
7 if($res = db_getresult_byrow())
8 echo "\n ".
9 "new Array({$res['codice']}, \"{$res['nome']}\")";
10
11 while($res = db_getresult_byrow())
12 echo ",\n ".
13 "new Array({$res['codice']}, \"{$res['nome']}\")";
14
15 echo "\n);\n\n";
16 
17 echo "for(i = 0; i < options.length; i++)\n";
18 echo " {\n";
19 echo " option = document.createElement(\"option\");\n";
20 echo " select.options.add(option);\n";
21 
22 echo " option.value = options<i>[1];\n";
23 echo " option.innerHTML = options<i>[1];\n";
24 echo "}\n";

While the code isn't 100% fixed (I still have issues alternatively in IE and FF :)), when the query returns few rows it generates the whole script, but if the rows are many the script gets broken (I saw this by replacing eval() with alert() in the client-side script

An example is attached (couldn't resist to try the new upload thing ;D)

Quote:

As long as you are waiting for readyState == 4, everything will be there.

while(req.readystate != 4);
just like that?

Matthew Leverton
Supreme Loser
January 1999
avatar

Quote:

while(req.readystate != 4);

No, the function will be called multiple times:

function onLoad()
{
  if (req.readystate != 4) return;
}

I would do this:

req.open("GET", url, true /* not false */);
req.onreadystatechange = function() {
  if (req.readystate != 4) return;
  // process
};

And use a callback function. It's the only good way to do it. (See Miran's example.)

BAF
Member #2,981
December 2002
avatar

Quote:

req.open("GET",
'<?php echo "http://".$_SERVER['SERVER_NAME'].
dirname($_SERVER['PHP_SELF']).
"/listacomuni.php?p=" ?>' + prov,

All your open()'s have stray php. Is that intended?

Marco Radaelli
Member #3,028
December 2002
avatar

BAF said:

All your open()'s have stray php. Is that intended?

Yes; due to the fact that the xml~ stuff needs an absolute path, that php code ensures everything works if I move the whole directory :)

Great... it doesn't work. I mean, I think I did everything you suggested

client-side code

1<script language="Javascript">
2 
3var req = false;
4 
5function aggiornacomuni(prov)
6{
7 /* Recupero codice javascript */
8 
9 if (window.XMLHttpRequest)
10 {
11 req = new XMLHttpRequest();
12 req.open("GET",
13 '<?php echo "http://".$_SERVER['SERVER_NAME'].
14 dirname($_SERVER['PHP_SELF']).
15 "/listacomuni.php?p=" ?>' + prov,
16 true);
17 }
18 else if(window.ActiveXObject)
19 {
20 req = new ActiveXObject("Msxml2.XMLHTTP");
21 req.open("GET",
22 '<?php echo "http://".$_SERVER['SERVER_NAME'].
23 dirname($_SERVER['PHP_SELF']).
24 "/listacomuni.php?p=" ?>' + prov,
25 true);
26 }
27
28 req.onreadystatechange = runjs;
29 req.send("");
30
31 select = document.getElementById('comune');
32
33 for(i = 0; i < select.options.length; i++)
34 select.remove(i);
35}
36 
37function runjs()
38{
39 if(req.readyState != 4)
40 return;
41
42 alert(req.responseTEXT);
43}
44</script>

server-side code

1<?php
2 
3 $provincia = $_GET['p'];
4 
5 include_once("db_fx.php");
6 
7 db_query("SELECT codice, nome FROM comuni WHERE codprovincia={$provincia} ORDER BY nome");
8 
9 header("Content-type: text/javascript");
10 
11 echo "options = new Array(";
12 
13 if($res = db_getresult_byrow())
14 echo "\n ".
15 "new Array({$res['codice']}, \"{$res['nome']}\")";
16
17 while($res = db_getresult_byrow())
18 echo ",\n ".
19 "new Array({$res['codice']}, \"{$res['nome']}\")";
20
21 echo "\n);\n\n";
22 
23 echo "for(i = 0; i < options.length; i++)\n";
24 echo " {\n";
25 echo " option = document.createElement(\"option\");\n";
26 echo " select.options.add(option);\n";
27 
28 echo " option.value = options<i>[0];\n";
29 echo " option.innerHTML = options<i>[1];\n";
30 echo "}\n";
31 
32?>

But it keeps failing (output is truncated) if the query returns too many rows. I have no idea about what to do, I can't understand why readyState becomes 4 before the script ends its output.

I also tried to put every js piece in a variable and echo that only at the end: same result.

I don't think it's a timeout issue, because it all ends in 1-2 seconds. I don't even understand why there are those boxes in the generated javascript (long.png in my post above), alert() doesn't have troubles with accented letters.

:(

[edit]

I changed
eval(req.responseTEXT);
to
eval(req.responseText);

and now it works in FF (it does wait for the whole data, while IE still doesn't ???). But I have another issue to add ;D
select.remove(i);
is not working ::)

 1   2 


Go to: