tags:

views:

266

answers:

7

I'm totally newbie to jquery and ajax, my recently project is to help the representatives (reps) to manage customer quotations online. I have a page which displays all the quotations in a big table.

I've managed to use ajax to fetch and display the quotations which associate to a particular rep after i click that rep's name. But the only problem is the speed of response. The first few clicks are ok and very smooth. But after several tries, the response become slow and I cant even scroll down the webpage, and later on the web browser craches....

Please have a look at my ajax code. here it is:

<!-- Data display area -->
<br /><input type="image" id="printbtn" value="Print" src="images/printer.png"/><br />

  <div id="container">
    <div id="content">

    </div>  
  </div>  
<!-- Data display area -->  

<!-- AJAX FETCH QUOTES DATA + Tablesorter + FIXED TABLE HEADER--> 
<script type="text/javascript">
//<![CDATA[ 
$(function(){

$("a.repID").click(function(){ 
    $('div#loader').append("<p align='center'><img src='images/loadingbar2.gif' id='loading' /></p>"); 
 var repID = $(this).attr("title");   

$.ajax({
    type:'POST',
    url:'quote_info.php',
    data:'repID=' + repID,
    cache: false,

    success:function(data)
    {
       $("#container").html('<div id="content">' + data + '</div>');
       $("#loading").fadeOut(500, function() {$(this).remove();});
       $("#sortme").tablesorter();
       $('.tbl').fixedtableheader(); 
    }
});
return false;
});
});
</script>
<!-- AJAX FETCH QUOTES DATA + Tablesorter + FIXED TABLE HEADER-->

Problem found, the tablesorter and fixed header inside the success handler are causing the problem, but if I put them outside the handler, they will not work, where should i put them? or how should i do to have that two functions, many thanks!!!!

Server side php code:

<?php
header('Content-Type: text/html; charset=ISO-8859-1');
$conn=mysql_connect("localhost","root","");
mysql_select_db("auma",$conn);

$repID = $_POST['repID'];

if ($repID == "All") {
    $whereClause = "";
} else {
    $whereClause = "WHERE repID='$repID'";
} 

$quoteinfoSQL = "SELECT q.quoteWeek, q.quoteID, q.quoteRev, q.customerName, q.repID, 
             q.quoteDesc, q.quoteValue, q.quoteProject, q.quotePM, q.quoteDR, q.quoteDS, 
             a.followUp, a.quoteStatus, a.furtherAction, a.UKConNo, a.clientRef, a.CorS  
             FROM auma_quote q 
             INNER JOIN auma_action a on a.quoteID = q.quoteID 
             $whereClause  
             ORDER BY q.quoteWeek DESC, q.quoteID DESC";



// execute the statement
$rsQuoteinfo = mysql_query( $quoteinfoSQL );

$html.= "<br />";

if ($repID == "All") {
    $html.= "<img src=\"images/users.png\" />&nbsp; Quotations from all representatives.</h6>";
} else {
    $html.= "<img src=\"images/users.png\" />&nbsp; Quotations from <b>$repID</b>.</h6>";
} 



$html.= "<br />";


$html.= "<table id=\"sortme\" class=\"tbl\">";
$html.= "<thead>";
$html.= "<tr>
         <th>Week</th>
         <th>Quote ID</th>
         <th>Rev</th>
         <th>Customer</th>
         <th>Rep ID</th>
         <th>Description</th>
         <th>Gross Value</th>
         <th>Project</th>
         <th>GP%</th>
         <th>Date Received</th>
         <th>Date Sent</th>
         <th>Follow up Action</th>
         <th>Result</th>
         <th>Further Action</th>
         <th>UK Contract No.</th>
         <th>Client Ref.</th>
         <th>Contractor or Specification</th>
         <th></th>
         </tr>";
$html.= "</thead>";
$html.= "<tbody>";

while($quoteinfoRow = mysql_fetch_array($rsQuoteinfo)){
    $quoteWeek = $quoteinfoRow['quoteWeek'];
    $quoteRev = $quoteinfoRow['quoteRev'];
    $customerName = htmlspecialchars($quoteinfoRow['customerName']);
    $repIDs= $quoteinfoRow['repID'];
    $quoteID= $quoteinfoRow['quoteID'];
    $quoteDesc = htmlspecialchars($quoteinfoRow['quoteDesc']);
    $quoteValue = htmlspecialchars($quoteinfoRow['quoteValue']);
    $quoteProject = htmlspecialchars($quoteinfoRow['quoteProject']);
    $quotePM = $quoteinfoRow['quotePM'];
    $quoteDR = $quoteinfoRow['quoteDR'];
    $quoteDS = $quoteinfoRow['quoteDS'];
    $followUp = htmlspecialchars($quoteinfoRow['followUp']);
    $quoteStatus = htmlspecialchars($quoteinfoRow['quoteStatus']);
    $furtherAction = htmlspecialchars($quoteinfoRow['furtherAction']);
    $UKConNo = $quoteinfoRow['UKConNo'];
    $clientRef = $quoteinfoRow['clientRef'];
    $CorS = htmlspecialchars($quoteinfoRow['CorS']);

    $html.= "<tr>";
    $html.= "<td>$quoteWeek</td>";
    $html.= "<td>$quoteID</td>";
    $html.= "<td>$quoteRev</td>";
    $html.= "<td>$customerName</td>";    
    $html.= "<td>$repIDs</td>";
    $html.= "<td>$quoteDesc</td>";
    $html.= "<td>$quoteValue</td>";
    $html.= "<td>$quoteProject</td>";
    $html.= "<td>$quotePM</td>";
    $html.= "<td>$quoteDR</td>";
    $html.= "<td>$quoteDS</td>";
    $html.= "<td>$followUp</td>";
    $html.= "<td>$quoteStatus</td>";
    $html.= "<td>$furtherAction</td>";
    $html.= "<td>$UKConNo</td>";
    $html.= "<td>$clientRef</td>";
    $html.= "<td>$CorS</td>";

    $html.= "<td align=\"center\"><a href=\"quotedetails.php?quoteID=$quoteID&amp;customerName=$customerName\" ><input type=\"image\" src=\"images/edit.png\" /></a></td>";

    $html.= "</tr>";
}  // while



$html.= "</tbody>";
$html.= "</table>";
$html.= "<br />";
echo $html;


?>

Response sample from Firebug:

<br /><img src="images/users.png" />&nbsp; Quotations from <b>NA</b>.</h6>
<br />
<table id="sortme" class="tbl">
<thead>
<tr>

         <th>Week</th>

         <th>Quote ID</th>

         <th>Rev</th>

         <th>Customer</th>

         <th>Rep ID</th>

         <th>Description</th>

         <th>Gross Value</th>

         <th>Project</th>

         <th>GP%</th>

         <th>Date Received</th>

         <th>Date Sent</th>

         <th>Follow up Action</th>

         <th>Result</th>

         <th>Further Action</th>

         <th>UK Contract No.</th>

         <th>Client Ref.</th>

         <th>Contractor or Specification</th>

         <th></th>

         </tr>
</thead>
<tbody>
<tr>
<td>9</td>
<td>Q42389</td>
<td>0</td>
<td>Worldwide Procurement Services Ltd</td>
<td>NA</td>
<td>1 x Motor (Z011.274)</td>
<td>£2,954</td>
<td>COM: 649862</td>
<td>spares net</td>
<td>2010-02-28</td>
<td>2010-03-03</td>
<td></td>
<td>ORDERED</td>
<td></td>
<td>28824</td>
<td></td>
<td></td>
<td align="center"><a href="quotedetails.php?quoteID=Q42389&amp;customerName=Worldwide Procurement Services Ltd" ><input type="image" src="images/edit.png" /></a></td>
</tr>
<tr>
<td>1</td>
<td>Q41883</td>
<td>1</td>
<td>Ital (International Trading Alliance) Ltd</td>
<td>NA</td>
<td>1xSAM10.1E75</td>
<td>£2,059</td>
<td>COM: 553697</td>
<td>25% net</td>
<td>2010-01-05</td>
<td>2010-01-08</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td align="center"><a href="quotedetails.php?quoteID=Q41883&amp;customerName=Ital (International Trading Alliance) Ltd" ><input type="image" src="images/edit.png" /></a></td>
</tr>
</tbody>
</table>
<br />
+1  A: 

Without walking through the code in my head...I can tell you that your Javascript is more than likely presenting a memory leak.

Try tuning your Javascript code to better handle the objects its creating and you should notice an improvement. Here's an article (that links to a couple of other good articles as well) to get you started:

Resolving JavaScript Memory Leaks

Justin Niessner
thanks you Justin, but I dont know how to do that, would you please help me on turning that?
Patrick
+2  A: 

Mmmh not quite sure but if you are not removing (or overwriting) #sortme or .tbl every time, then these lines might be related to your performance problem:

$("#sortme").tablesorter();
$('.tbl').fixedtableheader();

I have no insight in these functions but it could be that you bind handlers to the elements every time you do the Ajax call. Just bind them once and see if it gets better.

