Jump to content
Larry Ullman's Book Forums

Question About Ajax


Recommended Posts

Hi there,

 

Just trying out writing my own simple Ajax application. Now, in the book the first example of Ajax is defined with two files, ajax.js and test.js. So they are:

 

From ajax.js:

 

function getXMLHttpRequestObject() {

'use strict';

 

var ajax;

if(window.XMLHttpRequest){ //Not IE

ajax = new XMLHttpRequest();

} else if (window.ActiveXObject) { //Older IE

ajax = new ActiveXObject('MSXML2.XMLHTTP.3.0');

}

return ajax;

} // end of getXMLHttpRequestObject()

 

From test.js:

 

window.onload = function() {

'use strict';

 

var ajax = getXMLHttpRequestObject();

 

ajax.onreadystatechange = function(){

if(ajax.readyState == 4){

if( (ajax.status >= 200 && ajax.status < 300) || (ajax.status == 304) ) {

document.getElementById('output').innerHTML = ajax.responseText;

} else {

document.getElementById('output').innerHTML = ajax.statusText;

}

}

}//end of onreadystatechange anonymous function

 

document.getElementById('btn').onclick = function() {

'use strict';

 

ajax.open('GET', 'resources/test.txt', true);

ajax.send(null);

}; //end of onclick anonymous function

 

}; //end of onload anonymous function

 

This code obviously works. Now, because of variable scrope, ajax is only defined once using the getXMLHttpRequestObject()... I get that, because everything is within this window.onload function and all variables are accessible. The onreadystatechange function is defined anonymously and thus has access to the ajax variable. Now, I wanted to experiment creating the event listener using the DOM LEVEL 2 approach (addEventListener method), but I find that I cannot access the variable ajax because of variable scope.

 

I DID READ on page 458 the workaround for this, but I am left confused. Larry says to "get the object that referenced the event" and ends that "...the target of the event will be the same XMLHttpRequest object." What does that mean? I don't understand how defining the event object gives you access to that variable. That's my first question.

 

My second question is from my code. I tried using the event object to define it, but the error I am getting is 'ajax.open is not a function' from Firebug. It would seem then that ajax is not defined, or not defined properly... so I am confused as to why my code doesn't work. I used the DOM LEVEL 1 approach for the event handler because I just don't know how to use the addEventListener way.

 

Here is my code:

 

function addEvent(obj, type, fn) {

'use strict';

 

if(obj && obj.addEventListener) {

obj.addEventListener(type, fn, false);

} else if (obj && obj.attachEvent) {

obj.attachEvent('on' + type, fn);

}

}

 

function $(id) {

'use strict';

 

if (id && typeof id === 'string') {

return document.getElementById(id);

}

}

 

//-------------------------AJAX begins here-----------------------------------------

 

//Step 1: Function to create ajax object

function getXMLHttpRequestObject() {

'use strict';

 

var ajax;

 

if(window.XMLHttpRequest) {

ajax = window.XMLHttpRequest;

} else if (window.ActiveXObject) {

ajax = window.ActiveXObject('MSXML2.XMLHTTP.3.0');

}

return ajax;

}

 

//Step 2: Define result handler. This is the function that will be called during the Ajax transaction.

function ajaxHandlingFunction(e) {

'use strict';

if (typeof e == 'undefined') e = window.event;

var ajax = e.target || e.srcElement;

if(ajax.readyState == 4) {

if( (ajax.status >= 200 && ajax.status < 300) || (ajax.status == 304) ) {

document.getElementById('output').innerHTML = ajax.responseText;

} else {

document.getElementById('output').innerHTML = 'Error ' + ajax.statusText;

}

}

}//end of ajaxHandlingFunction

 

 

window.onload = function() {

'use strict';

var button = $('btn');

var ajax = getXMLHttpRequestObject();

ajax.onreadystatechange = ajaxHandlingFunction;

button.onclick = function(){

ajax.open('GET', 'resources/test.txt', true);

ajax.send(null);

};

};

 

Any ideas on why I get 'ajax.open isn't a function', and how would you do this using the addEventListnener approach? Sorry for the long post, but I guess it reflects my confusion.

Link to comment
Share on other sites

I'll answer your first question now and answer the second one later.

Short answer, use this in the function referenced by the onreadystatechange event handler to properly reference the Ajax object.

The following is a very simple example (which won't work in IE):

 

test.html

<html>

 <body>

   <script>

     window.onload = function () {

       var ajax = new XMLHttpRequest();

       ajax.open('get', 'test.php');

       ajax.addEventListener('readystatechange', handleResponse, false);

       ajax.send(null);

     };

     function handleResponse() {

       if ((this.readyState === 4) && (this.status === 200)) {

         alert(this.responseText);

       }

     }

   </script>

 </body>

</html>

 

test.php

 

<?php

 echo 'This text was sent via Ajax.';

 

Hopefully that clears up that confusion.

I'll address your second question tomorrow when I have more time.

(Hopefully someone else will answer it in the meantime, though.)

 

That help?

Link to comment
Share on other sites

I played around with FireBug and some similar code, and everything was working fine for me.

A couple of ideas:

 

1) Alert ajax to the screen right after it's declared for the onload function. Make sure you're getting an Ajax object.

