この記事で扱うRatyは、divひとつを星評価のUIに変えるJavaScriptプラグイン(npmパッケージ名はraty-js)です。日本語の解説記事は$('#element').raty({ score: 3 })というjQuery記法で書かれたものがほとんどですが、この書き方はもう動きません。2022年10月2日公開のv4.0.0でjQuery依存そのものが取り除かれ、起動構文がnew Raty(element, options)に変わったためです。
やっかいなのは、古い記事の手順どおりnpm install raty-jsを実行すると最新の4.3.0が入る点です。インストールは成功し、ファイルも配置され、それでも星は出ません。原因がバージョン差にあると気づきにくい構図になっています。
以下は、公式changelogとUPGRADE.md、およびraty-js 4.3.0のソースを根拠に、旧記法からの移行点、画像版とフォント版のファイル配置、初期化で生成されるDOM、Rails 8での保存と表示までを順に整理したものです。
まとめ
先に結論だけ並べます。根拠は各セクションで扱います。
| 論点 | 結論(raty-js 4.3.0基準) |
|---|---|
| 起動 | new Raty(el, options) を作り init() を呼ぶ |
| jQuery記法の最終版 | raty-js 3.1.1。v4系には $.fn.raty が無い |
| 公式npmパッケージ | raty-js。raty は無関係な別物 |
| 設定の優先順位 | 要素の data-* がJSのオプションに勝つ |
| 最終リリース | 4.3.0 / 2024-04-28(2026-08-05時点で更新なし) |
既存プロジェクトがv2系のまま止まっているなら、移行は書き換えというより置き換えに近い規模になります。まずどこが失効したのかを押さえます。
v4.0.0で失効したjQuery記法と新旧オプションの対応
Ratyのバージョンは3世代に分かれます。v2系までがjQueryプラグイン、v3系がjQuery依存を残したままの内部リファクタリング、v4系が純粋なJavaScriptクラスです。世代をまたぐと動かなくなるのは、破壊的変更が2回入っているからです。
jQuery記法が動かない直接の原因
公式changelogのv4.0.0には「The jQuery dependency was dropped, so the syntax to start Raty was changed」と明記されています。v4系のsrc/raty.jsはclass Ratyを定義してexport default Ratyするだけの構造で、jQuery.fnへの登録処理を持ちません。$.fn.ratyが生えないため、$('#element').raty(...)はis not a functionで止まります。
さらにv4.2.0(2023-08-24)では、テストコードからもjQueryが取り除かれました。changelogの表現は「Drops jQuery from tests and so from the entire project」です。プロジェクト全体からjQueryが消えたという宣言なので、待っていても旧記法が復活することはありません。
なお、RatyがjQueryを手放したこととjQuery本体の動向は別の話です。jQuery自体は4.0系へ進んでおり、そちらの変更点はjQuery 4.0とは?新機能・変更点と3系からの移行ポイントを総まとめで扱っています。
書き換えが必要な5つの箇所
手を入れる箇所は次の表に集約できます。旧の書き方は世代で異なるため、v2系とv3系を分けています。
| 対象 | v2系(3.0.0未満) | v3系(3.1.1まで) | v4.3.0 |
|---|---|---|---|
| 起動 | $('[data-raty]').raty({}) |
同左 | new Raty(el, {}).init() |
| 共通パス | $.raty.path = 'assets' |
同左 | { path: 'assets' } |
| キャンセル | { cancel: true } |
{ cancelButton: true } |
同左 |
| 関数呼び出し | $('#el').raty('score') |
$('#el').data('raty').score() |
raty.score() |
| clickの引数 | (score, evt) |
同左 | (score, element, evt) |
このうち事故になりやすいのはcancelの改名です。v4に{ cancel: true }を渡してもエラーは出ません。defaultOptions()にcancelというキーが存在せず、渡された値がどこからも読まれないまま無視されるためです。キャンセルボタンだけが静かに消え、コンソールには何も出ません。
加えてv3.0.0ではdestroy・reload・setの3関数が削除されています。代替APIは用意されていないので、これらを呼んでいるコードはインスタンスを作り直す設計へ変える必要があります。
raty-js 4.3.0の導入手順とファイル配置
導入で最初につまずくのはパッケージ名です。npmでratyを引くと、koaベースのNode.jsフレームワークという無関係なパッケージが出てきます(1.0.5・2017-02-21公開)。星評価プラグインの公式パッケージはraty-jsで、リポジトリにgithub.com/wbotelhos/ratyが設定されています。
npm・CDN・3種類のビルドファイルの選び分け
インストール自体は1行です。
npm install raty-js
そのあと、どのファイルを読むかで書き方が変わります。4.3.0のpackage.jsonはmainにsrc/raty.js、typesにsrc/raty.d.tsを指定しています。src/raty.jsはexport default Ratyを持つES Modulesなので、バンドラやimportmap経由なら次の形で読み込めます。
import Raty from 'raty-js';
const raty = new Raty(document.querySelector('[data-raty]'), { score: 3 });
raty.init();
scriptタグで読む場合はbuild/配下です。ここで参照名が3通りに分かれます。build/raty.jsとbuild/raty.min.jsはクラス宣言を含む通常スクリプトなので、後続のスクリプトからRatyという名前で参照できます。ただしwindow.Ratyはundefinedのままです。クラス宣言はグローバルオブジェクトのプロパティにならないため、window.Ratyを前提にした遅延初期化コードは失敗します。
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/raty.min.js"></script>
3つ目のUMD版(build/raty.umd.min.js)だけは形が違い、名前空間オブジェクトをglobalThis.ratyに置きます。このファイルでは素のRatyが未定義になり、クラス本体はwindow.raty.defaultから取ります。AMDやCommonJSと共存させる必要がなければ、raty.min.jsを選ぶ方が迷いません。
画像で星を出す場合に必要な5ファイルとpath指定
既定のstarTypeはimgで、星をPNG画像として描画します。npmパッケージのsrc/images/にはstar-on.png・star-off.png・star-half.png・cancel-on.png・cancel-off.pngの5枚が同梱されています。半星とキャンセルボタンを使わないなら前者2枚だけで動きます。
pathを省くと、生成されるimgタグのsrcはディレクトリを持たないstar-on.pngになります。ブラウザは現在のページURLからの相対で解決するため、/products/12のような階層下では404です。配信ディレクトリを明示してください。
new Raty(document.querySelector('[data-raty]'), {
path: '/assets/raty',
score: 3
}).init();
フォントアイコンで星を出す場合(starType: ‘i’)
画像を置きたくない場合はstarTypeにiを指定します。星はimgではなくi要素として生成され、star-on-png・star-off-pngといったクラス名が付きます。見た目はsrc/raty.cssのWebフォントが担当し、サイズはfont-size: 2emが既定です。
<link rel="stylesheet" href="/assets/raty/raty.css">
注意したいのがraty.cssの中身です。@font-faceのsrcが./fonts/raty.eotのような相対パスで書かれているため、CSSだけをコピーしてフォント一式を置き忘れると、星の位置に何も表示されないか豆腐文字が並びます。raty.cssと同じ階層にfonts/ディレクトリごと配置するのが確実です。
初期化で生成されるDOMと主要オプションの挙動
defaultOptions()が返すオプションは35個ありますが、実装で毎回触るのは一部です。使用頻度の高いものを既定値付きで並べます。
| オプション | 既定値 | 役割 |
|---|---|---|
score |
undefined |
初期スコア |
scoreName |
'score' |
hidden fieldのname |
number |
5 |
星の数 |
numberMax |
20 |
星の数の上限 |
path |
undefined |
アイコンの配信ディレクトリ |
starType |
'img' |
img か i |
readOnly |
false |
読み取り専用 |
noRatedMsg |
'Not rated yet!' |
未採点時のtooltip |
half |
false |
半星での投票 |
halfShow |
true |
半星での表示 |
round |
{ down: 0.25, full: 0.6, up: 0.76 } |
半星の丸め境界 |
cancelButton |
false |
取り消しボタン |
hints |
['bad', 'poor', 'regular', 'good', 'gorgeous'] |
星ごとのtooltip |
target |
undefined |
tooltipの表示先要素 |
残りの21個はアイコンの個別指定(starOn・starHalf等)とtarget系の細かい制御です。まずは上の14個で組み立てて、足りなければ公式tutorialを引く順序が早いです。
init()を呼ぶまで何も起きない構造
v4のコンストラクタはthis.elementとthis.optを代入するだけで、DOMには触れません。描画はinit()の中です。v2系の$(el).raty()が呼んだ瞬間に描画されたのと違い、init()を書き忘れるとdivは空のままになります。例外も出ないので、移行時に見落としやすい差分です。
init()を通すと、対象要素の中に星の要素と、スコアを保持するinput type="hidden"が生成されます。{ score: 3 }で初期化した場合、hidden fieldはname="score" value="3"です。フォーム送信時に評価値が飛ぶのはこのhidden fieldのおかげで、独自にinputを用意する必要はありません。
Railsのようにmodel[attribute]形式のパラメータ名が必要ならscoreNameを使います。
new Raty(document.querySelector('[data-raty]'), {
scoreName: 'review[rating]',
path: '/assets/raty'
}).init();
これでhidden fieldはname="review[rating]"になり、Strong Parametersのreviewスコープにそのまま乗ります。
読み取り専用表示に切り替えるreadOnly
投稿済みの評価を表示するだけならreadOnly: trueです。v4では星のイベントリスナーを外すのではなく、要素にpointer-events: noneを当てる実装に変わりました(changelog v4.0.0「The read only function now uses pointerEvents over unbind the listeners」)。同時にdata-read-only="true"が付き、hidden fieldにもreadonly属性が入ります。
スコア未設定のままreadOnlyにすると、全ての星のtooltipがNot rated yet!になります。日本語サイトではnoRatedMsgで差し替えてください。
new Raty(document.querySelector('[data-raty]'), {
readOnly: true,
noRatedMsg: 'まだ評価がありません',
score: 4
}).init();
半星表示と丸めルール(half・halfShow・round)
平均評価のような小数値を扱うときは、表示用のhalfShowと投票用のhalfを混同しないでください。halfShowは既定で有効で、小数スコアを半星のアイコンとして描くだけの機能です。ユーザーに半星単位で投票させるならhalf: trueを別途指定します。
丸めの境界はroundで、既定値は{ down: 0.25, full: 0.6, up: 0.76 }です。公式tutorialの規定では、halfShowが有効なとき小数部が0.25以下で切り捨て、0.25超かつ0.76未満で半星、0.76以上で切り上げになります。halfShowを無効にするとfull(0.6)だけが参照され、0.6未満は切り捨て、0.6以上は切り上げの2値判定に変わります。
丸められるのはアイコンの見た目だけです。hidden fieldに入る値は小数のままで、{ half: true, score: 2.5 }ならvalue="2.5"が送信されます。平均値を保存している場合でも表示と保存値がずれません。
インスタンス経由で呼ぶ5つの関数
v4では関数をインスタンスから呼びます。変数に受けておかないと後から操作できないので、動的に触る予定があるなら必ず保持してください。
const raty = new Raty(document.querySelector('[data-raty]'), {});
raty.init();
raty.score(2); // スコアを設定
raty.score(); // 現在のスコアを取得(2が返る)
raty.click(4); // 4番目の星をクリックしたことにする
raty.move(3.5); // 3.5の位置へマウス移動を再現
raty.cancel(true); // 評価を取り消す(hiddenのvalueが消える)
raty.readOnly(true); // 読み取り専用に切り替える
並び順には理由があります。cancel()の実装は_isReadOnly()が偽のときだけ本体を実行するため、readOnly(true)のあとにcancel()を呼んでも例外を出さずに素通りします。読み取り専用への切り替えは最後に置いてください。
Rails 8での評価データ保存と読み取り専用表示
SERP上位9本のうち5本がRailsのレビュー機能実装記事で、日本語圏の「raty」需要はここに集中しています。Rails 8.1.3.1(2026-07-30リリース)を前提に、保存から一覧表示までを組み立てます。
半星を保存するdecimalカラムと範囲バリデーション
半星投票を許すなら整数カラムでは足りません。ジェネレータの属性オプションで精度まで指定できます。
rails generate migration AddRatingToReviews rating:decimal{3,1}
この記法はprecision: 3, scale: 1に展開されます。波括弧を省いたrating:decimalだけでは精度指定が付かないので、後からマイグレーションを手で編集することになります。モデル側では範囲を縛ります。
class Review < ApplicationRecord
validates :rating,
presence: true,
numericality: { greater_than: 0, less_than_or_equal_to: 5 }
end
星の数をnumberオプションで5から変えた場合、この上限値も合わせて変更してください。フロントの星の数とサーバーの許容範囲がずれると、UIでは選べないのにバリデーションだけ通る状態が生まれます。
フォーム側の記述とStrong Parameters
ビューではtag.divヘルパーでdata属性を組み立てます。@review.ratingがnilのときRailsは該当のdata属性ごと出力しないため、未評価と評価済みで分岐を書く必要がなくなります。
<%= form_with model: @review do |f| %>
<%= tag.div(data: { raty: true, score_name: "review[rating]", score: @review.rating&.to_f }) %>
<%= f.submit "評価を送信" %>
<% end %>
to_fを挟んでいるのは、decimalカラムの値がBigDecimalで返るためです。素のまま渡すとdata-score="0.25e1"という指数表記がHTMLに出ます。Raty側は正しく2.5として解釈しますが、ソースを読んだ人が驚くだけなので浮動小数に落としておきます。
data属性はキャメルケースに変換されて読み込まれるため、score_nameはscoreNameオプションとして解釈されます。コントローラはRails 8で追加されたparams.expectで受けます。
def create
@review = @product.reviews.build(review_params)
if @review.save
redirect_to @product, notice: "評価を保存しました"
else
render :new, status: :unprocessable_content
end
end
private
def review_params
params.expect(review: [:rating, :body])
end
ステータスシンボルに:unprocessable_contentを使っている点に注意してください。Rack 3.1以降で:unprocessable_entityは非推奨扱いになり、Rails 8.1のscaffoldもActionDispatch::Constants::UNPROCESSABLE_CONTENT経由で新しい方を生成します。
平均評価の算出と一覧での読み取り専用表示
商品一覧に平均点を並べるのが、レビュー機能で最初に求められる表示です。平均はBigDecimalで返るので、丸めてから渡します。
<% @products.each do |product| %>
<% average = product.reviews.average(:rating)&.round(1) %>
<%= tag.div(data: { raty: true, read_only: true,
score: average&.to_f,
path: "/raty" }) %>
<%= average ? "#{average} (#{product.reviews_count}件)" : "評価はまだありません" %>
<% end %>
平均がnilのときはdata-scoreが出力されず、noRatedMsgの状態になります。件数を毎回COUNTで数えると一覧ページで効いてくるので、reviews_countのようなカウンタを持たせてください。持ち方と後付けの手順はcounter_cacheとは?Railsでの実装手順とカウントずれ・後付けの対処にまとめています。
初期化は1本のスクリプトで全要素をまとめて処理できます。
document.querySelectorAll('[data-raty]').forEach((el) => {
new Raty(el, {}).init();
});
Propshaft環境でのアセット配置
Rails 8のアプリケーションジェネレータは、既定でpropshaftとimportmap-railsをGemfileに書き込みます。Sprockets前提の古い記事どおりapp/assets/javascriptsへ置いても読まれないので、配置先を先に確認してください。
星のPNGをアセットパイプラインに載せる場合、Propshaftはファイル名にダイジェストを付けて配信します。/assets/raty/star-on-a1b2c3d4.pngのような形になるため、pathにディレクトリ名を書く方法は開発環境でも本番でも404になります。ディレクトリではなくアイコンごとに解決済みのURLを渡し、pathは空文字で無効化します。
<%= tag.div(data: { raty: true, path: "",
star_on: asset_path("raty/star-on.png"),
star_off: asset_path("raty/star-off.png") }) %>
設定が細かくなるのを避けたいなら、星のPNGだけpublic/raty/に置いてpath: "/raty"で参照する割り切りもあります。ダイジェストが付かないぶんキャッシュ制御は弱くなりますが、5枚の静的画像なら実害は小さいと考えます。raty.cssをパイプラインに載せる場合は、Propshaftがurl()を書き換える対象にfonts/が入っているかも確認してください。
JavaScript側はsrc/raty.jsがES Modulesなので、vendor/javascript/に置いてimportmapにpinできます。アセットパイプラインのコマンドと役割分担はrails assets:clobberとは?precompile・cleanとの違いとアセットパイプライン運用【Rails 8 Propshaft対応】で整理しています。
再評価の許可と、他人の評価を書き換えさせない制御
自分の評価だけ編集可能にする切り替えは、ビュー側の分岐で表現できます。
<%= tag.div(data: { raty: true,
score: @review.rating&.to_f,
read_only: @review.user_id != current_user&.id }) %>
編集フォームから同じscoreNameで送れば、updateアクションで上書きされます。再投稿を1人1件に保ちたい場合は、validates :user_id, uniqueness: { scope: :product_id }で重複を止めるのが確実です。
ただしreadOnlyはブラウザのUI操作を止めるだけで、hidden fieldにreadonly属性が付くにすぎません。リクエストを直接組み立てれば任意の値を送れるので、更新可否の検証はコントローラに必ず置いてください。認証基盤から用意する場合の選択肢はSorcery gemでRailsに認証機能を実装する手順|Deviseとの違い・最新0.18.0対応で比較しています。
星が出ないときの原因切り分け
ここまでの内容を踏まえると、トラブルの原因はほぼ5つに絞り込めます。
症状とエラーメッセージから見る5つの原因
| 症状 | 原因 | 対処 |
|---|---|---|
$(...).raty is not a function |
v4に旧記法 | new Raty() へ書き換え |
window.Raty is not a constructor |
window.Raty を参照 |
素の Raty を使う |
Raty is not defined |
UMD版を読み込み | window.raty.default を使う |
| divが空・エラーなし | init() の呼び忘れ |
生成直後に1行追加 |
| 星の枠だけ出て画像が壊れる | path 未指定 |
生成された img の src を確認 |
切り分けの順番は、まずコンソールを見ることです。何も出ていなければinit()とpath、エラーが出ていれば旧記法かグローバル参照の問題と判断できます。
JSのオプションをdata属性が上書きする優先順位
これは公式tutorialに「Data Support」として書かれた正式な機能ですが、優先順位を知らないと原因不明のバグになります。src/raty.jsのコンストラクタは、既定値・引数のオプション・要素のdatasetの順にオブジェクトを展開します。最後に展開されるdatasetが勝つ構造です。
<div data-raty data-score="5">という要素に対して{ score: 1 }を渡すと、適用されるのは5になります。サーバー側でレンダリングしたdata属性が、JS側の共通設定を静かに上書きする事故が起こります。テンプレートで一括初期化している場合は、data属性の出力内容を先に確認してください。
Ratyを採用すべきでない3つの条件
Ratyは依存が無く、hidden fieldの生成まで面倒を見てくれます。それでも次に当てはまるなら別の手段を選ぶべきです。
キーボード操作とスクリーンリーダーへの対応が要件のフォーム
src/raty.jsにはrole・aria-*・tabindex・keydownのいずれも実装されていません。登録しているリスナーはclick・mouseover・mouseleaveだけです。生成されるのはimgまたはi要素の並びなので、キーボードだけで評価を選ぶ操作ができません。フォームコントロールとしての要件を満たすには自前で補うことになり、それならネイティブのinput type="radio"をCSSで星に見せる実装の方が、追加するコードは少なく済みます。
集計・重複防止・多次元評価までRails側で必要な場合
Ratyの作者自身がREADMEの冒頭で「Rating for Rails? It’s Rating」と別gemを案内しています。rating gemは2.1.0が2026-06-30に公開されており、こちらはスコアの集計やスコープ付きの評価をモデル層で扱います。表示だけをRatyに任せ、データモデルはgemに寄せる分担が作者の想定です。
ベンダーの継続更新を前提にした長期保守
4.3.0のリリースは2024-04-28で、リポジトリへの最終pushも同日です。2026-08-05時点で2年以上リリースがなく、オープンなissueは52件、pull requestは7件が残っています。アーカイブはされておらずMITライセンスでコードも小さいため自前保守は現実的ですが、更新が続く前提での選定はできません。新規プロジェクトでこれを許容できるかは、着手前に決めておくべきです。
よくある質問
星の数を5個から増やしたいのですが上限はありますか?
numberオプションで変更でき、上限はnumberMaxの既定値である20個です。{ number: 10 }なら10個の星が並びます。20を超える数を指定した場合はnumberMaxの値で頭打ちになるため、それ以上必要ならnumberMaxも同時に引き上げてください。サーバー側のバリデーション上限も忘れずに合わせます。
星の色や大きさは変えられますか?
starType: 'i'を指定してフォントアイコン版にすると、star-on-pngなどのクラスに対してCSSでcolorとfont-sizeを当てられます。src/raty.cssの既定はfont-size: 2emです。画像版のまま色を変えたい場合は、starOn・starOff・starHalfに自作PNGのファイル名を渡す方法になります。
古いjQuery記法のまま動かし続けることはできますか?
できます。$.fn.ratyを持つ最後のバージョンはraty-js 3.1.1(2021-09-30公開)で、mainがlib/jquery.raty.js、依存にjquery ^3.6が入っています。npm install [email protected]で固定すれば既存コードは動きます。ただし2021年で更新が止まっている系列なので、当面の延命策と考えてください。
Ratyで選んだ評価をAjaxで即時保存できますか?
clickコールバックでスコアを受け取って送信します。v4では引数が(score, element, evt)の3つに変わっている点に注意してください。thisはRatyインスタンスを指します。コールバック内でfalseを返すとクリック自体が取り消され、スコアは変化しません。バリデーションエラー時に選択を戻す用途に使えます。
読み取り専用にすれば評価を改ざんされませんか?
いいえ、防げません。readOnly: trueが行うのは、要素にpointer-events: noneを当て、hidden fieldにreadonly属性を付けることだけです。ブラウザのUI操作を止めるための機能であり、リクエストを直接組み立てれば任意の値を送れます。評価の更新可否はコントローラ側で必ず検証してください。