Update:

Maybe it is sufficient to put them outside, e.g.:

$(function() {
    $("#sortme").tablesorter();
    $('.tbl').fixedtableheader();

    $("a.repID").click(function(){...});  
});

As for the tablesorter plugin, it has an update method (see examples), so you can put this inside your success method:

success: function(date) {
    //...
    $("#sortme").trigger("update"); 
}
Felix Kling
If i remove that two function from 'success' function, they will not work. or can you show me how to bind them once but keep them working?
Patrick
@Patrick: Well I don't know how your HTML looks like. You should put them outside your success function. It could also be that it doesn't help at all. What is the data you get back and where to you put it?
Felix Kling
thank you Felix, you help me found the source of the problem. it is because of that two function inside the success handler.my data is a large table which contains like 11 <th> and totally 969 rows, some reps got like 200 rows, and some others got like 50~100 rows, so pretty big data set. I put them in the <div id="content">. i updated my question, please have a look at the code.
Patrick
@Patrick: But that means that `data` always contains a complete table, with an element with ID `#sortme`? Can you post the relevant part of your HTML and a sample response from your server?
Felix Kling
I've tried what you said just now, still no luck, I click the <th>, it doesnt sort, I scroll down the page, header doesnt fix as well.
Patrick
@Patrick: Ok, I see that every response contains an element with ID `#sortme`. Then your original code should be correct. But there is still the performance problem... does the headers of the tables change? If not, maybe you can create an empty table in your HTML file and only let the Ajax call get and inject the data rows. (and then you can use the code from my answer)
Felix Kling
You are rite, I coded the whole table in server side file, so each time a ajax called, the complete table return to that display area. That's why the tablesorter and headerfix didnt work if I didnt put them outside the success handler. any suggestions on how to solve my problem? thank you in advance.
Patrick
ok I'll try that, thanks m8, i'll update the result later.
Patrick
ahhhh, still not turn good..... but thanks anyway.
Patrick
A: 

Your scripting appears to be ok, but I'm wondering if your data could possibly have any single quotes inside of it?

fudgey
It wouldn't matter if it did.
T.J. Crowder
+1  A: 

Given just the information in your question, it's hard to know what the source of the slowdown is.

