- 技術 💻
WOW.jsの使い方|animate.css 4で動かない原因と代替コードを実測
結論
WOW.jsは、要素に class="wow fadeInUp" を付け、animate.css と wow.min.js を読み込んで new WOW().init() を呼ぶだけで、スクロールして画面に入った要素をアニメーションさせるライブラリです。animate.css 4 をそのまま読み込むと「表示はされるが動かない」ので、animate.compat.css を使うか、animateClass: 'animate__animated' を指定します。
この記事でわかること
- CDN・npm・WordPress での入れ方と、data-wow-delay など4つの属性・7つのオプション
- Chromium で測った表示のタイミング・遅れ・待機中の負荷と、動かない原因12個
- Intersection Observer と CSS だけで同じことをする代替コード(445バイト)と判断表
WOW.jsが中でしていることは単純です。読み込み時に対象を隠し、50ミリ秒ごとに「画面に入ったか」を確かめ、入った要素に animate.css のクラスを付けて動かします。
flowchart TD
A[init 呼び出し] --> B[.wow の要素を集める]
B --> C[visibility:hidden と<br>animation-name:none で隠す]
C --> D{50ms ごとに確認<br>画面に入った?}
D -->|まだ| D
D -->|入った| E[animated クラスを付け<br>animation-name を戻す]
E --> F[animate.css の<br>アニメーションが動く]
F --> G[animationend で<br>animated を外す]
よく調べられることの答えを先にまとめます。
| やりたいこと | いちばん早い方法 | 詳しい節 |
|---|---|---|
| WOW.jsの使い方を知りたい | animate.css と wow.min.js を読み込み、class="wow fadeInUp"、最後に new WOW().init() |
WOW.jsの使い方(最短の3ステップ) |
| CDNで読み込みたい | jsDelivr の wow.js@1.2.2/dist/wow.min.js か cdnjs の wow/1.1.2 |
WOW.jsの使い方(最短の3ステップ) |
| 表示はされるが動かない | animate.css 4 の接頭辞が原因。animate.compat.css に替える |
animate.css 4と組み合わせるときの注意 |
| 遅らせる・ゆっくりにする | data-wow-delay="0.5s"・data-wow-duration="2s" |
data-wow-delayなどの属性一覧 |
| もっと早く・遅く始めたい | data-wow-offset="200"(画面の下から200px入ったら始まる) |
data-wow-delayなどの属性一覧 |
| スマホでは動かしたくない | new WOW({ mobile: false }) |
new WOW()のオプション一覧 |
| React・Next.jsで「window is not defined」 | useEffect の中で動的に import する | WordPress・React・Next.jsでWOW.jsを使う |
| 動きを減らす設定の人に配慮したい | prefers-reduced-motion のときは init を呼ばない |
prefers-reduced-motionへの配慮 |
| ライブラリなしで同じことをしたい | Intersection Observer でクラスを付け、CSS の transition で動かす | Intersection ObserverとCSSで同じことをする代替コード |
確認環境:Ubuntu 24.04.5 LTS の上で、npm の wow.js 1.2.2(Thomas Grainger 氏の MIT 版)・wowjs 1.1.3(Matthieu Aussaguel 氏の元の版)・animate.css 4.1.1 を作業用のフォルダーに入れ、Python 3.13 の http.server を 127.0.0.1 で起動して、自作の見本ページ(高さ200pxの箱を8個並べたもの)を Chromium 153.0.8010.12(Playwright 1.63.0・Node.js 22.23.4)で開きました。ホイールのスクロールを送り、要素が画面に入った時刻とクラスが付いた時刻の差、待機中の処理時間(DevTools Protocol の Performance.getMetrics)、印刷と全画面スクリーンショットでの見え方、iPhone 13 のエミュレーション、動きを減らす設定を測っています。メンテナンス状況とライセンスは GitHub の README・npm の登録情報・cdnjs、animate.css の使い方は公式サイト animate.style、CSS の機能は MDN で確かめました。WordPress の管理画面と実機の iPhone・Android は動かしておらず、公式の情報の範囲で書いています。図は mermaid-cli 11.16.0 で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「WOW.jsの使い方|animate.css 4で動かない原因と代替コードを実測」
WOW.jsとは(スクロールで要素をアニメーションさせる仕組み)
この節では、WOW.jsが何をするライブラリで、animate.css とどう役割を分けているかが分かります。
WOW.jsは「いつ動かすか」だけを受け持つ小さな JavaScript ライブラリで、「どう動かすか」は animate.css などの CSS が受け持ちます。 README には「既定では animate.css のアニメーションを発火させるために使える」と書かれています。jQuery は使いません(この記事の見本ページはすべて jQuery なしで動いています)。
WOW.js が受け持つこと
最初に対象の要素を隠す(visibility:hidden)
スクロールのたびに画面に入ったかを判定する
入った要素に animated クラスを付ける
遅延・時間・回数を style 属性に書き込む
animate.css が受け持つこと
fadeInUp などの @keyframes(97個)
.animated の再生時間(既定 1秒)と fill-mode
動きを減らす設定のときに 1ms にする
印刷時の扱い
見本ページで、読み込み直後と画面に入ったあとの要素の状態を Chromium で読み取ると、次のようになりました。WOW.js は CSS のファイルではなく、要素の style 属性を書き換えて隠していることが分かります。
読み込み直後(画面の外)
class = "box wow fadeInUp"
style = "visibility: hidden; animation-name: none;"
画面に入った直後
class = "box wow fadeInUp animated"
style = "visibility: visible; animation-name: fadeInUp;"
アニメーション終了後(animationend)
class = "box wow fadeInUp" ← animated は外れる
style = "visibility: visible; animation-name: fadeInUp;"
画面に入った要素の不透明度(opacity)を100ミリ秒ごとに読むと 0.03 → 0.19 → 0.44 → 0.63 → 0.77 → 0.86 → 0.93 → 0.97 → 0.99 → 1.00 と変わり、animate.css の既定どおり約1秒でフェードインしていました。CSS のアニメーションそのものの書き方は、関連記事「CSS アニメーション|transitionとkeyframesの違いと実用20選」で扱っています。
WOW.jsの使い方(最短の3ステップ)
この節では、静的な HTML に WOW.js を入れて動かすまでの手順と、CDN・npm・ダウンロードのどれで入れるかが分かります。
animate.css と wow.min.js を読み込む
CSS は head に、JavaScript は body の最後(head でも動く)に置く。animate.css 4 なら接頭辞なしで使える animate.compat.css を選ぶと、WOW.js の既定の設定のまま動く。
動かしたい要素に wow とアニメーション名を付ける
class="wow fadeInUp" のように、wow(目印)と animate.css のアニメーション名を並べる。遅延などは data-wow-* 属性で要素ごとに指定する。
new WOW().init() を呼ぶ
wow.min.js を読み込んだあとに書く。順番が逆だと WOW is not defined で止まる。init は DOMContentLoaded を待つので、head に置いても動いた。
この形で、見本ページの8個の箱がスクロールに合わせて順にフェードインしました。そのまま使える最小のページです(ファイルは npm で入れたものを lib/ に置いています)。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="lib/animate.compat.css">
</head>
<body>
<section class="wow fadeInUp">ふわっと下から</section>
<section class="wow fadeInLeft" data-wow-delay="0.3s">0.3秒遅れて左から</section>
<section class="wow bounceIn" data-wow-duration="2s">2秒かけて弾む</section>
<script src="lib/wow.min.js"></script>
<script>
new WOW().init();
</script>
</body>
</html>
入れ方は3通りあり、どれも中身は同じファイルです。2026年10月29日時点の版と、手元で確かめたファイルの場所は次のとおりです。
| 入れ方 | 読み込むファイル | ライセンス | 向いている場面 |
|---|---|---|---|
| npm(MIT 版) | npm i wow.js → node_modules/wow.js/dist/wow.min.js(1.2.2) |
MIT | Vite・webpack などでまとめる |
| npm(元の版) | npm i wowjs → node_modules/wowjs/dist/wow.min.js(1.1.3) |
GPLv3/商用ライセンス | GPLv3 互換のオープンソース |
| CDN(jsDelivr) | jsDelivr の npm 配信で wow.js@1.2.2/dist/wow.min.js |
MIT | 静的な HTML・試作 |
| CDN(cdnjs) | cdnjs の ajax/libs/wow/1.1.2/wow.min.js |
GPL-3.0 と表示 | 以前の記事どおりに入れる |
| ダウンロード | GitHub のリポジトリから dist/wow.min.js を取り出す | 取り出したリポジトリによる | CDN を使えないサイト |
jsDelivr から読み込むときは、手元の npm のファイルから計算した SRI(改ざんを検知する integrity 属性)を付けられます。jsDelivr の /npm/ の配信は npm に登録されたファイルをそのまま返すため、次の値になります。
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/animate.css@4.1.1/animate.min.css"
integrity="sha384-Gu3KVV2H9d+yA4QDpVB7VcOyhJlAVrcXd0thEjr4KznfaFPLe0xQJyonVxONa4ZC"
crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/wow.js@1.2.2/dist/wow.min.js"
integrity="sha384-uDKW6sIHqqcPiyccJrLMW2OiOB7L186E5UweSqFyDcqS9Q6SiT9nHw+XyWBAu3ZV"
crossorigin="anonymous"></script>
この例の animate.min.css は接頭辞付きの版なので、次の節のとおり animateClass の指定と組み合わせます。接頭辞なしで使うなら、同じ場所の animate.compat.css を読み込みます(SRI の値はファイルごとに変わります)。
使えるアニメーション名は animate.css の @keyframes で、4.1.1 には97個あります。よく使うものを系統別にまとめます。
| 系統 | 主なクラス名(compat 版) | 動き |
|---|---|---|
| フェード | fadeIn・fadeInUp・fadeInDown・fadeInLeft・fadeInRight・fadeInUpBig | 透明から現れる(方向つき) |
| スライド | slideInUp・slideInDown・slideInLeft・slideInRight | 画面の外から滑り込む |
| ズーム | zoomIn・zoomInUp・zoomInLeft | 小さい状態から拡大して現れる |
| バウンス | bounceIn・bounceInUp・bounceInLeft | 弾みながら現れる |
| 回転・反転 | rotateIn・rotateInUpLeft・flipInX・flipInY | 回りながら・裏返りながら現れる |
| 注意を引く | bounce・pulse・shake・swing・tada・heartBeat・rubberBand | その場で動く(現れる動きではない) |
| その他 | lightSpeedInRight・jackInTheBox・rollIn・backInUp | 個性の強い登場 |
animate.css 4と組み合わせるときの注意(動かない最大の原因)
この節では、WOW.js の記事どおりに書いたのに「表示されるが動かない」原因と、2つの直し方が分かります。
animate.css は 4.0 からクラス名に animate__ の接頭辞が付きました。 WOW.js は2016年の版のまま、接頭辞のない animated クラスと fadeInUp のような名前を前提にしています。animate.css の公式サイトは、接頭辞なしで使う場合は animate.compat.css を読み込むよう案内しています。
3通りの組み合わせを同じページで比べました。要素が画面に入ってから100ミリ秒ごとの opacity です。
| 組み合わせ | class | 計算された animation-name | opacity の変化 | 結果 |
|---|---|---|---|---|
| animate.min.css+既定の WOW | wow fadeInUp | none | 1.00 のまま | 急に現れるだけ |
| animate.compat.css+既定の WOW | wow fadeInUp | fadeInUp | 0.03→0.44→0.77→0.93→1.00 | 動く |
| animate.min.css+animateClass 指定 | wow animate__fadeInUp | fadeInUp | 0.03→0.44→0.77→0.92→1.00 | 動く |
1行目では、WOW.js は animated を付けて要素を見えるようにしていますが、animate.min.css には接頭辞のない .fadeInUp の規則が無いため、アニメーション名が none のままでした。エラーは何も出ないので、気づきにくいつまずきです。
animate.css 4 の読み込み方
class="wow fadeInUp" と書く。表示されるだけで動かないanimateClass: 'animate__animated' にして animate__fadeInUp と書く見分け方:DevTools で要素の animation-name が none なら接頭辞の食い違い
接頭辞付きのまま使うときの書き方です。animate__delay-2s などの animate.css 側のクラスも、この形なら一緒に使えます。
<link rel="stylesheet" href="lib/animate.min.css">
<section class="wow animate__fadeInUp">接頭辞付きで書く</section>
<script src="lib/wow.min.js"></script>
<script>
new WOW({ animateClass: 'animate__animated' }).init();
</script>
animate.css 4 の再生時間は CSS 変数 --animate-duration(既定 1s)でまとめて変えられます。WOW.js の data-wow-duration は要素の style 属性に直接書くので、両方を指定すると要素ごとの data-wow-duration が優先されます。
data-wow-delayなどの属性一覧(4つの属性の実測)
この節では、要素ごとに付ける data-wow-* の4つの属性の意味と、Chromium で計算された値が分かります。
属性は4つだけで、どれも CSS の animation 系のプロパティに置き換わります(offset だけは WOW.js 自身の判定に使います)。 単位は s と ms のどちらでも書けました。
| 属性 | 意味 | 書いた値 | 計算された値(Chromium) |
|---|---|---|---|
| data-wow-duration | アニメーションの長さ | 2s/1500ms | animation-duration: 2s/1.5s |
| data-wow-delay | 画面に入ってから動き出すまでの待ち | 0.5s/200ms | animation-delay: 0.5s/0.2s |
| data-wow-iteration | 繰り返す回数 | 3/infinite | animation-iteration-count: 3/infinite |
| data-wow-offset | 画面の下端から何px入ったら始めるか | 200 | 下端から244px入った時点で開始(既定は4px) |
offset の行は、60px ずつホイールを送って測った値です。既定(offset 0)の要素は下端から4px入ったところでクラスが付き、data-wow-offset="200" の要素は244px入ったところで付きました。WOW.js の判定は「要素の上端 ≤ スクロール位置+画面の高さ−offset」なので、offset は「画面の下から何px上がったら始めるか」と考えると合います。差の44pxは、スクロールが60px刻みであることと50ミリ秒ごとの確認による遅れです。
<section class="wow fadeInUp" data-wow-delay="0.5s">0.5秒待ってから</section>
<section class="wow fadeInUp" data-wow-duration="2s">2秒かけて</section>
<section class="wow fadeInUp" data-wow-iteration="3">3回くり返す</section>
<section class="wow fadeInUp" data-wow-offset="200">下から200px入ったら</section>
<section class="wow bounceInLeft"
data-wow-duration="1500ms" data-wow-delay="200ms"
data-wow-iteration="infinite">組み合わせ</section>
読み込み直後の style 属性には、すでに値が入っていました(隠している間も設定は書き込まれる)。
visibility: hidden; animation-duration: 1500ms; animation-delay: 200ms;
animation-iteration-count: infinite; animation-name: none;
WOW.js は要素の位置を offsetTop でたどって毎回計算するので、要素が多いページでは判定の時間が増えます。操作への反応の速さ(INP)が気になるページは、関連記事「INP改善の方法|Core Web Vitalsの原因別コードと改善前後の実測値」で扱っています。
new WOW()のオプション一覧(offset・mobile・live・callback)
この節では、new WOW({...}) に渡す7つのオプションの既定値と、実際に変えたときの動きが分かります。
1.2.2 の dist/wow.js に書かれている既定値は7つです。 どれも README と一致しました。
| オプション | 既定値 | 意味 | 確かめた結果 |
|---|---|---|---|
| boxClass | 'wow' | 対象を見分けるクラス名 | 'reveal-on-scroll' に変えても動いた |
| animateClass | 'animated' | 画面に入ったときに付けるクラス | animate.css 4 は 'animate__animated' |
| offset | 0 | すべての要素の既定の offset(px) | 要素の data-wow-offset が優先 |
| mobile | true | false にすると携帯端末では動かさない | iPhone 13 のエミュレーションで、全要素が最初から表示された |
| live | true | あとから追加された要素も対象にする | 300ms 後に追加した要素もフェードインした |
| callback | null | 要素が動き出すたびに呼ぶ関数 | 6要素すべてで、表示順に呼ばれた |
| scrollContainer | null | window 以外のスクロール領域の CSS セレクター | 位置によっては判定がずれた(下の注意) |
const wow = new WOW({
boxClass: 'reveal-on-scroll', // class="reveal-on-scroll fadeInUp" を対象にする
animateClass: 'animated',
offset: 100, // 全体で、下から100px入ったら
mobile: false, // 携帯端末では動かさない(最初から表示)
live: true,
callback(box) {
console.log('表示:', box.id); // 動き出した要素
},
});
wow.init();
// 要素ごとに「wow」という名前のイベントも飛ぶ(boxClass と同じ名前)
document.addEventListener('wow', (e) => console.log('event:', e.target.id), true);
mobile: false の判定は User-Agent の文字列(iPhone・Android など)で行います。iPhone 13 のエミュレーションでは、mobile: false のページは読み込み直後から全要素が visibility: visible で、アニメーションは付きませんでした。既定(true)のページは、パソコンと同じく画面の外の要素が隠れていました。画面の幅ではなく端末の種類で分けている点に注意します。
scrollContainer には注意が要ります。 高さ600pxのスクロール領域の中に箱を置いて試すと、領域がページの一番上にあるときは、箱がまだ領域の下端より119px下(領域の上端から719px)にある時点で表示されました。判定に領域ではなく画面全体の高さを使うためです。領域の上にほかの内容(高さ1,200px)があるページでは、箱が領域の中で見えても隠れたままでした。要素の位置をページの上端から数え、領域のスクロール量と比べているためです。スクロール領域の中で動かしたいときは、後半の Intersection Observer の書き方(root に領域を指定できる)が確実です。
Ajax で追加した要素は、live: true(既定)なら MutationObserver が拾うので、sync() を呼ばなくても動きました。README は、MutationObserver の無い以前のブラウザ向けに wow.sync() を案内しています。
スクロールで表示されるタイミングの実測(WOW.jsとIntersection Observer)
この節では、要素が画面に入ってから WOW.js が動かし始めるまでの遅れと、スクロールしていない間の負荷を、代替の方法と比べた数値で分かります。
見本ページで、マウスホイールを100pxずつ60ミリ秒おきに40回送り、8個の箱それぞれについて「画面に入った時刻」と「クラスが付いた時刻」の差を測りました。そのあと10秒間スクロールせずに置き、その間の処理を数えています。
| 方法 | 入ってから動き出すまで(8要素) | 10秒の待機中のタイマー呼び出し | 待機中のスクリプト時間 | 待機中のタスク時間 |
|---|---|---|---|---|
| WOW.js 1.2.2 | 7.6〜26.6ms(中央値 16.3ms) | 200回 | 2.66ms | 14.04ms |
| Intersection Observer | 0〜0.4ms | 0回 | 0ms | 1.78ms |
| CSS のスクロール駆動アニメーション | (スクロール位置に連動) | 0回 | 0ms | 1.09ms |
1遅れは最大で約27ms
50ミリ秒ごとの確認なので、運が悪いと1フレーム半ほど遅れる。目で見て分かる差ではないが、速いスクロールでは「入ってから現れる」感じが出る。
2止まっていても20回/秒
live: true の既定では、全要素を表示し終えたあともタイマーが止まらない。10秒で200回呼ばれた。
3負荷そのものは小さい
10秒で 2.66ms のスクリプト時間は、1回あたり約0.013ms。要素数が数百になるまでは体感に影響しにくい。
CSS のスクロール駆動アニメーション(animation-timeline: view())は、時間ではなくスクロール位置で進みます。箱(高さ200px)が画面の下端から入った量ごとの opacity は、0px で 0.2、50px で 0.41、100px で 0.62、150px で 0.825、200px で 1 でした。スクロールを止めると途中の状態で止まる点が、WOW.js と違います。
WordPress・React・Next.jsでWOW.jsを使う
この節では、WordPress のテーマ、Vite などの npm の環境、React・Next.js で WOW.js を読み込むときの書き方と、出たエラーが分かります。
WOW.js はブラウザの window を読み込んだ瞬間に使うので、サーバー側で動く処理(Node.js・Next.js の SSR)から読み込むとエラーになります。 Node.js で require しただけで、次のエラーになりました。
$ node -e "require('wow.js')"
ReferenceError: window is not defined
$ node -e "require('wowjs')"
/…/node_modules/wowjs/dist/wow.js:336
if ('requestAnimationFrame' in window) {
^
ReferenceError: window is not defined
環境ごとの読み込み方をまとめます。WordPress の関数は WordPress の開発者向けドキュメントにある wp_enqueue_style・wp_enqueue_script・wp_add_inline_script の使い方です(管理画面での操作は試していません)。
| 環境 | 読み込み方 | つまずきやすい点 |
|---|---|---|
| 静的な HTML | link と script を書き、最後に new WOW().init() | script の順番が逆だと WOW is not defined |
| WordPress(テーマ) | functions.php で wp_enqueue_style・wp_enqueue_script、init は wp_add_inline_script | テーマや別のプラグインが別の版の animate.css を読んでいる |
| Vite・webpack | import WOW from 'wow.js'; new WOW().init(); | CSS(animate.css)の import を忘れる |
| React(クライアントだけ) | useEffect の中で init | 画面遷移のたびに init が増える |
| Next.js(App Router) | 'use client' の部品の useEffect で import('wow.js') | 上の階層で import すると window is not defined |
<?php
// functions.php(テーマ)
add_action('wp_enqueue_scripts', function () {
$dir = get_stylesheet_directory_uri();
wp_enqueue_style('animate', $dir . '/lib/animate.compat.css', [], '4.1.1');
wp_enqueue_script('wow', $dir . '/lib/wow.min.js', [], '1.2.2', true); // true = body の最後
wp_add_inline_script('wow', 'new WOW().init();'); // wow.min.js の後に出力される
});
// app/components/WowInit.jsx(Next.js の App Router)
'use client';
import { useEffect } from 'react';
import 'animate.css/animate.compat.css';
export default function WowInit() {
useEffect(() => {
let wow;
import('wow.js').then(({ default: WOW }) => {
wow = new WOW({ live: true });
wow.init();
});
return () => wow && wow.stop && wow.stop(); // 画面を離れたら止める
}, []);
return null;
}
WOW.js 1.2.2 には stop() があり、scroll・resize のイベントと50ミリ秒のタイマーを外します。React で部品が作り直されるたびに init すると、タイマーが重なっていきます。React のための仕組みはなく、要素の追加は MutationObserver で拾う作りなので、React の画面では後半の Intersection Observer の書き方を部品として持つほうが扱いやすくなります。jQuery は使いません。jQuery 自体の使い方は、関連記事「jQueryとは|使い方・Ajax・fetchとの対応表を4.0の実行結果で解説」で扱っています。
WOW.jsが動かない・表示されないときの原因と直し方
この節では、WOW.js がうまく動かないときに、症状から原因を絞り込み、直す方法が分かります。
最初に DevTools(F12)で対象の要素を選び、class と style 属性を見ると、ほとんどの原因が分かります。 次の表は、見本ページでわざと起こして確かめたものです。
| 症状 | 原因 | 直し方 |
|---|---|---|
| 表示されるが動かない(急に現れる) | animate.css 4 を接頭辞付きのまま読んでいる | animate.compat.css に替える/animateClass を指定 |
| 何も起きず、普通に表示される | new WOW().init() を呼んでいない(style 属性が付かない) | init を呼ぶ |
コンソールに Uncaught ReferenceError: WOW is not defined |
wow.min.js より前に init を書いた・パス違い | script の順番とファイルのパスを確かめる |
| ずっと隠れたまま(visibility: hidden) | scrollContainer の領域がページの途中にある | 領域を使わないか、Intersection Observer に替える |
| スマホだけ動かない | mobile: false を指定している | mobile: true(既定)に戻す |
Next.js で ReferenceError: window is not defined |
サーバー側で import した | 'use client' の部品の useEffect で import |
| 印刷・PDF で要素が消える | 画面に入っていない要素が hidden のまま | 印刷用の CSS で visible にする(次の次の節) |
| 1回しか動かない | WOW.js は表示した要素を対象から外す作り | くり返すなら Intersection Observer で外す・付けるを書く |
| 思ったより早く・遅く始まる | offset の向きの思い違い | offset は「画面の下から何px入ったら」 |
| アニメーションの途中で横スクロールが出る | fadeInRight などが画面の外から入る(幅1280pxで横に500pxスクロールできた) | 箱を包む main などに overflow-x: clip(body に付けても止まらなかった) |
| 動きを減らす設定でも隠れる | WOW.js は prefers-reduced-motion を見ない | 設定を見て init を呼ばない |
| GPL のライセンスが気になる | cdnjs の 1.1.2・npm の wowjs は元の版 | MIT 版(npm の wow.js)か代替コードにする |
読み込み順を逆にしたページで出たエラーの原文です。この場合、init が動かないので要素は隠されず、アニメーションなしで表示されていました。
ReferenceError: WOW is not defined
at http://127.0.0.1:18162/order-err.html:6:30
JavaScript が無効なブラウザ(Chromium の設定で無効にして確認)では、WOW.js が動かないので、要素は最初から表示されました。WOW.js は CSS ではなく JavaScript で隠すため、読み込みに失敗しても本文が見えなくなることはありません。この性質は代替のコードを書くときにも守りたい点です。
prefers-reduced-motionへの配慮(動きを減らす設定)
この節では、OS の「視差効果を減らす」「アニメーションを減らす」設定をしている人に対して、WOW.js と animate.css がどう動き、何を足すとよいかが分かります。
animate.css 4 は動きを減らす設定のときにアニメーションを 1ms にしますが、WOW.js 自体はこの設定を見ません。 そのため「スクロールするまで隠れ、入った瞬間に現れる」動きは残ります。animate.css の公式サイトは、この対応を重要なアクセシビリティの機能として、無効にしないよう書いています。
| ページ | 設定なし:入ってから約200ms後の opacity | 動きを減らす設定:入る前 | 動きを減らす設定:入ってから120ms後 |
|---|---|---|---|
| WOW.js+animate.compat.css | 0.19(動いている) | hidden(隠れている) | 1(すぐ表示) |
| WOW.js+animateClass 指定 | 0.19 | hidden | 1 |
| WOW.js+設定を見て init しない | (init するので1行目と同じ) | visible(最初から表示) | visible |
| Intersection Observer+CSS(後半のコード) | 0(150ms)→0.22(300ms)。0.2秒の遅延つき | opacity 1(最初から表示) | 1 |
設定を見て init を呼ばない書き方です。init を呼ばなければ要素は隠されないので、Chromium の reducedMotion: reduce のエミュレーションで、最初から全要素が表示されました。
<script src="lib/wow.min.js"></script>
<script>
// 動きを減らす設定の人には、隠しも動かしもしない
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
new WOW().init();
}
</script>
- 1動きを減らす設定では隠さないmatchMedia で確かめて init を呼ばない。CSS の代替なら @media で opacity を1にする
- 2くり返し・無限の動きを本文に使わないdata-wow-iteration="infinite" は読む妨げになる。ボタンの目印など、ごく一部に限る
- 3大きく動く・回る・点滅する動きを避けるrotateIn・hinge・flash などは、画面酔いしやすい人の負担になる
- 4遅延は短くdata-wow-delay を長くすると、スクロールしても文字が出ず、壊れたページに見える
- 5JavaScript なしでも読めるCSS だけで最初から opacity: 0 にしない。読み込みに失敗すると本文が消える
ブラウザはこの設定を prefers-reduced-motion: reduce として受け取ります。OS・端末ごとの設定の場所は、MDN の「prefers-reduced-motion」に次のようにまとめられています(手元の Windows・Mac・スマホでは操作していません)。
| OS・端末 | 設定の場所(MDN の記載) |
|---|---|
| Windows 11 | 設定 > アクセシビリティ > 視覚効果 > アニメーション効果 |
| Windows 10 | 設定 > 簡単操作 > ディスプレイ > アニメーションを表示する |
| macOS 15 まで | システム設定 > アクセシビリティ > 表示 > 動きの抑制 |
| iPhone(iOS) | 設定 > 一般 > アクセシビリティ > 視覚効果を減らす |
| Android 9 以上 | 設定 > ユーザー補助 > アニメーションの削除 |
| Linux(GNOME) | Settings > Accessibility > Seeing > Reduced animation |
作る側が確かめるときは、OS の設定を変えなくても、Chrome の DevTools の「Rendering」パネルで prefers-reduced-motion を切り替えられます。この記事の測定は、Playwright の reducedMotion: reduce で同じ状態を作っています。
印刷・スクリーンショットで要素が消える問題
この節では、WOW.js を使ったページを印刷したり、全体のスクリーンショットを撮ったりしたときに起きることと、その直し方が分かります。
WOW.js は、まだスクロールしていない要素を visibility: hidden のままにするので、印刷や全画面のキャプチャには写りません。 見本ページを読み込んだ直後は8個中8個が隠れていて、次の結果になりました。
| 操作 | 結果 |
|---|---|
| 読み込み直後に隠れている要素 | 8個中8個(画面の外にあるため) |
| 全画面スクリーンショット(1280×4392px) | 箱の色(オレンジ)の画素が0行。箱の場所は白いまま |
| 全画面スクリーンショットのあと | 8個とも hidden のまま(撮るだけでは表示されない) |
| PDF に印刷(page.pdf) | 文字は「top」「end」だけ。box 0〜7 の文字が無い |
| 印刷用の CSS を足して PDF に印刷 | box 0〜7 の文字がすべて出た |
/* 印刷では WOW.js の隠しを打ち消す */
@media print {
.wow {
visibility: visible !important;
animation: none !important;
}
}
style 属性に書かれた値を打ち消すため、!important が要ります。全画面キャプチャの拡張機能などで撮る画像も同じ理由で抜けるので、最後までスクロールしてから撮ります。検索エンジンがこの隠れた要素をどう扱うかは、各社の公開情報に WOW.js についての記述が無いため、ここでは扱いません。
WOW.jsのメンテナンス状況とライセンス(2026年10月29日時点)
この節では、WOW.js が今も更新されているか、商用のサイトで使えるかを、公式の情報で確かめた結果が分かります。
WOW.js には2つの系統があり、どちらも2016年を最後に新しい版が出ていません。 npm の MIT 版(wow.js)には、登録情報に非推奨(deprecated)の印が付いていて、インストールすると次の警告が出ました。
$ npm i wow.js@1.2.2 wowjs@1.1.3 animate.css@4.1.1
npm warn deprecated wow.js@1.2.2: deprecated in favour of aos (Animate On Scroll)
added 3 packages in 2s
| 項目 | wow.js(npm) | wowjs(npm)・cdnjs の wow |
|---|---|---|
| 作者・リポジトリ | Thomas Grainger 氏の fork(graingert/WOW) | Matthieu Aussaguel 氏(matthieua/WOW) |
| 最新版と公開日 | 1.2.2(2016年9月5日) | npm 1.1.3(2016年5月6日)・cdnjs 1.1.2 |
| ライセンス | MIT(README に「100% MIT Licensed, not GPL」) | GPLv3 と有料の商用ライセンスの2本立て |
| 状態 | npm で非推奨。README は AOS への移行を案内 | README に非推奨の記載は無い。新しい版は無い |
| ファイルの中身 | UMD 形式(import できる)。先頭の表記は v1.2.1 | window.WOW に入れる形式 |
元の版の README には、商用のサイト・テーマ・製品を作る場合は商用ライセンスが適切で、GPLv3 互換のオープンソースなら GPLv3 で使える、と書かれています。cdnjs の wow/1.1.2 も GPL-3.0 と表示されています。以前の記事にある cdnjs の URL をそのまま使うと、こちらの系統になります。会社のサイトで使うなら、MIT 版の npm の wow.js を選ぶか、次の節の代替コードにするのが分かりやすい判断です(ライセンスの最終的な判断は、各社の法務の確認に従います)。
npm の登録情報では、移行先として案内されている AOS も最新版が 2.3.4 で、登録情報の最終更新は2022年6月です。「スクロールで現れる」だけなら、ブラウザに標準の Intersection Observer がすべての主要ブラウザで使えるので、ライブラリを足さずに書けます。
Intersection ObserverとCSSで同じことをする代替コード
この節では、WOW.js を使わずに、ブラウザの標準の機能だけで「スクロールで要素が現れる」動きを作るコードと、その測定結果が分かります。
JavaScript は「画面に入ったらクラスを付ける」だけにし、動きは CSS の transition で書きます。 JavaScript の部分は445バイト(gzip で279バイト)で、WOW.js の8,107バイトと animate.css の71,750バイトの代わりになります。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>document.documentElement.classList.add('js')</script>
<style>
.js .reveal {
opacity: 0;
transform: translateY(40px);
transition: opacity .6s ease-out, transform .6s ease-out;
transition-delay: var(--delay, 0s);
}
.js .reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
.js .reveal { opacity: 1; transform: none; transition: none; }
}
@media print {
.js .reveal { opacity: 1; transform: none; }
}
</style>
</head>
<body>
<section class="reveal">下から現れる</section>
<section class="reveal" style="--delay: .2s">0.2秒遅れて現れる</section>
<script src="reveal.js"></script>
</body>
</html>
// reveal.js(445バイト)
(() => {
const els = document.querySelectorAll('.reveal');
if (!('IntersectionObserver' in window)) { els.forEach(el => el.classList.add('is-in')); return; }
const io = new IntersectionObserver((entries) => {
for (const e of entries) {
if (!e.isIntersecting) continue;
e.target.classList.add('is-in');
io.unobserve(e.target); // 1回だけ。くり返すならこの行を消し、else で外す
}
}, { rootMargin: '0px 0px -10% 0px' }); // 画面の下10%に入ったら(WOW.js の offset にあたる)
els.forEach(el => io.observe(el));
})();
このページを Chromium で確かめた結果です。
| 条件 | 入る前の opacity | 入ってから150ms ごとの opacity | 結果 |
|---|---|---|---|
| 通常 | 0 | 0 → 0.22 → 0.59 → 0.84 → 0.99 | 0.2秒待ってから動く |
| 動きを減らす設定 | 1 | 1 → 1 → 1 → 1 → 1 | 最初から表示 |
| JavaScript 無効 | 1 | 1 → 1 → 1 → 1 → 1 | 最初から表示 |
| PDF に印刷 | - | - | box 0〜2 の文字がすべて出た |
WOW.js の機能との対応です。スクロール領域の中で使うときは、root にその領域の要素を渡せば、領域の高さで判定されます。
| WOW.js | 代替コードでの書き方 |
|---|---|
| class="wow fadeInUp" | class="reveal"(動きは CSS の .reveal に書く) |
| data-wow-delay="0.2s" | style="--delay: .2s"(transition-delay) |
| data-wow-duration="2s" | .reveal の transition の秒数を変える |
| data-wow-offset="200" | rootMargin: '0px 0px -200px 0px' |
| scrollContainer | root: document.querySelector('#sc') |
| callback | isIntersecting の中に処理を書く |
| animate.css の動きを使いたい | is-in の代わりに 'animate__animated', 'animate__fadeInUp' を classList.add |
Intersection Observer の threshold・rootMargin の詳しい意味や、遅延読み込みへの使い方は、関連記事「IntersectionObserver使い方|無限スクロール・遅延読込レシピ集」で扱っています。スクロール位置そのものを取る方法は、関連記事「JavaScriptスクロール位置の取得と移動|scrollTo・イベント早見表」で扱っています。
JavaScript を使わない方法として、CSS のスクロール駆動アニメーションもあります。Chromium 153 では CSS.supports('animation-timeline: view()') が true でしたが、MDN では「Limited availability(主要なブラウザの一部で動かない)」とされているので、動かないブラウザでは最初から表示される書き方にします。
@keyframes reveal-up {
from { opacity: 0; transform: translateY(40px); }
to { opacity: 1; transform: none; }
}
@supports (animation-timeline: view()) {
@media (prefers-reduced-motion: no-preference) {
.reveal-sd {
animation: reveal-up linear both;
animation-timeline: view(); /* animation の後に書く */
animation-range: entry 0% entry 100%;
}
}
}
WOW.js・AOS・代替コードの比較表と判断表
この節では、WOW.js をこのまま使うか、別の方法に替えるかを決めるための比較と、判断の順番が分かります。
| 観点 | WOW.js+animate.css | AOS | Intersection Observer+CSS | CSS スクロール駆動 |
|---|---|---|---|---|
| 追加するファイル | JS 8KB+CSS 72KB | JS と CSS(npm 2.3.4) | 自作の JS 445バイト | なし |
| 更新 | 2016年が最後 | 2022年が最後(npm) | ブラウザの標準 | ブラウザの標準 |
| 待機中のタイマー | 50ms ごとに動く | 今回は測っていない | 無し | 無し |
| 動きを減らす設定 | 自分で init を止める | 今回は測っていない | CSS の @media で対応 | CSS の @media で対応 |
| 対応ブラウザ | 主要ブラウザ | 主要ブラウザ | 主要ブラウザ | 一部(MDN で Limited) |
AOS は今回動かしておらず、npm の登録情報だけを載せています。
- 1今あるサイトで動いていて、直すのは一部 → WOW.js のままanimate.css の版・ライセンスの系統・prefers-reduced-motion だけ見直す
- 2新しく作る・作り直す → Intersection Observer+CSS依存が増えず、React・Next.js の部品にもしやすい
- 3animate.css の動きの種類を使いたい → Intersection Observer+animate.cssclassList.add で animate__ のクラスを付ける
- 4スクロール量に合わせて動かしたい → CSS スクロール駆動動かないブラウザでは最初から表示する書き方にする
- 5スライドを横に送りたい → スライダーのライブラリスクロールで現れる動きとは別の部品
スライダー(カルーセル)の作り方は、関連記事「Swiperの使い方|オプション早見表・React/Vue・動かない原因を実演」で扱っています。
よくある質問
この節では、WOW.js の使い方で検索されることの多い疑問に、短く答えます。
Q. WOW.jsとは何ですか?
A. スクロールして画面に入った要素に、animate.css などのアニメーションのクラスを付けて動かす JavaScript ライブラリです。要素に class="wow fadeInUp" を付け、new WOW().init() を呼ぶだけで使えます。jQuery は要りません。
Q. WOW.jsが動きません。
A. まず DevTools で要素の style 属性を見ます。何も付いていなければ init を呼んでいないか、wow.min.js の読み込みに失敗しています。visibility: visible なのに動かないなら、animate.css 4 の接頭辞の食い違いなので、animate.compat.css に替えます。
Q. animate.css 4で使えますか?
A. 使えます。animate.compat.css を読み込むか、new WOW({ animateClass: 'animate__animated' }) にして、クラスを animate__fadeInUp のように接頭辞付きで書きます。
Q. data-wow-delayの単位は?
A. s と ms のどちらでも書けます。0.5s も 500ms も、そのまま CSS の animation-delay に入ります。data-wow-delay="1" のように単位を省くと、style 属性に入らず、animation-delay は 0s のままでした。
Q. data-wow-offsetは何を指定しますか?
A. 画面の下端から何px入ったらアニメーションを始めるかです。200 にすると、要素の上端が下端から約200px上がったところで始まりました。全体の既定は new WOW({ offset: 100 }) で決めます。
Q. スクロールを戻したら、もう一度動かせますか? A. WOW.js は一度表示した要素を対象から外すので、そのままではできません。Intersection Observer で、画面から出たらクラスを外し、入ったら付け直す書き方にします。
Q. スマホでだけ止められますか?
A. new WOW({ mobile: false }) で、携帯端末(User-Agent で判定)では隠しも動かしもしなくなります。画面の幅で分けたいときは、matchMedia で幅を確かめてから init を呼びます。
Q. 商用のサイトで使えますか?
A. npm の wow.js(MIT 版)は MIT ライセンスです。元の版(npm の wowjs・cdnjs の wow)は GPLv3 と商用ライセンスの2本立てで、README は商用のサイトには商用ライセンスが適切としています。どちらを読み込んでいるかを確かめます。
Q. WOW.jsは今も更新されていますか? A. 2026年10月29日時点で、どちらの系統も2016年の版が最新です。npm の wow.js は非推奨の印が付いていて、AOS への移行が案内されています。新しく作るなら Intersection Observer と CSS で書く方法が選べます。
Q. Next.jsで「window is not defined」になります。
A. WOW.js を読み込むとすぐに window を使うためです。'use client' の部品の useEffect の中で import('wow.js') し、そこで init を呼びます。
まとめ
この節では、WOW.js の使い方の要点を5つにまとめます。
- 使い方は3つ:animate.css と wow.min.js を読み込み、
class="wow fadeInUp"を付け、wow.min.js の後でnew WOW().init()。 - animate.css 4 は接頭辞に注意:そのままでは「表示されるが動かない」。animate.compat.css か
animateClass: 'animate__animated'。 - 属性は4つ:data-wow-duration・delay・iteration・offset。offset は画面の下から何px入ったら始めるか。
- 配慮を足す:動きを減らす設定では init を呼ばない。印刷用の CSS で隠しを打ち消す。
- 新しく作るなら代替も:2016年から更新が無く、50ms ごとのタイマーが動き続ける。Intersection Observer+CSS(445バイト)で同じことができる。
参考・出典
確認日はいずれも 2026年10月29日です。
- GitHub「graingert/WOW」README(MIT 版・既定値・非推奨の案内):https://github.com/graingert/WOW
- GitHub「matthieua/WOW」README(元の版・ライセンス・sync()):https://github.com/matthieua/WOW
- npm「wow.js」:https://www.npmjs.com/package/wow.js
- npm「wowjs」:https://www.npmjs.com/package/wowjs
- cdnjs「wow」:https://cdnjs.com/libraries/wow
- Animate.css 公式サイト(v4 の使い方・animate.compat.css・prefers-reduced-motion):https://animate.style/
- MDN「animation-timeline」:https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timeline
- MDN「Intersection Observer API」:https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API
- MDN「prefers-reduced-motion」:https://developer.mozilla.org/ja/docs/Web/CSS/@media/prefers-reduced-motion
- WordPress Developer Resources「wp_add_inline_script()」:https://developer.wordpress.org/reference/functions/wp_add_inline_script/
- Chrome for Developers「Emulate CSS media features」:https://developer.chrome.com/docs/devtools/rendering/emulate-css
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「WOW.jsの使い方|animate.css 4で動かない原因と代替コードを実測」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。