Archive for the ‘Code’ Category

Get Table Cell Height’s with Javascript

March 7, 2007

Before I begin, I’ve only tested this using Firefox on a Mac, but it should work in any browser…

I’ve seen a lot of people trying to find out the height of a table cell, and I have on many occasions have tried myself only to give up. You can get the height of a cell, but it’s always going to be the height you set it to, not the height it becomes after data has been placed into it and it expands. What a pain, right? Well, I’ve found a solution to this problem, though it involves a few DIVs and javascript.

So, say you have this:

<td>
	a really long string of text that takes up multiple
	lines and you have no way of knowing how tall this TD
	is.
</td>

Instead of that, change it to:

<td>
	<div id="content_top"></div>
	<div id="content">
		a really long string...
	</div>
	<div id="content_bottom"></div>
</td>

See where I’m going? We have pretty much created two points to measure by… the content_top DIV and the content_bottom DIV. Now, how do we measure them?

Whenever we need the height, we call a function, say getTDHeight(). getTDHeight looks something like this:

function getTDHeight(){
	contentTopDiv = document.getElementById("content_top")
	contentBotDiv = document.getElementById("content_bottom")
	contentTop = getPixelsFromTop(contentTopDiv);
	contentBottom = getPixelsFromTop(contentBotDiv);
	contentHeight = contentBottom - contentHeight;
	alert("The cell height is " + contentHeight);
}

So basically we are asking a function called getPixelsFromTop to give us the number of pixels the element “content_top” is from the top of the browser window. The same for the element “content_bottom”. With this, we’ll have something like content_top = 100 and content_bottom = 350. So we take the top value away from the bottom and that leaves us with the height of the cell… 250px

The getPixelsFromTop function is no biggie either:

function getPixelsFromTop(obj){
	objFromTop = obj.offsetTop;
	while(obj.offsetParent!=null) {
		objParent = obj.offsetParent;
		objFromTop += objParent.offsetTop;
		obj = objParent;
	}
	return objFromTop;
}

All it does is constantly adds the objects location from the top of it’s parent container, then loops up to that parent and repeats until it hits the top of the DOM, then returns the value.

Here is the code working.

Hope this helps. I’ve seen a lot of frustration over this on many forums. I know it’s a pain, but at least it is possible.

Helping a User Fill Out a Form

November 22, 2006

A while back I ran across an article about giving a user feedback as they are filling out a form. I can’t remember where I saw the article (I wish I could), but I really liked the idea.

The article wasn’t talking about validating a form and sending the user a message in an alert box, but something more ajaxy, a form assistant of sorts… something that helps them as they go along. This might seem like overkill for the simple form to follow, but it could be really helpful for a long, complicated form.

The idea has been on the back burner of my mind for a while and last night while driving home I started to think about it again and wanted to throw together a demo.

-
The Demo

1.jpg

First off, we have a simple form for some sort of registration. Style sheets are used to define the look of the forms. As usual, required fields are noted, in this case with a red outline.

-

2.jpg

Now, if a field is tabbed to, or the user places the mouse over it, I wanted to give them a little feedback about the field they are about to, or are, using.

-

3.jpg

Tabbing through the fields will allow the focused form element to display its “notes”.

-

4.jpg

Now, we do have some required fields, so let’s look at entering in some data. Once the form loses focus, JavaScript checks to see if the user left anything in the field. If so, we want to turn the border to green, representing that the required information has been supplied.

-

5.jpg

Originally, I had a submit button, but I thought that might be a bit boring for this example. Instead, if the form field has something in it, we want to save it. So now that the user has tabbed out of the field, notice the “progress” icon appearing in the field.

-

6.jpg

In this demo, I’m not really saving the data, but if I was, once the readyState returned 4, I would change the “progress” icon to a “saved” icon. And, after a second of two, we fade that away.

That’s basically what I was thinking for this demo. And I think it turned out kind of cool. With that, let’s take a quick look at how it’s done. You can download the prototype if you want. The link is near the bottom of this article.

-
How it’s done

