Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

bridgey

jQuery のまま、モダンに。

保守する人のためのライブラリです。 書き直す予算はなくても、事故だけは止められます。

<script src="https://cdn.jsdelivr.net/npm/bridgey@2"></script>

これだけ。ビルド不要、依存ゼロ、gzip 18kB、eval 不使用(CSP セーフ)。 HTML は1文字も変えなくていいし、
いま動いている jQuery のコードも1行も変えなくていい。


30秒で試す

demo.html という名前で保存して、ブラウザで開くだけです。サーバもビルドも要りません。

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>

<p>合計 <span id="total">0</span> 円</p>
<button id="add">追加する</button>

<script src="https://cdn.jsdelivr.net/npm/bridgey@2"></script>
<script>
  const count = $$.state(0);
  const total = $$.computed(() => count() * 1100);

  $$("#total").text(total, $$.money);        // 状態が変われば画面が勝手に追いつく
  $$("#add").click(() => count(n => n + 1)); // ここは jQuery のまま
</script>

</body>
</html>

$ を $$ にするだけ。増えた概念は1つだけです。

$$("#total").text("1,100");   // 値を渡す      → その瞬間だけ入る(jQuery と同じ)
$$("#total").text(total);     // state を渡す  → 以後ずっと自動で追いかける ← これだけが新しい

まず4語

state      状態を作る
computed   状態から計算する(合計・小計・表示するか)
when       条件で表示を出し入れする
repeat     一覧を描く(行の DOM を使い回すので入力が消えない)

これで8割書けます。残りは知らなくても詰まりません(必要になった場面で bridgey が教えます)。

const count = $$.state(0);

count()             // 読む   ← .val() と同じ手癖
count(5)            // 書く   ← .val(5) と同じ手癖
count(n => n + 1)   // 更新
`${count}`          // 補間もできる

何を解決するのか

実際の現場で起きていた事故を、そのまま止めます。

事故 bridgey では
数値計算が合わない(change の中で合計を計算して代入していた) 合計は導出値。代入する場所が存在しないので更新漏れが起きない
非表示の必須項目で送信できず、理由も出ない(CV が消える) 見えているフィールドだけを検証する。エラー0件で送信不能にならない
オートフィルで入力しても未入力扱い value の代入を検知する(既存 jQuery の .val() 経由でも追従)
HTML に要素を1つ挟むと静かに壊れる(next() / children() / eq()) 名前で引く。壊れたら0件で警告する(jQuery は黙る)
1箇所の undefined で全機能が死ぬ(common.js の $(function(){})) component() 単位でエラーを隔離。壊れた部品だけが止まる
class 名のタイポで、隠すべきものが見えてしまう CSS に無い class 名を検出し、似た名前を提示する
Ajax で追加した要素にプラグインが効かない component() が自動で適用。消えたら自動で片付ける
二重送信でレコードが2件できる action() / form() が構造的に防ぐ
リストを .html() で作り直して入力が消える repeat() がキー付き差分。行の DOM を使い回す
通信の後着追い越しで古い金額が残る resource() が前のリクエストを中断し、古い応答を捨てる

段階移行 — 全部書き直さなくていい

**1機能だけ置き換えられます。**予算が取れない現場のために、これが最優先の設計です。

第1週  金額計算だけ bridgey に(20行)。他は jQuery のまま。同じ DOM に共存OK
第2週  フォームの検証だけ移す。CV 事故が止まる
第3週  タブとアコーディオンを component に。undefined の全滅が止まる
       …残りは触らなくてよい

そして、そのまま卒業できます

同じ UI を4段階で書けます。①②③はいま全部動きます。

// ① いまのやり方(そのまま動く)
$(".tab").on("click", function () {
  $(".tab").removeClass("active");
  $(this).addClass("active");
});

// ② bridgey の JS API(事故が減る)
const current = $$.state("a");
$$(".tab").each((el) => $$(el)
  .click(() => current(el.dataset.tab))
  .selected(() => current() === el.dataset.tab));

// ③ ディレクティブ(宣言的な考え方が身につく・完全に任意)
//    <li data-brg-selected="isCurrent" data-brg-on-click="pick">
$$.bind("#tabs", { isCurrent, pick });

// ④ Svelte / Vue(卒業)
//    <li class:active={isCurrent} on:click={pick}>
//    <li :class="{active: isCurrent}" @click="pick">

③の記法は④と1対1で対応しています。