Personally I would use Firebug to "profile" the code by stepping through important lines. Set a breakpoint on the $.ajax({ line as well as in the success handler (on the $("#container").html('<div id="content">' + data + '</div>'); line). When inside the success handler, and stopped on that first line, click the "step over" button of Firebug to see how long it takes to execute each line. Also, if there is a large time delay between the sending of the AJAX request and execution of the success handler, then your server-side script may be a major contributor to the performance degradation.

One small thing that you can do right away: move the id='loading' attribute from the <img> tag to the <p> tag. The $("#loading").fadeOut(500, function() {$(this).remove();}); is basically causing a <p> element to "leak" with every call to the success handler and $('div#loader').append(".... Your success handler is not properly cleaning up.

Daniel Trebbien
thanks for your suggestion, I've moved that attr away from <img>, but still no luck, the browser got slower and slower as I do more ajax fetchin. I was thinking about the handler cleaning, but I dont know how, can you show me how to clean it up after every ajax call therefore, each new call will be a fresh start.
Patrick
+1  A: 

You are vulnerable to SQL injection:

$repID = $_POST['repID'];

[...]

$whereClause = "WHERE repID='$repID'";
Dolph
A: 

I had a problem with data overload with my tables. It would take up to 10 seconds to load my tables. So I used a 3rd-party data table tool. It does server-side scripting. Well worth the time to implement.

Get rid of the cdata remark tag at the start of script block. Use Firebug and see if it is loading the click event more than once. Select script tab in Firebug each time you click. I bet you have memory leak.

Gutzofter
A: 

I used the additional information that you have provided to write up a test case (http://pastebin.com/6S3RU0Fs). Using this test case, each time that I click the "click" link, the web browser process comsumes more and more memory which it does not release back to the operating system, indicating a memory leak. Commenting out line 15 causes the memory leak issue to disappear, so I'm pretty sure that the problem is in the fixedtableheader plugin.

You can still use the fixedtableheader plugin as long as you don't call fixedtableheader() multiple times. Instead, you can use the "update" functionality of the tablesorter plugin. To do this, you need to modify your PHP and Javascript somewhat.

The PHP script is altered to only output the table rows:

<?php
header('Content-Type: text/html; charset=ISO-8859-1');
$conn=mysql_connect("localhost","root","");
mysql_select_db("auma",$conn);

$repID = $_POST['repID'];

if ($repID == "All") {
    $whereClause = "";
} else {
    $whereClause = "WHERE repID='" . mysql_real_escape_string($repID, $conn) . "'";
} 

$quoteinfoSQL = "SELECT q.quoteWeek, q.quoteID, q.quoteRev, q.customerName, q.repID, 
             q.quoteDesc, q.quoteValue, q.quoteProject, q.quotePM, q.quoteDR, q.quoteDS, 
             a.followUp, a.quoteStatus, a.furtherAction, a.UKConNo, a.clientRef, a.CorS  
             FROM auma_quote q 
             INNER JOIN auma_action a on a.quoteID = q.quoteID 
             $whereClause  
             ORDER BY q.quoteWeek DESC, q.quoteID DESC";



// execute the statement
$rsQuoteinfo = mysql_query( $quoteinfoSQL, $conn );


while($quoteinfoRow = mysql_fetch_assoc($rsQuoteinfo)){
    $quoteWeek = $quoteinfoRow['quoteWeek'];
    $quoteRev = $quoteinfoRow['quoteRev'];
    $customerName = $quoteinfoRow['customerName'];
    $repIDs= $quoteinfoRow['repID'];
    $quoteID= $quoteinfoRow['quoteID'];
    $quoteDesc = $quoteinfoRow['quoteDesc'];
    $quoteValue = $quoteinfoRow['quoteValue'];
    $quoteProject = $quoteinfoRow['quoteProject'];
    $quotePM = $quoteinfoRow['quotePM'];
    $quoteDR = $quoteinfoRow['quoteDR'];
    $quoteDS = $quoteinfoRow['quoteDS'];
    $followUp = $quoteinfoRow['followUp'];
    $quoteStatus = $quoteinfoRow['quoteStatus'];
    $furtherAction = $quoteinfoRow['furtherAction'];
    $UKConNo = $quoteinfoRow['UKConNo'];
    $clientRef = $quoteinfoRow['clientRef'];
    $CorS = $quoteinfoRow['CorS'];

    $html.= "<tr>";
    echo "<td>" . htmlspecialchars($quoteWeek) . "</td>";
    echo "<td>" . htmlspecialchars($quoteID) . "</td>";
    echo "<td>" . htmlspecialchars($quoteRev) . "</td>";
    echo "<td>" . htmlspecialchars($customerName) . "</td>";    
    echo "<td>" . htmlspecialchars($repIDs) . "</td>";
    echo "<td>" . htmlspecialchars($quoteDesc) . "</td>";
    echo "<td>" . htmlspecialchars($quoteValue) . "</td>";
    echo "<td>" . htmlspecialchars($quoteProject) . "</td>";
    echo "<td>" . htmlspecialchars($quotePM) . "</td>";
    echo "<td>" . htmlspecialchars($quoteDR) . "</td>";
    echo "<td>" . htmlspecialchars($quoteDS) . "</td>";
    echo "<td>" . htmlspecialchars($followUp) . "</td>";
    echo "<td>" . htmlspecialchars($quoteStatus) . "</td>";
    echo "<td>" . htmlspecialchars($furtherAction) . "</td>";
    echo "<td>" . htmlspecialchars($UKConNo) . "</td>";
    echo "<td>" . htmlspecialchars($clientRef) . "</td>";
    echo "<td>" . htmlspecialchars($CorS) . "</td>";

    echo "<td align=\"center\"><a href=\"" . htmlspecialchars("quotedetails.php?quoteID=" . urlencode($quoteID) . "&customerName=" . urlencode($customerName)) . "\" ><input type=\"image\" src=\"images/edit.png\" /></a></td>";

    echo "</tr>";
}  // while

Note that I made some changes for efficiency and security:

  • Use mysql_fetch_assoc rather than mysql_fetch_array because you aren't indexing columns by number.
  • echo the HTML directly rather than building up the $html string, which is echoed anyway.
  • Escape all user input with mysql_real_escape_string if it is used to build an SQL query (helps prevent SQL injection attacks).
  • Consistently escape all text from databases and user input with htmlspecialchars and/or urlencode if it is sent back as HTML (helps prevent script injection/XSS).

Your Javascript is altered by moving the static HTML into the HTML file, calling tablesorter() and fixedtableheader() only once each on page load, using the data from the PHP script to set the inner HTML of the #sortme <tbody>, and triggering the "update" event on #sortme. I have modified the test case to show you what I mean (http://pastebin.com/qFn2jRLB).

Daniel Trebbien