First off, we create a basic form using a table. Each row needs its own ID, such as:

<tr id="fnameRow" style="height:32;width:370;" height="32">

Notice that I’m also setting the height and width of the TR via STYLE, this is important for the placement of the feedback DIV, which I’ll get to later. Another thing that is important, for my coding method, is the name of the ID. fname is the name of my form field, and when JavaScript looks at what field I’m editing, It will know at the same time which row needs to change.

In that row, I need my form fields, and they look something like this:

<input 
	class="textboxR" 
	type="text" 
	name="fname" 
	size="40" 
	maxlength="255" 
	onfocus="inputFocus(this)" 
	onmouseover="inputFocus(this)" 
	onmouseout="inputBlur(this)" 
	onblur="inputBlur(this)"
>

Notice the class; I have a textbox style for fields that are not required, and a textboxR for fields that are, the only difference being the border color. Now that I think about it, I probably should set this via JavaScript (with the fR_xxxx vars that follow).

Now that we have our form set up, I need to set some JavaScript variables. They are, for the most part:

  • borderReq – The color of a required border
  • borderReg – The color of the regular field border
  • borderYellow – What color yellow to use for the border.
  • borderGreen – What color green to use for the border.
  • bgRowOver – What color to use for the row background when it’s highlighted.
  • bgRowNorm – What color to use for the row background when it’s not highlighted
  • fI_fname – (fI = fieldInput) The text I want to appear in the note for form field fname. There is one of these for each field.
  • fR_fname – (fR = fieldRequired) Whether or not the field is required. There is one of these for each field.
  • lastRowTop – The top of the last row (of placing saving div)
  • lastRowWidth – The width of the last row (of placing saving div)

First, the field “notes” are displayed by aligning 4 DIVs, and changing the background color of the row. I couldn’t just place a div over it, because then it would cover (and disable) the interaction with the form field behind it; the onmouseover and onmouseout would stop working.

-

7.jpg

Here, let me turn on borders (in orange) on the four DIVs to illustrate. Notice the top left and right, and bottom left of the highlighted row has DIVs containing PNGs to create the round corners… I couldn’t stand the squarish look of just changing the row background color. Right below the bottom left rounded corner DIV is the DIV for the feedback, which contains it’s own round corners and DIV for content.

Back to the form fields… You can see that onFocus and onMouseOver call inputFocus (and pass it that form element) an onBlur and onMouseOut call inputBlur.

Now, let’s look at onFocus. Here’s the pseudocode:

  1. Get the name of the form (form.name)
  2. Get the content of the form (form.value)
  3. Set the background color of that row
  4. Set the border color of the form field to yellow
  5. Align my DIVs
    1. I need to get the location of the row in relation to the upper left of the browser. I don’t want to manually figure out each X/Y position of each row, and I might decide to move the form, so we have to do it with code. Two great functions (getAbsoluteLeft and getAbsoluteTop) to do this can be found here.
    2. I need to get the row width and height so I know how big to make note window (and how far down to push it). This is why the ROW had a STYLE with its width and height set. If you try to get its dimensions without specifying them, JavaScript will return nothing.
    3. Now that I have these, align my 4 DIVs in relation to that row.
  6. Put the note message you want to display into message area.
    1. I have a variable for each form element with the message I want to display.
  7. Turn on the visibility of all DIVs

Now, when we leave that field, let’s hide the note (inputBlur):

  1. Get the name of the form (form.name)
  2. Get the content of the form (form.value)
  3. Reset the background color of the row.
  4. If the field content contains nothing
    1. If it is required, set the border to red
    2. If it is not required, set the border color to gray
  5. Otherwise, they did type in something so
    1. Change to border color to green
    2. Compare what they typed to the original content we collected when they focused on it.
    3. If the contents differ (they added content to a blank field or edited a field they previously typed something in) save the data.
      1. Position the saving DIV
      2. Save the data

If you would like to download the working prototype, click here. If you do anything really cool with it, let me know.

-
Wrap up:

Some things I would like to do with this next:

  • Use an image for the checkbox… it just doesn’t match the other fields visually
  • Improve the graphic design of the form. I tried to keep this simple and didn’t spend too much time with it.
  • Figure out why I need those adjustors when lining up my DIVs.
  • Add form checking, so for instance, we can make sure the email address is an email address and if not, put an additional comment in the form note.
  • Optimize some of the HTML

Building a Javascript HTTP Request Queue

October 28, 2006

Anyone working with AJAX knows the headache of dealing with HTTP Requests. They can only be done one at a time, and any one called while waiting on another leaves that request blowing in the wind. The only way to successfully handle request that can be fired off at any time, is to use a queuing system to manage the calls. I’m sure there are other recommendations out there, but I always enjoy the challenge of creating my own solutions.

Before I get into the code of my AJAX Request Queue, I want to show you the debug window. I set this up as a div with a few form fields that I can pop open at any time to show me what’s going on in the queue.

callqueue.jpg

bullet_1 Form Field “queueMsg” – A general message about the state of the queued requests.
bullet_2 Form Field “queueInCall” – The value of the inCall variable… where or not the HTTP Request line is busy.
bullet_2 Form Field “queueReadyState” – The value of the readystate variable of the current HTTP Request, if in a call.
bullet_2 Form Field “queueTest” – The list of calls waiting to be executed. This example shows that there are 2 pending calls, one to get some slides and another to get comments for a particular slide.
 

The following is the Javascript code for the queue, all contained in a .JS file called createRequestObject, which is linked on pages that need to make HTTP Requests. I’m not going to go into too much detail about the code, just the overall idea.

Setting up initial variables:

// TURN ON DEBUG WINDOW FEEDBACK
// I WILL WANT TO DISABLE THIS IN THE LIVE VERSION…
var httpTesting = true;
// SET MY REQUEST OBJECT
var http = createRequestObject();
// VARIABLE TO TRACK IF WE ARE CURRENTLY IN A CALL
var inCall = false;
// QUEUE FOR CALLS
var callToArray = new Array();
// QUEUE FOR FUNCTION TO EXECUTE WHEN CALL COMPLETE
var returnToArray = new Array();

Create our Request Object:

function createRequestObject() {
  var reqObj;
  var browser = navigator.appName;
  if(browser == "Microsoft Internet Explorer"){
    reqObj = new ActiveXObject("Microsoft.XMLHTTP");
    isIE = true;
  }else{
    reqObj = new XMLHttpRequest();
  }
  return reqObj;
}

Insert calls to the Queue:

function sendCall(whereTo, returnTo){
  // GET THE NEXT ARRAY ITEM AND REMOVE FROM THE ARRAY
  callToArray.push(whereTo);
  returnToArray.push(returnTo);
}

Watcher function that is called on intervals to set out queued calls based on inCall variable:

function callQueue(){
  // IF WE HAVE A WAY OF MONITORING THE QUEUE, UPDATE IT
  if(httpTesting){
    calls = "";
    for(i=0;i<callToArray.length;i++){
      calls += callToArray[i] + "n";
    }
    document.getElementsByName("queueTest")[0].value = calls;
    document.getElementsByName("queueInCall")[0].value = inCall;
    document.getElementsByName("queueReadyState")[0].value = http.readyState;
  }
  // CHECK THE QUEUE AND SEND THE NEXT CALL IN LINE
  if(!inCall && callToArray.length > 0){
    // DO WE HAVE ANYTHING IN THE QUEUE?
    if(callToArray.length > 0){
      // WE DO, SO GET THE FIRST ITEM IN THE CALL ARRAY AND REMOVE IT
      whereTo = callToArray.shift();
      returnTo = returnToArray.shift();
      // SEND THAT CALL
      doCall(whereTo, returnTo);
    }else{
      // UPDATE DEBUG QUEUE
      if(httpTesting){
        document.getElementsByName("queueMsg")[0].value = "no items in queue.";
      }
    }
  }else{
    // UPDATE DEBUG QUEUE
    if(httpTesting){
      if(inCall){
        document.getElementsByName("queueMsg")[0].value = "currently in a call.";
      }else{
        document.getElementsByName("queueMsg")[0].value = "no items in queue.";
      }
    }
  }
}