bridgey Vue Svelte
data-brg-text="total" {{ total }} {total}
data-brg-when="isFree" v-if="isFree" {#if isFree}
data-brg-repeat="todos" v-for="t in todos" {#each todos as t}
data-brg-class-active="isOn" :class="{active: isOn}" class:active={isOn}
data-brg-attr-src="url" :src="url" src={url}
data-brg-value="keyword" v-model="keyword" bind:value={keyword}
data-brg-on-click="save" @click="save" on:click={save}

ディレクティブに式は書けません。

<p data-brg-when="qty > 3">   <!-- ✗ 書けない(警告が出ます) -->
const isFree = $$.computed(() => qty() >= 3);   // ロジックは JS 側
<p data-brg-when="isFree">    <!-- ✓ -->

式を評価しないので new Function が不要 = CSP セーフのまま。{{ }} を作らないので Blade / Twig / Smarty と衝突しません。


使えるもの

知っている jQuery のメソッドは、そのまま使えます

text html val attr removeAttr prop css addClass removeClass toggleClass hasClass data find closest parent children first last is each append empty remove on off click change submit input keydown focus … $$.fn(プラグイン拡張口)

そこに state を渡せるようになっただけです。

$$("#label").text(total);                    // 表示
$$("#btn").prop("disabled", busy);           // 活性
$$("#box").toggleClass("open", isOpen);      // 見た目
$$("#img").attr("src", url);                 // 属性
$$("#q").val(keyword);                       // 双方向
$$("#bar").css("width", () => `${pct()}%`);  // 関数を渡せば依存を自動追跡

表示の形はここで整えられます。

$$("#n").text(count, "件");                  // 3件            後ろに付ける
$$("#n").text(count, "残り{}件");            // 残り3件        {} の位置に入れる
$$("#t").text(total, $$.money, "円");        // 23,800円       3桁区切り+単位

困ったら足す4語

$$("#signup").form({ … })   // フォームの事故を止める(非表示・オートフィル・二重送信)
$$("#plan input").group()   // ラジオ/チェックボックスの選択を1つの状態に
$$.component("tabs", fn)    // common.js を安全にする(要素がある頁でだけ動く・エラー隔離)
$$.resource(src, fetcher)   // 通信の競合を止める(自動中断・後着追い越しなし)

知らなくても書けるもの(出会ったときに覚える)

mode vars toggleAttr selected expanded invalid busy disabled action scope effect tick batch untrack bind money

.addClass("active") と書くと、bridgey がこう教えます。

[bridgey] addClass("active") で状態を表しているようです。
          → .selected(state) が使えます: aria-selected も一緒に付き、
            他の要素から消し忘れることもありません
          (このヒントは $$.hints = false で止められます)

セレクタ

id も class も今まで通り使えます。@ は data-brg 属性の省略記法です。

$$("#total")     // id="total"
$$(".total")     // class="total"
$$("@total")     // data-brg="total"   ← [data-brg="total"] の略記

@ を使うと、class 名や DOM 構造が変わっても JS が壊れません(デザイナーが自由に見た目を変えられる)。必須ではありません。0件になったときは、こう教えます。

[bridgey] $$(".price") が0件でした。text() を結びつけられません。
  HTML が変わった可能性があります(class 名の変更・要素の削除・読み込み順)。
  → 壊れにくくするには data 属性で結びつけてください:
     <span data-brg="price"></span>  →  $$("@price")

属性名は $$.attr = "data-ui" で変えられます。


名前は自由に変えられます

既定は $$。<script> タグの1箇所だけで変えられます。

<script src="…/bridgey@2"></script>                     <!-- $$ -->
<script src="…/bridgey@2" data-global="$"></script>      <!-- $ -->
<script src="…/bridgey@2" data-global="$$$"></script>    <!-- $$$ -->
<script src="…/bridgey@2" data-global="$_$"></script>    <!-- $_$ -->
<script src="…/bridgey@2" data-global="brg,$$"></script> <!-- 両方 -->

$$ が既に使われていたら(Prototype.js など)、勝手に別名へ逃がさず警告します。サイトによって名前が変わると、コピーしたコードが動かない原因が分からなくなるからです。window.bridgey からは常に使えます。


npm でも使えます(型が付きます)

npm i bridgey
import { $$ } from "bridgey";
import { $$ as $ } from "bridgey";   // 好きな名前に(言語機能で足りる)

TypeScript で書かれているので、型定義は実装と乖離しません。

$$("input#name").prop("checked", flag);   // OK
const count = $$("#n").state(0);
count("あ");                               // ❌ number ではない

持たないもの、その代わりに

持たないもの 代わりに
fadeIn / slideToggle CSS の transition + .toggleClass("is-open", state)
$.ajax / $.get / $.post fetch()。競合制御が要るときは $$.resource()
$.extend / $.each / $.map {...a, ...b} / for...of / Array#map
serialize() new FormData(form)。検証は form()
next / prev / siblings / eq / index closest("@row").find("@price")(構造ではなく名前で引く)
width / height / offset getBoundingClientRect()
show / hide / toggle when(state)
ルーター / SPA 化 URL の主権はサーバーに
スコープ付き CSS / 仮想DOM / SSR 入れません

呼ぶと、代わりの書き方を教えて例外になります。

$$("#row").next();
// Error: [bridgey] next() は使えません。HTML の構造が変わると壊れるため v2 では
//        提供していません(要素を1つ挟むだけで、例外も出ないまま静かに動かなくなります)。
//        → 構造ではなく名前で引いてください: $$(this).closest("@row").find("@price")

デモを動かす

npm install
npm run build

あとは demo/index.html をブラウザで開くだけです(サーバー不要)。

start demo/index.html      # Windows
open demo/index.html       # macOS

デモでは、実際に作者がレガシーな現場で遭遇した事故を再現して止めてみせます。

  • 「HTML を1段ラップする」 → 従来のやり方(next())だけが静かに壊れる
  • 「オートフィルを再現」 → イベントが飛ばなくても検証が追従する
  • 「送信を5連打」 → 1回しか送信されない
  • 「式を書いてみる」 → ディレクティブが警告を出す
  • わざと壊した component → 他の部品は動き続ける
  • 同じ画面を ②JS API と ③ディレクティブで並べて比較

コンソールを開いておくと、警告とヒントが読めます。


動作環境

  • モダンブラウザ(ES2020 / AbortController / MutationObserver)
  • eval / new Function を使いません → script-src 'self' の厳格な CSP でも動きます
  • jQuery と同じページ・同じ要素で共存できます($ は奪いません)
  • jQuery プラグイン(slick / DataTables 等)は bridgey では動きません。jQuery 本体と併用してください

v1 からの移行

v2 は破壊的変更です。v1 は 1.x ブランチで凍結しています。

v1 v2
useEngine(svelteEngine) / mount(Component) 削除。Svelte / Vue への依存をやめました
state(0) → count.value $$.state(0) → count() / count(5)
bindText / bindClass / bindAttr / bindValue .text(state) / .toggleClass(n, state) / .attr(n, state) / .val(state)
computed(deps, fn) $$.computed(fn)(依存は自動追跡)
.val() が String のサブクラス 普通の文字列(=== "" が期待通り動く)
jQuery 共存は非サポート 公式サポート

開発

npm test          # jsdom で DOM も dist も検査
npm run typecheck # tsc(型検査 + .d.ts 生成)
npm run build     # esbuild で dist を作る

テストは「事故の名前」で書いてあります。

✔ ★①見えていないフィールドは検証しない
✔ ★②JS からの値の代入も検知する(オートフィル・既存 jQuery の .val() 相当)
✔ ★行の DOM は使い回される(入力中の文字が消えない)
✔ ★後着追い越しが起きない(遅い応答が後で届いても上書きしない)
✔ ★1つの初期化が失敗しても、他の部品は動き続ける
✔ ★数値計算が合わない: 合計を「書き込む場所」を無くす
✔ dist: eval / new Function を使っていない(CSPセーフ)

English

bridgey — modern state management with jQuery's ergonomics. Zero dependencies, no build step, ~18kB gzipped, and no eval (works under a strict CSP).

Built for people who maintain legacy jQuery rather than start new apps: adopt it one feature at a time, keep jQuery on the same DOM node, and never edit your HTML if you can't. Where jQuery fails silently, bridgey tells you what broke — empty selector matches, class names missing from your CSS, duplicate list keys, components that failed to initialize.

Four words to start: state, computed, when, repeat. Optional directives (data-brg-text, data-brg-when, …) map one-to-one onto Vue and Svelte template syntax, so this becomes a stepping stone rather than a dead end.

<script src="https://cdn.jsdelivr.net/npm/bridgey@2"></script>
<script>
  const count = $$.state(0);
  $$("#total").text($$.computed(() => count() * 1100), $$.money);
  $$("#add").click(() => count(n => n + 1));
</script>

MIT License

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages