Reputation: 87
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
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
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
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