If we are not currently in a call and have a pending request, the call URL is sent here:

function doCall(whereTo, returnTo){
  inCall = true;
  http.open('get', whereTo);
  // DO WE HAVE A FUNCTION TO CALL ONCE CALL IS COMPLETED?
  if(returnTo.length > 0){
    eval("http.onreadystatechange = "+returnTo);
  }
  // SEND CALL
  http.send(null);
}

This is a generic return function intended to just clear the inCall variable when we have a call type that we don’t need/expect a response from:

function hr_inCall(){
  if(http.readyState == 4){
    inCall = false;
  }
}

And finally, to start the Queue engine, we have:

var queueWatcher = setInterval(callQueue, 100);

All that together successfully runs my Request Queue and gives me the ability to monitor it. A pause button would be a nice addition, but I haven’t had time to implement it. However, it would simply need to clear the queueWatcher interval on pause and restart it on play.

Finally, for any page that needs to make a call, for example, a log in, I would just have the submit button call a function that gathers the data and sends it to the queue, as such:

function checkLogin(){
  username = document.getElementsByName("loginEmail")[0].value;
  password = document.getElementsByName("loginPassword")[0].value;
  document.getElementById("loginSubmit").innerHTML = "Checking Login...";
  sendCall("req/getLogin.php?username=" + username + "&password=" + password, "hr_checkLogin");
}

You will notice when I sendCall, I have “hr_checkLogin” as the second parameter, which, once the call is complete, will call:

function hr_checkLogin(){
  if(http.readyState == 4){
    inCall = false;
    response = http.responseText
    if(response != "0"){
      document.getElementById("loginArea").innerHTML = "Login successful. Welcome back. <A href='projects.php'>Projects</a>";
    }else{
      document.getElementById("loginSubmit").innerHTML = "<font color='red'>Login Failed</font>";
    }
  }
}

The important thing is to always set inCall to false once your call is complete, or the next request will never be processed.

Like I said, there may be better solutions out there, but I’m a big fan of building my own, which always helps me to become a better programmer.

Any thoughts?

“Save as” real data when the page is generated with Javascript document.write

September 27, 2006

I ran into an interesting problem today that involved the need to “Save as” a web page that is 99% generated with Javascript document.writes. Of course, saving this page will give you only the javascript code, not what you are seeing on the screen – in my case, lots of table rows created by parsing a text file.

Having javascript read in a text file meant I was doing everything local, using FileSystemObject, so that was (A) Why I could not use a server side language to generate the file, and (B) good that I had the proper security to read and write files.

I found a lot of people pulling their hair out over this and no real solution. If you are working locally, can use FileSystemObject and need to save actual data, not javascript document.writes, then I think this may help you.

First off, I import my text file, parse the data, and document.write it into tables, such as:

document.write("<tr'>");
document.write(" <td valign='top'>"+(i+1)+"</td>");
document.write(" <td valign='top'>"+sfc[0]+"</td>");
document.write(" <td valign='top'>"+sfc[1]+"</td>");
document.write(" <td valign='top'>"+sfc[2]+"</td>");
document.write("</tr>");

On screen, I may get something like:

1 01-002 Artist Need to remove border from the image.
2 01-003 Programmer Make the next link disabled until they click on ‘Note’
3 01-003 Programmer Change the direction text to “Continue” when they click on ‘Note’

But if I try to save that page to the desktop, I get the javascript document.writes listed above… until you do this:

Surround your javascript with a div and give it an ID:

<body>
<div id=”content”>
<script>
//… tons of JS code to parse and write your document.
</script>
</div>
</body>

Now, right after that div, call a function and pass it the div’s innerHTML:

<body>
<div id=”content”>
<script>
//… tons of JS code to parse and write your document.
</script>
</div>
<script>
saveHTML(document.getElementById('content').innerHTML);
</script>
</body>

