My First (Relatively) large Javascript Project

T

Tom Cole

While I've done javascript work for as long as I can remember (since
Netscape first released it), I've only ever used it for trivial
things, change a color here, validate a text element there, blah blah
blah. With Ajax (actually, the uncovering of the XmlHTTPRequest
object) I absolutely see the benefit of moving more of the UI work to
the client, rather than doing page refreshes.

I know there are a bunch of libraries out there (prototype, DOJO,
etc.) but I wanted to write one that specifically dealt just with Ajax
requests, and handling the responses (i.e. parsing the DOM tree of an
XML response or the instantiation of an object for a JSON response,
etc.). As I progressed I added logging capabilities and a few touches
here and there.

The biggest part of the challenge was trying to wrap myself around the
Object vs Prototype style in javascript. I still only know 1/100th of
what I need to, but I seem to have been able to write a relatively
self contained little package that only exposes what I wanted to and
not expose what was 100% internal. Writing classes (or objects or
whatever the JS community calls them) is completely foreign to me. I
am only use to writing some inline functions that are called by
various elements on the page.

So...I put up a dumb little test page at http://www.lamatek.com/Ajax/
and was looking for constructive criticism. Not of the test page, but
of the javascript code. Is my encapsulation okay...that sort of thing.
It all appears to work as I intended, but because of my lack of
knowedge & experience it would be nice to get your opinions.

Thanks in advance.
 
L

-Lost

Tom Cole said:
While I've done javascript work for as long as I can remember (since
Netscape first released it), I've only ever used it for trivial
things, change a color here, validate a text element there, blah blah
blah. With Ajax (actually, the uncovering of the XmlHTTPRequest
object) I absolutely see the benefit of moving more of the UI work to
the client, rather than doing page refreshes.

I know there are a bunch of libraries out there (prototype, DOJO,
etc.) but I wanted to write one that specifically dealt just with Ajax
requests, and handling the responses (i.e. parsing the DOM tree of an
XML response or the instantiation of an object for a JSON response,
etc.). As I progressed I added logging capabilities and a few touches
here and there.

The biggest part of the challenge was trying to wrap myself around the
Object vs Prototype style in javascript. I still only know 1/100th of
what I need to, but I seem to have been able to write a relatively
self contained little package that only exposes what I wanted to and
not expose what was 100% internal. Writing classes (or objects or
whatever the JS community calls them) is completely foreign to me. I
am only use to writing some inline functions that are called by
various elements on the page.

So...I put up a dumb little test page at http://www.lamatek.com/Ajax/
and was looking for constructive criticism. Not of the test page, but
of the javascript code. Is my encapsulation okay...that sort of thing.
It all appears to work as I intended, but because of my lack of
knowedge & experience it would be nice to get your opinions.

Well, at least you are not using $() as a wrapper for getElementById().

All in all though, it looks fairly clean. (I am no expert though.) At first glance,
aside from your strange markup-in-comments style, I thought it looked similar to Kruse's
AjaxRequest (AjaxToolbox).

Also, just out of plain nosiness, are you able to publicize the contents of "test.do?"

-Lost
 
T

Tom Cole

Well, at least you are not using $() as a wrapper for getElementById().

All in all though, it looks fairly clean. (I am no expert though.) At first glance,
aside from your strange markup-in-comments style, I thought it looked similar to Kruse's
AjaxRequest (AjaxToolbox).

Also, just out of plain nosiness, are you able to publicize the contents of "test.do?"

-Lost- Hide quoted text -

- Show quoted text -

Where is Kruse's AjaxRequest? Can you give me a link to it? Maybe I've
just beat an Old Horse to death (I'm sure I have, but at least I
learned something while I was shipping him :)

Sure, the test.do is just a simple servlet that dishes out test data.
Here it is (sorry if the format is messed up, I use Google Groups so
who knows...):

