1

I am trying to set up a form so that it submits via ajax when I hit enter. To do this I wanted to trigger the form to submit when the enter key is pressed on the input field. However the keyup event for the enter key keeps firing multiple times if the key is held down any longer than a split second which in turn sends lots of ajax requests causing the browser to crash.

I cannot figure out why the event keeps firing multiple times. Here is the view for the page:

<div class="page-content">
  <div class="l-edit-header">
    <h1>Edit</h1>
    <div class="piece-header">
      <%= image_tag @piece.user.avatar_url(:small), class: "avatar-small" %>
      <div class="piece-header-info">
        <h1><a href="<%= piece_path @piece %>"><%= @piece.title %></a></h1>
        <em>
          By <%= link_to @piece.user.username, user_path(@piece.user) %>
          <%= @piece.created_at.strftime("%B %d, %Y") %>
        </em>
      </div>
    </div>
  </div>

  <div class="l-edit-main">

    <div class="piece-main">
      <%= image_tag @piece.image_url %>
      <p id="piece-description" class="piece-main-description"><%= @piece.description %></p>
      <div class="piece-main-links">
        <%= link_to "Delete", piece_path(@piece), method: :delete if current_user == @piece.user %>
      </div>
    </div>
  </div>

  <div class="l-edit-side">
    <div class="form-container">
      <%= form_tag piece_tags_path(@piece), id: "new_tag", remote: true do  %>
        <%= label_tag :new_tag, "New Tag"%>
        <%= text_field_tag :tag, "", data: {autocomplete_source: tags_url}, placeholder: "Add a tag and press Enter" %>
        <div id="tags" class="piece-tags">
          <%= render partial: "tags/delete_tag_list", locals: {piece: @piece, method: :delete} %>
        </div>
      <% end %>
    </div>

    <div class="form-container">
      <%= simple_form_for @piece do |f| %>
        <%= f.association :category, include_blank: false %>
        <%= f.input :published, as: :hidden, input_html: {value: true} %>
        <%= f.input :title %>
        <%= f.input :description %>

        <div class="form-submit">
          <%= f.button :submit, "Publish" %>
        </div>
      <% end %>
    </div>
  </div>
</div>

and here is the Javascript for the tag form I am trying to work with:

var tagReplace = {
    init: function(){
        //Replace "#tags" with new updated tags html on tag create
        $("#new_tag").on("ajax:success", function(e, data, status, xhr){
            $("#tags").html(data);
            tagReplace.init();
            $("#tag").val("");
        });

        //Replace "#tags" with new updated tags html on teg delete
        $("#tags a[data-remote]").on("ajax:success", function(e, data, status, xhr){
            $("#tags").html(data);
            tagReplace.init();
        });


        $("#new_tag").on("keydown", function(e){
            if (e.which == 13){
                event.preventDefault();
            }
        });

        $("#tag").on("keyup", function(e){
            if (e.which == 13){
                $("#new_tag").submit();
                console.log("pressed enter on new tag");
            }
        });

    },
    getTags: function(){
        $.get( $("#tag").data("autocomplete-source"), tagReplace.initAutocomplete);
    },
    initAutocomplete: function(tagsArray){
        $("#tag").autocomplete({
            source: tagsArray
        });
    }
};

//Initalize
$(document).on('ready page:load', function () {
    tagReplace.init();
});

As you can see I have prevented the default behaviour for the return key being pressed on the form and have added a console.log to count the number of times the event is being triggered.

I thought this could be to do with the fact I am using turbolinks but I can't seem to figure out why.

How can I ensure that the event only gets triggered one time for each time the enter key is pressed? At the moment the Javascript is crashing the browser when I hit enter.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Cu1ture
  • 1,273
  • 1
  • 14
  • 29

1 Answers1

5

You are calling tagReplace.init(); 2 times within itself and, as @Dezl explains in his answer related to this topic, "If you have delegated events bound to the document, make sure you attach them outside of the ready function, otherwise they will get rebound on every page:load event (causing the same functions to be run multiple times)."

Community
  • 1
  • 1
Madness
  • 2,730
  • 3
  • 20
  • 29
  • Thanks. Why does this cause the events to get triggered multiple times? I thought since the init function was only assigning callbacks on ajax:success rather than actually triggering them it wouldn't cause a loop like this? – Cu1ture Jul 24 '15 at 20:06
  • 2
    So every time init() is called it creates not one, but 2 infinite loops. It is like a double rainbow, but much worse. – Madness Jul 24 '15 at 20:07
  • Haha, but don't the init functions inside the ajax:success callbacks only get run once the ajax request is returned successfully? Wouldn't this mean they wouldn't be infinite loops? Sorry if this is obvious I just want to check my understanding. Appreciate the help :) – Cu1ture Jul 24 '15 at 20:10