Cannot call a user defined JS function after return the AJAX content

I am having a problem of calling the user defined JS function after I make the AJAX call. Basically, I created couple radio buttons on the main html page. When the user clicks on one of the radio button, it will trigger the AJAX call and return another html file in the “div” content that I set in the main html page. The other html file contains a user defined JS function (e.g. “updateContent()”) which use the onclick event handler to call the function. When I’m running the app, and click on the button. I had seen the firebug was complaining the “updateContent() is not defined” error. The function itself works fine and must be defined properly. Please help me about this! Thanks.

Here is the code in the main.html page:

<script type="text/javascript">
  var asyncRequest;
	function getTools(url){
  	 try
         {
		asyncRequest = new XMLHttpRequest();
		asyncRequest.onreadystatechange = stateChange;
		asyncRequest.open('GET', url, true);
		asyncRequest.send(null);
	} 
	catch (exception){
		alert('Your request is failed');
	} 
}

	function stateChange(){
		if (asyncRequest.readyState == 4 && asyncRequest.status == 200){					  
                document.getElementById('toolArea').innerHTML = asyncRequest.responseText;
               } 
} 
</script>..............
<body>
<input type="radio" name="weight" onclick="getTools('secondTool.html')" />
<input type="radio" name="weight" onclick="getTools('thirdTool.html')" />.......
<div class = "toolArea" id = "toolArea">&nbsp;</div></body>

This other html file (e.g. secondTool.html) contains the user defined JS function:


<script type="text/javascript">
 function updateContent(){
   ....
   ....
  }
</script>
<body>
  ....
  ....
  <input type="button" value="Update" onclick="updateContent()"></body>

for starters, your function stateChange() needs to be inside getTools(url) and not outisde of it.

stateChange() is within the scope of getTools() so that is not an issue (not a functional one at least). The issue is that your attempting to add a script dynamically using innerHTML, which won’t work. A script tag will need to be created as a node manually and injected into the DOM apart from innerHTML, which will not execute script tags. That is why updateContent() is undefined, because it isn’t seen. This were methods such as; JQuery.html() come handy, as it actually handles the dirty work of parsing script tags for you. Otherwise, you need to do it manually.

are you sure it’s within scope?

I have highlighted in red the open/close brackets of getTools().

I still think the code for stateChange() need to be inside getTools().

If stateChange() is outside of getTools(), then stateChange() won’t see asyncRequest.

But I’m not saying that is the only problem.

 
<script type="text/javascript">
  var asyncRequest;
function getTools(url) [B][COLOR=red]{[/COLOR][/B]
  try
         {
asyncRequest = new XMLHttpRequest();
asyncRequest.onreadystatechange = stateChange;
asyncRequest.open('GET', url, true);
asyncRequest.send(null);
} 
catch (exception){
alert('Your request is failed');
} 
[B][COLOR=red]}[/COLOR][/B]
 
function stateChange(){
if (asyncRequest.readyState == 4 && asyncRequest.status == 200){   
                document.getElementById('toolArea').innerHTML = asyncRequest.responseText;
               } 
} 
</script>..............
 

in this example, if you put requestHandler() outside of testAjax() then the alert() will not work and you will get a “scope” error (at least in my IE8 I do)

index.htm

 
<script type="text/javascript">
    function startAJAX(){
        var xmlHttp = null;   
        try {
            // Firefox, Opera 8.0+, Safari Browsers
            xmlHttp = new XMLHttpRequest();
        }
        catch (e)
        {    
            try {
                // IE Browsers v6+
                xmlHttp =new ActiveXObject("Msxml2.XMLHTTP");  
            }
            catch (e) {
                try {
                    // IE Browsers v5+
                    xmlHttp =new ActiveXObject("Microsoft.XMLHTTP");
                }
                catch (e) {
                    alert("Your browser does not support AJAX!");
                }   
            }    
        } 
        return xmlHttp;     
    }
    //==========================================
 