<pre>
import java.io.BufferedWriter;
import java.io.IOException;
import java.io_OutputStreamWriter;
import java.util.Date;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Test extends HttpServlet {

public void doPost(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
String xml = request.getParameter("type");
if (xml == null) {
BufferedWriter output = new BufferedWriter(new
OutputStreamWriter(response.getOutputStream()));
output.write("This is a null test.");
output.flush();
output.close();
}
else if (xml.equalsIgnoreCase("text")) {
BufferedWriter output = new BufferedWriter(new
OutputStreamWriter(response.getOutputStream()));
output.write("This is only a test.");
output.flush();
output.close();
}
else if (xml.equalsIgnoreCase("xml")){
response.setContentType("text/xml");
response.setHeader("Expires", (new Date()).toGMTString());
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
BufferedWriter output = new BufferedWriter(response.getWriter());
String value = "<?xml version=\"1.0\" encoding=\"UTF-8\"?
<request>";
value += "<date>" + (new Date()).toGMTString() + "</date>";
Enumeration headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String header = (String) headers.nextElement();
value += "<header><name>" + header + "</name><value>" +
request.getHeader(header) + "</value></header>";
}
value += "</request>";
output.write(value);
output.flush();
output.close();
}
else if (xml.equalsIgnoreCase("json")) {
response.setContentType("text/x-json");
response.setHeader("Expires", (new Date()).toGMTString());
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
BufferedWriter output = new BufferedWriter(response.getWriter());
String value = "{ \"header\": { ";
value += "\"date\": \"" + (new Date()).toGMTString() + "\", ";
Enumeration headers = request.getHeaderNames();
value += "\"headers\": [ ";
int count = 0;
while (headers.hasMoreElements()) {
String header = (String) headers.nextElement();
if (count > 0)
value += ", ";
value += "{ \"name\": \"" + header + "\", \"value\": \"" +
request.getHeader(header) + "\" }";
count++;
}
value += "] } }";
output.write(value);
output.flush();
output.close();
}
else {
BufferedWriter output = new BufferedWriter(new
OutputStreamWriter(response.getOutputStream()));
output.write("This servlet does not support requests of type " +
xml);
output.flush();
output.close();
}
}

public void doGet(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
doPost(request, response);
}
}
</pre>
 
T

Tom Cole

Nice work for a first project.

Couple of points:

Line 105:
/**Attempts to hold browser type. Only works after an AjaxRequest has
been created.*/
var browser = (navigator.appName.indexOf('Microsoft') >= 0);

This puzzles me; you're apparently using a variable named "browser" to
indicate whether you're in an IE environment. This is cryptic; I
can't tell from a given context what the variable "browser" means
without looking up its definition here. Also, this variable, as
you're defining it here, will only work internally within the context
of your AjaxRequest function, so saying it will only work after and
AjaxRequest has been creating is moot - it still isn't in scope
outside your AjaxRequest function.

