Mar 31

Date published: 3-31-2014

Rails version: 4.0.0

Counting with Javascript

It’s common in Rails apps that take input to use text areas to enter large blocks of text. It’s also common to have a limit on the amount of text that can be entered to ensure the database doesn’t get filled with pointlessly long posts or comments. It’s easy to put a validation in a model to limit the amount of text that can be entered, but it’s nice to give feedback in the view to show how much of the limit the user has already used.

Validation in the model

For our example, we’re going to assume we have a model with a field called “synopsis” that will let the user enter a short description of the item stored in that model. We might use that synopsis to show some information on a search page, but we don’t want it to be really long because each item shown on the page will only have a small blurb of text with it so we can fit a lot of results on one screen. The model might have fields like this:

#  id             :integer          not null, primary key
#  name           :string(255)
#  description    :text
#  synopsis       :text

We don’t know how long a synopsis might look good on our search results page, so we arbitrarily decide we want to limit our synopsis to 300 characters which is too long for a string. We need a validation on the model to limit the length of the synopsis, so in our list of validations, we’ll have something like this:

validates :synopsis, length: { maximum: 300 }

That will make sure that if the user enters a synopsis, it can’t go over the length that we decided on. Next let’s setup the form that will accept input from the user.

The form

We want to show something on the form to let the user know how much of the allotted space they’ve used. In the form where we have controls for user input, we’ll want to have something like this:

<%= f.label :synopsis %>
<%= f.text_area :synopsis, id: "counted", rows: 6 %>
<p>Current character count: <span id="char-count">0</span> / 300</p>

Note that both the text area and the character count have an id associated with them. We’ll need to use Javascript to update the character count, and those ids give us a way to get hold of those elements in the script that we write. Add the following script after the form in your view:

  <script type="text/javascript">
    $(document).ready(function() {
      $('#char-count').html($('#counted').val().length);
    });

    $('#counted').keyup(function() {
      $('#char-count').html($(this).val().length);
    });
  </script>

There are two different functions here that do similar things. The first function makes sure that when the page is finished loaded, it checks whatever is written in the text area and sets the character count. This doesn’t matter when we’re creating a new item, but it will come into play when editing an existing item that already has a synopsis. The second function occurs whenever there is a keyup event on the text area, and it also updates the character count to show the new length of the text in the text area.

It looks like everything is how we want it, but there is one bug that could come up if the synopsis has any carriage returns. The Javascript function will see returns as only one character, but Rails saves them as two characters. Basically Javascript recognizes it as “\n” whereas Rails stores it as “\n\r” which is a problem if the user tries to use all the available space. We need to add something to the Javascript to recognize carriage returns as two characters. The easiest way to handle this is replace all returns with what Rails will expect before calculating the length. Replace the two lines where we calculate the length with this:

$('#char-count').html($('#counted').val().replace(/\n/g, "\n\r").length);

$('#char-count').html($(this).val().replace(/\n/g, "\n\r").length);

Now if you reload the page, you’ll notice that when you hit the “Return” key, the character count goes up by two instead of one. The user can see as they type just how much more they can type and be sure that the item will save correctly as long as they don’t go over the character limit.

Leave a Reply

preload preload preload