2) If 1) is okay, alert ajax.open to the screen and see what it says.

3) If 2) is okay, make sure that 'resources/test.txt' is a valid resource.

 

Lemme know what you find, and we'll go from there.

  • Upvote 1
Link to comment
Share on other sites

I'll answer your first question now and answer the second one later.

Short answer, use this in the function referenced by the onreadystatechange event handler to properly reference the Ajax object.

The following is a very simple example (which won't work in IE):

 

test.html

<html>

<body>

<script>

window.onload = function () {

var ajax = new XMLHttpRequest();

ajax.open('get', 'test.php');

ajax.addEventListener('readystatechange', handleResponse, false);

ajax.send(null);

};

function handleResponse() {

if ((this.readyState === 4) && (this.status === 200)) {

alert(this.responseText);

}

}

</script>

</body>

</html>

 

test.php

 

<?php

echo 'This text was sent via Ajax.';

 

Hopefully that clears up that confusion.

I'll address your second question tomorrow when I have more time.

(Hopefully someone else will answer it in the meantime, though.)

 

That help?

 

Is this line correct Hartley?

 

ajax.addEventListener('readystatechange', handleResponse, false);

 

of my knowledge so far with the ajax object i know i can do it like this

 

ajax.onreadystatechange = handleStateChange; (Ref PG.429)

 

Also in the js book library file

 

// Script 11.1 - ajax.js

// This script defines a function that creates an XMLHttpRequest object.

 

// This function returns an XMLHttpRequest object.

 

// Script 11.1 - ajax.js
// This script defines a function that creates an XMLHttpRequest object.
// This function returns an XMLHttpRequest object.
function getXMLHttpRequestObject() {

var ajax = null;
// Most browsers:
if (window.XMLHttpRequest) {
 ajax = new XMLHttpRequest();
} else if (window.ActiveXObject) { // Older IE.
 ajax = new ActiveXObject('MSXML2.XMLHTTP.3.0');
}

// Return the object:
return ajax;
} // End of getXMLHttpRequestObject() function.

 

So the ajax = new XMLHttpRequest() would be okay for newer versions of IE.

Link to comment
Share on other sites

To answer your questions, Edward:

1) synergy specifically asked how to do things using the DOM level 2 approach, that's why I didn't use ajax.onreadystatechange. There are 4 event models in JS, and the traditional model is only one way of doing things.

2) I specifically said the following before my code snippet:

 

The following is a very simple example (which won't work in IE):

 

Does that answer your questions?

Link to comment
Share on other sites

Hi again,

 

Thanks Hartley for the replies! Appreciated. So let's see:

 

1. I see what you did with the DOM level 2 approach. Very cool. So 'this' refers to the window object?...

2. About firebug: Here is an image of the error I get, with the code I gave above ('from ajax2.js'). Why am I getting this error?s2yv0x.jpg

 

3. From the book's examples, I am still confused about using the event object. In other words, I'm confused about variable scoping. Larry mentions to use the event object of the ready state event handler on page 458, and this confuses me, because being a newb I don't see how using this event object is the workaround to not being able to access the 'ajax' variable. In other words, when you create a SEPARATE ready state event handler (not an anonymous function, ie, not 'ajax.onreadystatechange = function(){ etc...), you lose access to the ajax variable you previously made. So in this function:

 

window.onload = function() {

'use strict';

 

var ajax = getXMLHttpRequestObject();

 

ajax.onreadystatechange = function(){

if(ajax.readyState == 4){

if( (ajax.status >= 200 && ajax.status < 300) || (ajax.status == 304) ) {

document.getElementById('output').innerHTML = ajax.responseText;

} else {

document.getElementById('output').innerHTML = ajax.statusText;

}

}

}//end of onreadystatechange anonymous function

 

document.getElementById('btn').onclick = function() {

'use strict';

 

ajax.open('GET', 'resources/test.txt', true);

ajax.send(null);

}; //end of onclick anonymous function

 

}; //end of onload anonymous function

 

You have access to the ajax variable and thus have access to it when defining your ready state handler. if I try to do it seperately, as in my example from 'ajax2.js' in my first post, I lose access to the ajax variable. The confusing this is, in the book, Larry defines the variable ajax twice... once as:

 

var ajax = e.target || e.srcElement; // page 458

 

AND, in the window.onload function on page 459 (all part of the same script):

 

var ajax = getXMLHttpRequest();

 

SO... why is this variable defined twice? It just confuses me. Sorry about my questions being rather all over the place.

Link to comment
Share on other sites

I can only answer the last one. This is because of variable scope. "var ajax", when defined inside a function, is only available there. If you define it outside function scope, or nest functions, the ajax variable will be available. This is a pretty important thing about Javascript, so I would recommend understanding it. (And it's not very hard eighter, pretty similar to how variables in PHP works.)

  • Upvote 1
Link to comment
Share on other sites

synergy, I've looked at your code that's causing the error several times, and it looks fine. I can only assume two things:

1) You're not running your script on a server or localhost (although this shouldn't matter since you're only loading a text file).

2) The path to the text file in your ajax.open method call is no good.

 

