Perikles
Perikles

Reputation: 87

jQuery ajax requests to Rails server doubling each time

I've set up a simple demo app (with users and messages) to test using jQuery, Ajax, and Rails together. The idea is that when you mouseover a user row on the user index page, the page will use Ajax to load that user's first message. This functionally 'works', but the number of server GET requests to fetch the data doubles on each mouseover, until the page slows to a crawl. Can anyone help me figure out why this is happening?

The user index.html.erb that displays the output looks this:

  ...
<% @users.each do |user| %>
    <tr class="user_name" id="<%=user.id %>">
       <td><%= user.name %></td>
       <td><%= user.email %></td>
       <td><%= link_to 'Show', user %></td>
       <td><%= link_to 'Edit', edit_user_path(user) %></td>
       <td><%= link_to 'Destroy', user, confirm: 'Are you sure?', method: :delete %></td>
    </tr>
   <% end %>
</table>

<div id="first_message"></div>
...

My jQuery-based Ajax code (in assets/ajax.js) looks like this:

$(document).ready(function () {
  $('.user_name').mouseover(function() {
    var userId = $(this).attr('id');
    $('#first_message').load(window.location + userId + "/first_message");
  });
});

I added the following route to handle loading the message:

match 'users/:id/first_message' => 'users#first_message'

The controller to handle the request looks like this:

class UsersController < ApplicationController
   def first_message
     @user = User.find(params[:id])

     respond_to do |format|
        format.html # first_message.html.erb
     end
   end

Finally the html response page (first_message.html.erb) for the Ajax request looks like this:

<h3> <%= @user.name %>'s first message: </h3>
<% if @user.messages.length > 0  %>
    <%= @user.messages[0].content %>
<% else  %>
      ...
<% end %>

Any thoughts?

Upvotes: 1

Views: 635

Answers (3)

Alok Swain
Alok Swain

Reputation: 6519

Not entirely sure but looks like after the HTML is placed inside the div#first_message, the layout also gets rendered again which leads to the binding of the mouseover event again. This would lead to the get request call getting multiplied.

Upvotes: 0

coreyward
coreyward

Reputation: 80041

In addition to what Dylan Markow has mentioned about your layout, you have some performance gotchas in your JS:

The mouseover event fires when the cursor enters child elements as well, so in this case every time you hover over a td. You should use mouseenter instead to avoid this.

http://api.jquery.com/mouseover/

After you have retrieved the data, you should unbind the event so you never fetch the same information again:

$(function () {
  $('.user_name').mouseenter(function() {
    var userId = $(this).attr('id');
    $('#first_message').load(window.location + userId + "/first_message");
    $(this).unbind('mouseenter');
  });
});

Of course, you're losing that data because you're not storing it (opting to instead just use load). You should probably use a $.get and keep the data around (you can store it with the tr element via $(this).data('first-message', httpResponseData);).

Upvotes: 0

Dylan Markow
Dylan Markow

Reputation: 124419

I would assume that this line below is loading in your first_message.html.erb file in it's entirety, including the layout which requires your ajax.js file. Because of this, your ajax.js file gets included over and over again.

$('#first_message').load(window.location + userId + "/first_message");

You can either turn your first_message view into a partial, or you can tell it not to render the layout with all the JS files, etc:

format.html { render :layout => false } # first_message.html.erb

Upvotes: 1

Related Questions