👌

フォームに動的文字数制限を設ける

2024/02/08に公開

実際の動き

それぞれのテキストエリアにて設定した文字数の制限を超えると、文字数カウンターが赤色になり、submitボタンが押せなくなります。

開発環境

ruby '3.1.2'
rails '6.1.7'
jQuery '1.12.4'
cloud9

View

余計な装飾は除いています。

hoge.html.erb
<%= form_with model: @user, local: false do |f| %>
    <div class="characters-limit-container">
        <%= f.label :name, 'ニックネーム' %>
        <%= f.text_field :name, class: "characters-limit", data: {limit: 10} %>
        <!--新規投稿画面の場合-->
        <p class="output-count">0/10字</p>
        <!--編集画面の場合-->
        <p class="output-count"><%= @user.name.size %>/10字</p>
    </div>
    <div class="characters-limit-container">
        <%= f.label :introduction, "自己紹介" %>
        <%= f.text_area :introduction, class: "characters-limit", data: {limit: 400} %>
        <!--新規投稿画面の場合-->
        <p class="output-count">0/400字</p>
        <!--編集画面の場合-->
        <p class="output-count"><%= @user.introduction.size %>/400字</p>
    </div>
    <%= f.submit '変更を保存', class: "add-disabled" %>
<% end %>

<div class="characters-limit-container">
  <!--この中に記述-->
</div>
<div class="characters-limit-container">
   <!--この中に記述-->
</div>

それぞれのフォームグループはcharacters-limit-containerクラスを持つタグで囲うようにしてください。
制限したい文字数はdata: {limit: xx}で設定することができます。

JavaScript

hoge.js
$(document).ready(function(event){
    $('.characters-limit').on('input', function(){
      let limit = $(this).data('limit');
      let countNum = $(this).val().length;
      let output = $(this).closest('.characters-limit-container').find('.output-count');
      output.text(countNum + '/' + limit + '字');
      
      $('.characters-limit').each(function() {
        if ($(this).val().length > $(this).data('limit')) {
          $('.add-disabled').prop('disabled', true);
          return false;
        } else {
          $('.add-disabled').prop('disabled', false);
        }
      });
      
      if(countNum > limit){
        output.addClass('over-limit');
      } else {
        output.removeClass('over-limit');
      }
      
    });
});

$('.characters-limit').on('input', function()
characters-limitクラスを持つ要素に文字を入力する度に動作します。"keyup"ではなく"input"を用いるのはマウス右クリックで張り付けした文字もカウントするためです。

let limit = $(this).data('limit');
入力されたテキストエリアの"data-limit"属性から、制限したい文字数を取得します。

let countNum = $(this).val().length;
文字数をカウントし、変数に代入します。

let output = $(this).closest('.characters-limit-container').find('.output-count');
入力したテキストエリアの一番近くのcharacters-limit-containerクラスを持つ親要素内において、output-countクラスを持つ要素を探します。今回の場合は1つ上の階層にあるので、closest()の代わりにparent()メソッドを用いても問題ありません。

$('.characters-limit').each(function() {
    if ($(this).val().length > $(this).data('limit')) {
      $('.add-disabled').prop('disabled', true);
      return false;
    } else{
      $('.add-disabled').prop('disabled', false);
    }
});

全てのcharacters-limitクラスに関して、入力された文字数が制限を上回っていないかチェックします。1つでも制限を超えている場合は、add-disabledクラスを持つsubmitボタンに、disabled属性を追加することでボタンを押せなくします。

if(countNum > limit){
  output.addClass('over-limit');
} else {
  output.removeClass('over-limit');
}

入力したテキストエリアについて、もし、入力した文字数が制限より多ければ、over-limitクラスを加えます(文字色を赤に変更)。

CSS

hoge.css
.over-limit{
  color: red;
}

好みに合わせて変更してください。

おわりに

制限したい文字数をjs側で設定する文字数カウンターの記事は多くありますが、view側で設定する方法を紹介した記事は見つけられず困ったので自分で書いてみました。
view側で設定できるほうが汎用的に使えて便利だと思います。

Discussion