Jump to content
Larry Ullman's Book Forums

Ajax Questions!


Recommended Posts

Hello everyone,

 

I have tried the first Ajax example in Chapter 13 and it is working fine! The problem is that I want to use this code for an actual site I'm developing and there are two problems.

 

1) The screen updates are too slow. I originally thought that the onChange() event was fired after each keystroke, however, what I figured out is that the event is fired only when a user focuses out of the "username" input field. This can be confusing to the user! Let's say he/she types a username and then focuses on the next field. At that point a "Username is unavailable!" message is displayed, so the user goes back to the "username" field and starts typing in a different username. Until they click on a different input field the "Username is unavailable!" message will will still display, even if they have changed their username to one that is available. Any ideas how I might improve the speed of the Ajax updates? I talked to HarleySan and he suggested that I could run a query once when the user first goes to the registration page and load all the usernames in a javascript array. Then, it could do everything on the user's local machine and give character by character feedback on the availability of a username (similar to how the google searches work).

 

I thought about changing the onChange() event to a keyUp() event. I realize that it will put a bit more strain on the server as a new query will be run with each keystroke, but users will only register once, and the site won't have 1000's of people registering each day, so I don't think this will bring the site down!

 

2) I want to add styles to the Ajax messages (i.e. red or green based on whether the message says a "username" is available or not). I tried to add a style to the <span> elements where the messages are displayed, however, because the page is not reloaded every time the message is changed the styles are not going to be interpreted. It's almost as though the messages displayed by Ajax exist outside of the web page and are not parsed as part of the page. How can I add styles to the messages? Should I add the styles to the echo statements where the messages are actually created inside the checkusername.php page?

	if (mysqli_num_rows($r) == 1) {
	echo '<span style="color: red";>The directory name is unavailable!</span>';
} else {
	echo '<span style="color: green;">"The directory name is available!</span>';
}

Can I even embed a <span> inside of another <span> (there is a <span> element on the form where these Ajax messages are displayed)? Is there another generic css element I could use?

 

Any help would be greatly appreciated!

 

Matt

Link to comment
Share on other sites

Hi Matt,

 

I actually changed onChange to onkeyup. This way the username field is updated as the user types, unless you do have a particularly busy site I wouldn't expect this to be a problem. I also changed the font and colour of the text in the echo statement itself i think, like you have.

 

I was just playing around with the example when I did all this, it wasn't very recent but i'm pretty sure those were the changes I implemented, it seems like we have pretty similar ideas of what we wanted to do. However if those don't work for you, i'll gladly try and find the code i did write.

Link to comment
Share on other sites

I don't think you can embed a <span> inside another one. But you could use <strong class="something" (or id="something" if that makes more sense)>, or <em class="something">, so as to redefine them in your css file (or just use the hierarchy to isolate these <strong> or <em> elements). It would make sense, semantically, since you want to emphasize these messages.

Link to comment
Share on other sites

I made the changes and it works perfectly!

 

The only problem is that, if at any time you hit the ENTER key while you are focused on the "username" field, the Ajax message will disappear. The only way to make it return is to click back in the "username" field and start changing the value again. It's a minor problem, but it obviously isn't going to be acceptable on a live site! Is there any way I can re-trigger the Ajax request whenever the user hits the ENTER key, or is there another solution to this problem?

 

I have a lot of programming experience, but I'm pretty new to javascript (well, not entirely new, but for years I have avoided it like the plague), so I apologize.

 

Thanks,

 

Matt

Link to comment
Share on other sites

For what it's worth, I would never perform an Ajax request onKeyUp. That's just crazy. If a username is 10 characters long, you'd perform 10 requests while entering that? No good. Whatever you're witnessing on a local or small site, Ajax requests onKeyUp is not tenable on a good live site. That's my opinion, anyway.

Link to comment
Share on other sites

For what it's worth, I would never perform an Ajax request onKeyUp. That's just crazy.

Well, it does look awesome, and it works like I want, however, I see what you mean! What are the alternatives then?

Link to comment
Share on other sites

I don't personally see what the problem is with onChange or lost focus. If you must do onKeyUp, I'd call a function onKeyUp, but then only execute the first Ajax request if the value is greater than X characters long. Then you'd also need to create a system so that subsequent keyup events don't trigger another request. You could use a flag variable for this. Or only do an Ajax request onkeyup every 4 or 5 characters.

Link to comment
Share on other sites

You could add an onFocus() event to set the <span> text to an empty string when the user clicks or tabs into the field. That should clear the 'directory name unavailable' message. Assuming your input field is named 'directory_name_label':

 

function clear_directoryname() { // clear directory name response field
 document.getElementById('directory_name_label').innerHTML = '';
}

In the form field <input> element, add:

onfocus="clear_directoryname()"

Link to comment
Share on other sites

I don't personally see what the problem is with onChange or lost focus.

The problem is that the onChange event is being fired when the user clicks outside of the text input field, therefore, they are not getting Ajax updates as they are typing the characters, but rather after they have finished typing and clicked somewhere else. This is far too late in my opinion!

 

What about doing the query, which selects all the usernames, when the page first loads. The query results are then put into an array. Then, I could use the onKeyUp event to constantly check the arrays and show the messages. Since it is all being done on locally on the user's machine, then there is no unnecessary stress being put on the server!

Link to comment
Share on other sites

Matt, as we discussed before, as long as the site remains relatively small, I believe that loading all the user names, etc. into a JS array on page load via Ajax, and then testing against that array is the best bet.

 

There are probably all sorts of ways you could further optimize things, but assuming you have only up to a couple thousand users total, any inefficiency with just using an array to store all your data won't even matter.

 

