- Cash は、IE11+ のモダンブラウザで DOM を操作するための jQuery スタイル構文を提供する、非常に小さな jQuery 代替ライブラリ
- モダンブラウザの機能を活用してコードベースを削減し、使い慣れた チェーン可能なメソッド をはるかに小さいファイルサイズで利用できる
- jQuery と 100% の機能同等性 を目指しているわけではないが、日常的なユースケースの大半をカバーし、実装されている API はおおむね jQuery と互換性がある
- サイズは minified & gzipped で 6KB で、jQuery Slim 3.4.1 の 24.4KB と比べて 76.6% 小さく、partial builds でさらに削減できる
- TypeScript コードベース、コードから生成される TypeScript 型、名前空間付きイベント、個別メソッドを除外できる partial builds をサポート
Cash が解決する問題
- Cash はモダンブラウザ向けの jQuery 代替で、DOM 操作のために jQuery スタイルの
$() セレクタとチェーン可能なコレクションメソッドを提供する
- サポート対象は IE11+ ブラウザ
- jQuery のすべての機能をそのまま実装することが目的ではないが、Cash が実装している機能の多くは jQuery API と互換になるよう設計されている
- jQuery から移行するユーザーは migration guide を確認できる
サイズと機能の比較
- ファイルサイズの比較では、Cash は Zepto 1.2.0 と jQuery Slim 3.4.1 より小さい
- Unminified: 36.5KB
- Minified: 16KB
- Minified & Gzipped: 6KB
- jQuery Slim 3.4.1 は minified & gzipped で 24.4KB であり、Cash はこれに対して 76.6% のサイズ削減効果がある
- より小さいバンドルが必要なら、partial builds を利用できる
- 機能比較では、Cash はモダンブラウザ対応、活発なメンテナンス、名前空間付きイベント、TypeScript コードベース、コードから生成される TypeScript 型を提供する
- Partial builds では Cash は個別メソッドの除外が可能で、Zepto と jQuery Slim はモジュール単位での除外となっている
使い方
<script src="https://cdn.jsdelivr.net/npm/cash-dom/…;
<script>
$(function () {
$('html').addClass ( 'dom-loaded' );
$('<footer>Appended with Cash</footer>').appendTo ( document.body );
});
</script>
npm install --save cash-dom
import $ from "cash-dom";
$(function () {
$('html').addClass ( 'dom-loaded' );
$('<footer>Appended with Cash</footer>').appendTo ( document.body );
});
API 構成
$() は Cash の中核となる セレクタメソッド で、操作可能なノードコレクションを返す
- 関数が渡された場合は、DOM の準備完了時にその関数を実行する
$() は selector、DOM node、nodeList、HTML string、Cash collection、document ready callback を受け取れる
- Cash は大きく 3 種類の API を提供する
- クエリセレクタ
- コレクションメソッド
- グローバル
$ オブジェクトのライブラリメソッド
コレクションメソッド
- コレクションメソッドは
$() でコレクションを作成した後、$(element).addClass(className) のような形で呼び出す
- 提供カテゴリは 属性、コレクション、CSS、データ、寸法、エフェクト、イベント、フォーム、DOM 操作、オフセット、探索に分かれる
- 主なメソッドには次が含まれる
- クラス/属性:
addClass, removeClass, toggleClass, attr, prop, removeAttr
- コレクション処理:
add, each, eq, filter, first, get, map, slice
- DOM 操作:
append, prepend, before, after, html, text, clone, remove, replaceWith, wrap
- イベント:
on, off, one, ready, trigger
- 探索:
find, children, closest, parent, parents, siblings, next, prev
- 一部の extra methods は提供されているが、デフォルトでは無効になっている
$.fn はコレクションのメインプロトタイプで、プラグインのようにすべてのコレクションへカスタムメソッドを追加できる
グローバル Cash メソッド
- グローバル
$ オブジェクトには型チェックとユーティリティメソッドが含まれる
- 型チェックメソッドとして
$.isArray, $.isFunction, $.isNumeric, $.isPlainObject, $.isWindow を提供する
- ユーティリティには
$.guid, $.each, $.extend, $.parseHTML, $.unique が含まれる
$.extend は対象オブジェクトをソースオブジェクトのプロパティで拡張し、ディープ拡張もサポートする
$.parseHTML は HTML 文字列からコレクションを返し、$.unique は重複を除去した新しい配列を返す
拡張と貢献
- Cash はカスタムメソッドで拡張でき、拡張方法は extending Cash にまとめられている
- 問題や機能要望は GitHub の issue で起票できる
- Pull request の作業フローは、リポジトリのクローン、依存関係のインストール、
npm run dev による自動再コンパイル、npm run test によるテスト実行、必要に応じた README の更新という順序になっている
- ライセンスは MIT
1件のコメント
Hacker News の意見
最近のブラウザは良くなっているので、DOM 操作を単純化するには、下の2行のエイリアスだけで十分な場合が多い
dqs = document.querySelector.bind(document);dqsA = document.querySelectorAll.bind(document);そのため、
document.querySelector('#country')の代わりにdqs('#country')、document.querySelectorAll('.city')の代わりにdqsA('.city')のように使える残りはそのままネイティブのブラウザ関数を使っても問題なく、通常は
import { dqs, dqsA } from '/lib/js/dqs.js';のようにモジュールから取り込むhttps://github.com/no-gravity/dqs.js
querySelector/querySelectorAllをバインドした2行自体は有用で合理的に見えるが、それをimportで取り込むのはやりすぎたかが簡単な2行なので、依存関係にする理由はなく、コピーして貼り付ければよい
たとえば複数のイベントを扱う
$.fn.one()や$.fn.on()は jQuery/Cash で使うほうが簡単で、内部実装を見るとかなり多くの処理をしている: https://github.com/fabiospampinato/cash/blob/master/src/even...querySelectorAll()はライブコレクションではないので、よく次のようにすぐ配列へ変換して使うdqsA = s => Array.from(document.querySelectorAll(s));こうすると結果に
.map()や.filter()のような配列メソッドをすぐ使えるため、jQuery に近い感覚が残るたとえばテスト中、Safari ではある要素の
selectイベントがまったく発生せず、一部ブラウザではキャレットが移動しただけではイベントが出なかったコンポーネント、状態、props のような React の機能が不要でも、ネイティブのブラウザ関数だけでは十分ではなく、React DOM がブラウザ差を覆い隠してくれる価値が明らかになった
ポリフィルをほぼすべて取り除いた後に jQuery に残る継続的な利点は、リストの自動処理だと思う
フォーム内のすべてのボタン選択を1回の呼び出しで解除できる能力は、まだ他ではなかなか匹敵しにくく、親要素のクエリも同様
ただし実装上の最大の問題は、リストが空だったときに黙って失敗する点
DOM ツリーを後でレイアウト目的でリファクタリングした際に生じたこうしたバグをあまりに多く直してきたので、いま jQuery を作り直すなら、デフォルトでは空集合でエラーを出し、本当に気にしないときだけ黙って失敗するようなチェーン呼び出しやフラグを用意すると思う
以前 Sizzle を取り除いてこのように変更できるか数時間調べたが、それ以上は進めなかった
結局 jQuery はライブラリ対フレームワークという昔からの議論にもつながっており、巨大なフレームワークでシングルページアプリを長くやってきた分、また幻滅の谷が近づいている気がする
.fooをすべて非表示にしろと命令すれば、存在すれば非表示になり、なければ何も起きない fire and forget 方式で、CSS に似ている.foo { color: red; }と書いたのに文書内に.fooがなくても、小さなオーバーヘッド以外の副作用はないdocument.querySelectorAll('input[type=checkbox]').forEach((i) => i.checked = false);これは反復可能な
NodeListとイテレータヘルパーを活用する方法多くの親要素クエリは
element.closest()で処理できるjQuery ベースのコードでは、全開発者がプロジェクト内のすべてのセレクタを把握していて、DOM が変わるたびに更新しなければならないが、当然不可能
jQuery の初期化関数を自前実装に置き換えて、長さチェックを強制することはできそう
主流の Web サイトが文字どおりメガバイト単位の JavaScriptを配っている状況で、50KB を節約するために機能の少ないライブラリを丸ごと書き直す理由がわからない
多くの記事はパッケージサイズと帯域幅の制約を理由に jQuery の使用に反対しながら、同時にずっと多くの帯域幅を使う SPA フレームワークは擁護している
完全にばかげたカーゴカルト的な推論だ
3つの場合とも答えは同じ: 大きいほうは自分ではなく、自分はもっと小さい別のことをしている
もう1つの答えは、大きすぎることが問題なら、小さくなることが解決策に聞こえるということ
結局聞きたいのは、開発者がなぜライブラリを書き直すことに時間を使うのか、ということのようだが、それほど驚くことではない
プログラミングのかなりの部分は既に作られたものを作り直すことであり、仕事上必要だったり、少し違う動作や性能特性が必要だったり、単にどう動くのか学びたかったりしてそうする
jQuery の代替を探しているなら、jQuery 4.0 を長く待ちすぎた末に、いくつか重要な違いを持つ独自の jQuery 風のものを作った
アニメーション、トゥイーン、タイムラインは jQuery のカスタムシステムではなく純粋な CSS を使い、単一要素とリストを透過的に扱い、インラインで使う方向を目指している
me()は要素 1 個を返す、または最初の要素かnullを返すと書かれており、any()は配列または空配列を返すと書かれているところが下の例では
any('button')?.forEach(...)、any('button')?.map(...)のように、nullになり得ることを示唆しているany()が上の説明どおり常に配列を返すのか、それとも下の例のようにnullもあり得るのか混乱する動作の局所性に特に関心がある
currentScript.parentElementを使ってみた経験がどうだったのか気になる先月ざっと調べたときは、おそらくニッチなケースでは信頼できないという印象を受けたが、正確にいつだったかは覚えていない
深く掘り下げたわけではなく、うまく動くようにしているのはうれしい
asyncやmoduleではないという前提なら、連続したスクリプト 3 つをロードしても、すべてのブラウザでcurrentScript.parentElementは依然として動くはずではないかと思うSvelteKit でもこの議論があり、結局対象要素を指定するためにランダム ID を実装した: https://github.com/sveltejs/kit/issues/2221
マイグレーションガイドを見ながら、jQuery ではできるが Cash ではできない機能のうち、知らなかったし、いつか使ってみてもよさそうなものをいくつか学んだ
https://github.com/fabiospampinato/cash/blob/master/docs/mig...
拡張目標として、TypeScript テンプレート文字列の魔法を使って要素型を正確に推論できるとよさそう
たとえば
$('div#name')がHTMLDivElementだと静的に推論できるtyped-query-selectorと呼ばれている実際の使用例はここにある: https://github.com/GoogleChrome/lighthouse/blob/main/types/i...
TypeScript でそれが可能なのか分からず、どうやればできるのかもよく見えない
jQuery 4 が最新ブラウザ向けの jQuery 代替だと聞いた
作っているブラウザ拡張では最初これを使っていたが、結局 JSX ライブラリに移った
「簡単なアプリ」の領域を超えると、jQuery はすぐに推論しにくいコードになり、jQuery に着想を得たライブラリを自分で作った者としてもそう感じる
結局、仕事に合った道具を使うべき
[1]: https://github.com/aleclarson/dough
中大規模アプリで jQuery をうまく扱えるならそれでよいが、自分の好みではない
以前 JS を減らそうとしていたとき、https://github.com/filamentgroup/shoestring を使っていた
主な理由は、本当に必要なものだけを入れる カスタムビルドを提供していたから
Cash にも似た機能があるようだが、ドキュメントでは少し見つけにくい: https://github.com/fabiospampinato/cash/blob/master/docs/par...
使うならまずそちらを試してみると思う
それでも、最近のブラウザが提供しているものをそのまま使うほうが、今でもより良い選択だと思っている
実際かなり良いし、jQuery はもはや必須ではない
特に小さな jQuery 代替でさえ 6kB なのに、React 風ライブラリの Preact はその半分のサイズだという点を見るとなおさらだ
すでにある Web API に別名を付ける以上の助けになるのか、よく分からない