function testAjax() {
        var ajaxObj = startAJAX();
        if(ajaxObj == null) return;
        ajaxObj.onreadystatechange=requestHandler;
 
        var url = "formProcessor.php";
        ajaxObj.open("GET",url,true);
        ajaxObj.send(null);
 
        function requestHandler() {
            if(ajaxObj.readyState==4 && (ajaxObj.status == 200 || ajaxObj.status == 304)) {
                alert('response = '+ajaxObj.responseText);
            }
        }
    }
</script>
 
<button onclick="testAjax();">test</button>
 

formProcessor.php

 
<?php
 
echo 'hello world';
 
?>

functions can see everything outside them unless the function is nested within another function.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
	<title>Untitled</title>
	<meta name="generator" content="BBEdit 9.6">
	
	<script type="text/javascript">
	
	
	(function() {
		b();
		alert(typeof(c) === 'undefined'?'c doesn\\'t exist':'c exist');
	})();
	
	function b() {
		alert('b called');
	
		function c() { }
		
	}
	
	</script>
	
</head>
<body>
</body>
</html>

as per my previous example code.

Yes, requestHandler() will not function as intended in that case because ajaxObj is declared within testAjax(). Therefore, requestHandler() will be called but ajaxObj will be undefined within the context of requestHandler(). Which is a significant difference to the original posters code because the asyncRequest is a global, accessible to all functions. Which is what I meant by it isn’t a functional issue, though we all know the story with globals. The real issue is that the poster is trying to execute dynamic JavaScript with innerHTML, which isn’t going to work. The dynamic JavaScript either needs to be evaled or added as a script node. This is where using event bubbling can come in handle to react to a click on the element added dynamically without using eval() or some other messy technique.

yep ok, didn’t notice that the ajax object was declared globally.

it’s not something I do or would encourage anyone to do.

I just saw the request handler being outside the calling function.

Even in published AJAX examples, it’s a common mistake to refer to the request object by name. It still works when the handler is nested but can cause problems.
Just use the this reference, which will reference the request object regardless of the location of the handling function:

function stateChange(){
		if (this.readyState == 4 && this.status == 200){					  
                document.getElementById('toolArea').innerHTML = this.responseText;
               } 

Thanks Logic Ali :slight_smile:

I think “mistake” is a bit harsh because technically it is not incorrect, but I can see your suggestion is a “better” or more “flexible” way of doing it.

but either way, my preference is to embed the request handler in the calling function, especially since it is unlikely to be used elsewhere, and thus keep everything together.

When the request object is global (which it doesn’t need to be), it could get re-assigned while a previous request is pending, causing the handler of the pending request to address the properties of the wrong object.

yep totally agree :agree: and I posted earlier that I wouldn’t encourage anyone to declare the object globally.

please don’t misunderstand me.

when I said I thought “mistake” was a bit harsh I was referring to referring to the ajax object by its name and not the “this” keyword.

That’s what I understood.

Hi all,

so what’s the best way of handle this? using jQuery or other javascript library?

<script type="text/javascript">

function getTools(url)
{
 var asyncRequest; /* Not global */

 try
   {
    asyncRequest = new XMLHttpRequest();
    asyncRequest.onreadystatechange = stateChange;
    asyncRequest.open('GET', url, true);
    asyncRequest.send(null);
   } 
   catch (exception)
   {
    alert('Your request failed');
   } 
}

function stateChange()
{
 if ( this.readyState == 4 && this.status == 200 )
 {
   document.getElementById('toolArea').innerHTML = this.responseText;
 } 
} 
</script>

So far what I had tried to do is created a separated js file, and put it in each of the html file. After the AJAX call, and the custom function will be called by the event handler.

I would like to know how can I handle this using jQuery or other javascript libraries.

Thanks.

Okay, with jQuery, using the load method.


function getTools(url) {
    $('#toolArea').load(url);
}

Thanks everyone for your great help!