This is going to take the HTML contained within the div and send it to a function. It sends the javascript code, but also it sends the generated HTML table. Perfect! Now, in that function, I split the code on the closing </script> tag to get rid of all my JS code, and wrap what’s left in simple html, head, body tags and write a new .html file.

Now, all we have to do is redirect. So after our call to saveHTML, add:

<body>
<div id=”content”>
<script>
//… tons of JS code to parse and write your document.
</script>
</div>
<script>
saveHTML(document.getElementById('content').innerHTML);
document.location.href = html;
</script>
</body>

Oh, and the variable ‘html’ is just the location/filename I use to write the file to, and now redirect to. For instance

var html = “c://some/directory/to/write/to.html”

Now, once we redirect, we are on a true HTML page with no content being generated by Javascript and we can successfully “Save as” and have a document with actual data in it.

Hope this helps.

Simple Div Scrolling

September 25, 2006

I needed a simple interaction to scroll an overlay:hidden div when the mouse is over a certain area. The resources I found online where bloated or part of larger packages that you had to buy, so I threw this together.

Here’s what screen looks like:

scrollingDiv screenshot

bullet_1 We have a list of connected users to our application, within a limited space.
bullet_2 This area scrolls the user list up. This area is brighter because it is active; the mouse is over it.
bullet_3 This area scrolls the user list down.
 

The code behind the layout is a three row table. The top and bottom rows contain the code to scroll, and the middle row has a div, overflow set to hidden, that contains our HTML for listing the users. Briefly:

<table width="300" border="0" cellspacing="1" cellpadding="0" bgcolor="#ffffff">
<tr>
<td bgcolor="#F3F9F1" width="100%" id="peopleUp" onMouseOver="scrollStart('Up', 'people', 'peopleUp');" onMouseOut="scrollEnd('peopleUp');">
<img src="gfx/spacer.gif" height="5" width="1">
</td>
</tr>
<tr>
<td>
<div id="people" style="height:100px; overflow-y:hidden; overflow-x:hidden;">
Loading user list…
</div>
</td>
</tr>
<tr>
<td bgcolor="#F3F9F1" id="peopleDown" onMouseOver="scrollStart('Down', 'people', 'peopleDown');" onMouseOut="scrollEnd('peopleDown');">
<img src="gfx/spacer.gif" height="5" width="1">
</td>
</tr>
</table>

Now, our JS on the page contains:

var ourInterval;
var origColor = "#F3F9F1";
var overColor = "#36FF00";
var scrollSpeed = 50;
var scrollHeight = 5;

Most of the above vars should be self explanatory, but just in case, scrollHeight is how many pixels the div moves up of down every millisecond, which is determined by scrollSpeed.

Next we have our functions that are called by the mouse over and out of the table’s top and bottom TD.

function scrollStart(direction, divID, elementID){
//CHANGE THE BACKGROUND COLOR OF THE TD THE MOUSE IS OVER
document.getElementById(elementID).style.backgroundColor = overColor;
// REPEATED CALL EITHER scrollUp OR scrollDown
ourInterval = setInterval("scroll"+direction+"('"+divID+"')", scrollSpeed);
}
function scrollEnd(which){
// OUR MOUSE IS OUT, SO RETURN TD TO ORIGINAL COLOR
document.getElementById(which).style.backgroundColor = origColor;
// STOP CALLING THE SCROLL FUNCTION
clearInterval(ourInterval);
}

And finally:

function scrollUp(which){
// SET THE SCROLL TOP
document.getElementById(which).scrollTop = document.getElementById(which).scrollTop - scrollHeight;
}
function scrollDown(which){
// SET THE SCROLL TOP
document.getElementById(which).scrollTop = document.getElementById(which).scrollTop + scrollHeight;
}

Quick and easy. The scrollUp and scrollDown functions could even be combined… we could just send it the direction var in scrollStart and then determine if we should add or subtract our scrollTop with that.

I hope this helps if you are looking for an easy way to handle div scrolling.