Beyond that, I'm not sure what to say (unless there's some minor syntax error we're missing).

I tried Googling your issue, and in every case, the solution was always that the original poster made a syntax error that, after finding it, corrected the error reported by FireBug.

 

To answer your other questions:

 

1) "this" in the function refers to the Ajax object. Whenever a method is called from an object, this in that method always refers to the object the method was called from. As an important note, in regular functions (i.e., not methods), "this" does in fact refer to the window object.

You can instead use the Event object like Larry does in his book to refer to the Ajax object, but I generally prefer "this" and it's quicker and easier. (Actually, I generally declare a global or pseudo-global Ajax object, so I don't even use "this" that often, but all the same, I prefer this over using the Event object.)

 

2) I have answered this question as best I can. See below for one final suggestion on trying to resolve this.

 

3) The whole concept behind scope is that a variable declared within a function cannot be accessed outside of that function unless it is passed as an argument or returned from that function. In the case of Larry's Ajax code, the Ajax object is declared in a function and not passed to any other functions, so the Ajax variable cannot be accessed from the onreadystatechange function (because the Ajax variable is not passed to the function). There are three ways to get around this problem:

 

i) Actually pass the Ajax variable to the onreadystatechange function, which Larry does not do.

 

ii) If you don't do i), then you can access the Ajax object (not the variable) via "this", which is what I recommended.

 

iii) If you don't want to do i) or ii), then you can access the Ajax object via the Event object, which is implicitly passed to all functions called by an event handler (e.g. onreadystatechange). Due to browser variances though, how you access the Ajax object from the Event object differs, which explains all the code to get the Ajax object again in the onreadystatechange function.

The fact that Larry choose to call the Ajax object variable "ajax" in both function is purely a matter of convenience. The variables can be named whatever you want, and the names don't have to be the same (because the variables are two different variables in two different scopes. It just so happens that because both refer to the Ajax object, Larry thought it best (and prudently so) to call both variables "ajax".

 

Does that help?

 

Looking at your code again, I think I just spotted the error. You're not actually creating a proper Ajax object in the getXMLHttpRequestObject function. Change the two following lines as following:

 

 

ajax = window.XMLHttpRequest; => ajax = new window.XMLHttpRequest();
ajax = window.ActiveXObject('MSXML2.XMLHTTP.3.0'); => ajax = new window.ActiveXObject('MSXML2.XMLHTTP.3.0');

 

Does that work?

  • Upvote 2
Link to comment
Share on other sites

Hi again,

 

Thanks Hartley! Wonderful. Thanks for finding that error! I've actually taken what you gave me and applied to the example in the book (to make it work with a click). I also put some stuff into a 'sendAjax' function because it just makes more sense to me that way (you click the button and that event is what generates everything):

 

//ajax2 test
function addEvent(obj, type, fn) {
'use strict';

if(obj && obj.addEventListener) {
 obj.addEventListener(type, fn, false);
} else if (obj && obj.attachEvent) {
 obj.attachEvent('on' + type, fn);
}
}
function $(id) {
'use strict';

if (id && typeof id === 'string') {
 return document.getElementById(id);
}
}
//-------------------------AJAX begins here-----------------------------------------
//Step 1: Function to create ajax object
function getXMLHttpRequestObject() {
'use strict';

var ajax;

if(window.XMLHttpRequest) {
 ajax = window.XMLHttpRequest();
} else if (window.ActiveXObject) {
 ajax = new window.ActiveXObject('MSXML2.XMLHTTP.3.0');
}
return ajax;
}
//Step 2: Define result handler. This is the function that will be called during the Ajax transaction.
function ajaxHandlingFunction() {
'use strict';
if(this.readyState == 4) {
 if( (this.status >= 200 && this.status < 300) || (this.status == 304) ) {
  document.getElementById('output').innerHTML = this.responseText;
 } else {
  document.getElementById('output').innerHTML = 'Error: ' + this.statusText;
 }
}
}//end of ajaxHandlingFunction
window.onload = function() {
'use strict';
var button = $('btn');
addEvent(button, 'click', sendAjax);
};
function sendAjax(){
var ajax = getXMLHttpRequestObject();
ajax.open('GET', 'resources/test.txt', true);
addEvent(ajax, 'readystatechange', ajaxHandlingFunction);
ajax.send(null);
}

 

One last question, what does Larry mean when he says "the object that is the target of the event will be the same XMLHttpRequest object"?

 

Thomas

Link to comment
Share on other sites

In the function linked to the onreadystatechange event, the e parameter is the Event object. The target property of that Event object (the actual property name differs, depending on the browser) is the exact same Ajax object you originally assigned to the ajax variable. That's what he meant.

In other words, both ajax and e.target refer to the exact same Ajax object.

It's important to remember that objects are passed and handled by reference as well, meaning that the ajax object is not copied, but instead, the same exact (one) Ajax object is referenced by both ajax and e.target.

  • Upvote 1
Link to comment
Share on other sites

 Share

×
×
  • Create New...