Yeah, I just changed this today because I needed to know the browser
type before a request was made in case the browser logging window was
displayed. This way I would know what to set the style.position
attribute to (IE6- doesn't support position: fixed). I'll change that
comment right away.
Line 119ff:
Your use of both style.top and style.bottom is a little strange. For
Drag & Drop you should only need to use top and left; bottom and right
are implicit. Set the height and width using style.height and
style.width; retrieve it using offsetHeight.

I forgot to mention that I did not write the Drag section. It was
something I found last minute because I wanted to display my log in a
floating div, rather than a separate window (as I originally wrote
it). I found that script at http://www.dynamicdrive.com/dynamicindex11/domdrag/.
Lines 142-144:
No point in assigning dummy functions; leave these blank or assign
them to null.

See above comment.
Lines 241ff:
No need for a try/catch block here; nothing you're doing will throw an
error. Also, try/catch all over the place can be disastrous for
debugging.

Again see above. I did add all the try catch blocks between line 115
and 251 as I was originally having a problem with some cross-browser
functionality. I will remove them now.
265ff:
Use "var" keyword to declare names for the following functions.

One question here. By using var prefix, it won't change the scope or
anything? I don't want these functions (methods) called by/available
to anything but an AjaxRequest.
365ff:
Ah, the "getRequest" function. First off, you shouldn't be using the
IE-specific conditional comments here or anywhere, for that matter.
All you need is:

var xml;
var _factories = [function() { return new XMLHttprequest(); },
function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
function() { return new ActiveXObject("Microsoft.XMLHTTP"); }];

for (var i=0; i<factories.length; i++) {
try {
xml = _factories();
break;
} catch(e) {
continue;
}

}


I will investigate this.
370ff:
Your "parseResponse" functions are all so similar, you should try to
reuse functionality from them. Something along the lines of:

function makeResponseHandler(fn) {
return function() {
if (xmlhttp.readyState != 4) return;
if (xmlhttp.status == 200) {
try {
value = fn(xmlhttp);
} catch (ex) { // log errors
}
}
}

}

var parseJSONResponse = makeResponseHandler(function(response)
{ return eval(response.responseText); });
var parseTextResponse = makeResponseHandler(function(response)
{ return response.responseText; });
var parseXMLResponse = makeResponseHandler(function(response) { return
response.responseXML; });

etc.

This is a great idea. I wrote them one at a time, text first, then XML
and lastly JSON. This would be a definite first place to look to
reduce LOC.
Also, as a general rule, I don't like to set any element.style
properties using JS except for top, left, height, and width; all
others should be specified in a stylesheet and toggled using CSS
classnames. Helps keep your style separate from your behavior.

I didn't want the end user to have to do any special additions other
than importing the library and start making requests.
 
T

Tom Cole

Where is Kruse's AjaxRequest? Can you give me a link to it? Maybe I've
just beat an Old Horse to death (I'm sure I have, but at least I
learned something while I was shipping him :)

Found it at: http://www.ajaxtoolbox.com/request/source.php. There are
a LOT of things there that I like. There are things in mine I like to
that I don't see there but..... But I like how he keeps an array of
all active requests, the automatic appending of unique-ids to prevent
caching, timeouts, grouping requests, etc. Seems WAY more industrial
that what I have working. I'll have to review the code a bit and see
what changes I want to make. Very,very nice, though.
Sure, the test.do is just a simple servlet that dishes out test data.
Here it is (sorry if the format is messed up, I use Google Groups so
who knows...):

<pre>
import java.io.BufferedWriter;
import java.io.IOException;
import java.io_OutputStreamWriter;
import java.util.Date;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Test extends HttpServlet {

public void doPost(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
String xml = request.getParameter("type");
if (xml == null) {
BufferedWriter output = new BufferedWriter(new
OutputStreamWriter(response.getOutputStream()));
output.write("This is a null test.");
output.flush();
output.close();
}
else if (xml.equalsIgnoreCase("text")) {
BufferedWriter output = new BufferedWriter(new
OutputStreamWriter(response.getOutputStream()));
output.write("This is only a test.");
output.flush();
output.close();
}
else if (xml.equalsIgnoreCase("xml")){
response.setContentType("text/xml");
response.setHeader("Expires", (new Date()).toGMTString());
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
BufferedWriter output = new BufferedWriter(response.getWriter());
String value = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><request>";

value += "<date>" + (new Date()).toGMTString() + "</date>";
Enumeration headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String header = (String) headers.nextElement();
value += "<header><name>" + header + "</name><value>" +
request.getHeader(header) + "</value></header>";
}
value += "</request>";
output.write(value);
output.flush();
output.close();
}
else if (xml.equalsIgnoreCase("json")) {
response.setContentType("text/x-json");
response.setHeader("Expires", (new Date()).toGMTString());
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
BufferedWriter output = new BufferedWriter(response.getWriter());
String value = "{ \"header\": { ";
value += "\"date\": \"" + (new Date()).toGMTString() + "\", ";
Enumeration headers = request.getHeaderNames();
value += "\"headers\": [ ";
int count = 0;
while (headers.hasMoreElements()) {
String header = (String) headers.nextElement();
if (count > 0)
value += ", ";
value += "{ \"name\": \"" + header + "\", \"value\": \"" +
request.getHeader(header) + "\" }";
count++;
}
value += "] } }";
output.write(value);
output.flush();
output.close();
}
else {
BufferedWriter output = new BufferedWriter(new
OutputStreamWriter(response.getOutputStream()));
output.write("This servlet does not support requests of type " +
xml);
output.flush();
output.close();
}
}

public void doGet(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
doPost(request, response);
}}

</pre>- Hide quoted text -

- Show quoted text -
 
T

Tom Cole

Well, at least you are not using $() as a wrapper for getElementById().

All in all though, it looks fairly clean. (I am no expert though.) At first glance,
aside from your strange markup-in-comments style, I thought it looked similar to Kruse's
AjaxRequest (AjaxToolbox).

The strange markup in the comments is to facilitate a "javadocs" style
tag document generator. Makes it's simpler to provide consistent
docs... plus it's similar to what I'm used to with Java classes.
 
M

Matt Kruse

David said:
Also, as a general rule, I don't like to set any element.style
properties using JS except for top, left, height, and width; all
others should be specified in a stylesheet and toggled using CSS
classnames. Helps keep your style separate from your behavior.

I agree for things which are truly style-related. For example, don't set
font-size or things like that.

But for positions, display, visibility, z-index, all those kinds of things,
it doesn't make sense to use a class. Plus, using a class locks you into a
certain set of properties rather than being able to tweak just one that you
want to adjust. Further, swapping a class is often slower than simply
applying a specific style.

So, as a general rule, I would say there is no general rule ;)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top