But yeah, I see the basic premise as follows (and this is subject to critique):

 

1) Make an Ajax call on page load to retrieve all the necessary data. (Use window.onload or some other similar event in the JS.) Also, if you put this after all the HTML is loaded via the PHP, you will notice NO lag from the Ajax call (because the page will already be fully displayed).

 

2) Think of a way to organize all the data you are going to return into a logical string that can be easily parsed on the JS side.

For example, you might do something like the following:

 

echo 'username1,username2,username3delimitingstuffhere!galleryfolder1,galleryfolder2,galleryfolder3';

 

3) Assuming the string above is returned, you'd probably want to store the data in a JS array as follows:

 

var aSeparatedDataTypes = oAjax.responseText.split('delimitingstuffhere!'); // oAjax is your Ajax object.

var aSeparatedData;

aSeparatedData[0] = aSeparatedDataTypes[0].split(','); // This'll store your user names in a 2D array starting at the 0 position.

aSeparatedData[1] = aSeparatedDataTypes[1].split(','); // This'll store your gallery folder names in a 2D array starting at the 1 position.

 

4) Once you have the data parsed on the JS side, you're good to go. For example, for the user name field, you can just loop through the aSeparatedData[0] array every onkeyup event. Naturally, you could further optimize this, if necessary. (For example, if a user name starts with 'A', quit the loop once you hit the B's.)

 

5) To display your warning messages, you will want to use DHTML (in other words, JS to edit the HTML and CSS on the fly).

 

6) I'd have an error class prepared in the CSS ahead of time, and then mark an invisible span next to the user name field, etc. with that class when a user name is already taken. Also, you'll want to use something like the innerHTML property to output the proper message to the now visible span.

 

Anyway, it's late and I'm tired. I think that's enough to get started with. I'm open for debate and critique, just not right now.

Link to comment
Share on other sites

HartleySan,

 

Thank you so much for this!

 

Since I will only be checking one field the code will be even easier!

I could just return the whole query result from Ajax.

 

Are query results delimited by anything besides whitespace?

Should the following line read:

 

var aSeparatedData = oAjax.responseText.split(' '); // oAjax is your Ajax object.

Matt

Link to comment
Share on other sites

Well, let's not confuse the query results with the response text from the PHP script.

 

I honestly don't know exactly how PHP handles MySQL query results, but as we all know, they're returned one row (record) at a time, and a while loop is typically used to loop through all the records. (Actually, the truth is, I don't know any way beyond using a while loop to go through MySQL query results in PHP. Larry, perhaps you, or someone else could enlighten us on this.)

 

Anyway, the point is, once you get your query results, you need to manually construct a string to return by using a while loop to run through all the results. I just (jokingly) used the phrase "delimitingstuffhere!" as the delimiter, but anything could be used. I hesitated from using a single space, as I'm not sure if user names can contain spaces, which would cause parsing errors.

 

Other alternatives are to return XML or use JSON, but both seem way to over the top for your needs.

 

Well, I'll leave it at that for now.

Link to comment
Share on other sites

  • 2 weeks later...

I agree with Antonio on this one. I personally don't like the dynamic checking of names, as it'll constantly be checking my user name before I even type what I want. Instead, I prefer a button to check after I have typed the name I want. It's also a much easier load on the server.

Link to comment
Share on other sites

(Actually, the truth is, I don't know any way beyond using a while loop to go through MySQL query results in PHP. Larry, perhaps you, or someone else could enlighten us on this.)

 

The query result is always in array form. That is why it's nice to use while loops. As mysql(i)_fetch_array will return NULL when there's no more results, this is the same code in practice:

 

while ( ($row = mysql_fetch_array($result, MYSQL_ASSOC)) != NULL ) 
{
  echo $row['name'];
}

 

A For-loop could be constructed this way:

 

$num = 1; // number of times

for ($i=0; $i<$num; $i++) 
{
  $row = mysql_fetch_array($result, MYSQL_ASSOC)
  echo $row['name'];
}

Link to comment
Share on other sites

If you want isolate a few rows from the array returned by a query, you can use mysqli_data_seek and mysqli_fetch_row. I used that for seeking the median value from a database. I'm sure there are many other ways of doing this, but I needed first to group results, then to calculate the median for each group, and this was the simplest way I found (it's just the beginning of the script):

 

foreach ($_POST['menu_langues'] as $key => $id)
{
 // I fetch the results for each group separately and order them
 // on "l", which is the column I'll use for calculating the median
 $query = "SELECT
t.id_langue AS id,
lg.langue AS langue,
t.l AS longueur
 FROM textes AS t
 INNER JOIN langues AS lg
USING (id_langue)
 WHERE t.id_langue = $id
 ORDER BY t.l";

 if ($result = @mysqli_query($bdd, $query))
 {
// Number of results
$n = mysqli_num_rows($result);

// I seek the first row returned by the query
// and fetch the content (the minimal value for "l")
mysqli_data_seek($result, 0);
$mini = mysqli_fetch_row($result);

// I seek the last row (its position in the array is equal to n - 1)
// and fetch the content (the maximal value for "l")
$rgx = $n - 1;
mysqli_data_seek($result, $rgx);
$maxi = mysqli_fetch_row($result);

// If the query returned an odd number of results,
// the median value is in the middle row
// For instance, with 15 results, the median is in the 8th row,
// numbered 7 in the array.
if ($nb % 2 == 1)
{
 	$rg = $nb / 2;
 	mysqli_data_seek($result, $rg);
 	$rang = mysqli_fetch_row($result);

 	// and so on
}
  }
}

 

This couple of functions can be very useful sometimes.

  • Upvote 1
Link to comment
Share on other sites

 Share

×
×
  • Create New...