jQueryは1個の要素と複数個の要素を同じように書かせる
#フロントエンド #設計 #JavaScript
「1個の要素と複数個の要素を同じように書かせる」というのが実は jQuery の特徴の一つだと思っている。
document.querySelector() や querySelectorAll が出現して以降 jQuery の必要性はほとんどなくなったと言われる。
実際にこんなサイトもあるし https://youmightnotneedjquery.com/
これは実際その通り
一方、jQuery で書かれたコードを querySelector や querySelectorAll に移行しようとしたら苦しかったという経験をした人は多そう。
上のコードが下のようになる。
code:javascript
$('.foo').on('click', e => {
// ...
})
Array.from(document.querySelectorAll('.foo')).forEach(el => {
el.addEventListener('click', e => {
// ...
})
})
これは単に記述が長いから、というだけの理由ではないはず。
jQuery のときは .foo が画面内に1個なのか複数個あるのか(あるいは1個もないのか)考える必要がなかった
よしなにやってくれていた
そもそも .click の代わりに .on('click') が導入された目的がそれだった気がする(うろ覚え)
が、DOM API を書いているときは明示的に考えないといけなくなった
これがミスの原因である、というのが問題
かなりコーディングが苦手な人とかなら記述量が理由になることはあるかもしれないが……
それはまた別の問題
例: デザイナが書いてるケースなど
#デザイナがコードを書けたほうが良いか問題
.on を見たときに、それを機械的に querySelector + addEventListener に置換して良いのか判断しにくい
.on 以外のメソッドでも都度人間が判断しなければならない。
「このメソッドって対象はマッチする1個の要素だっけ?全要素だっけ?」
逆に言うと、jQuery で書かれたコードが読む人に優しくなかったのは、書いてる人がこれを意識してなかったせいという面もある
明確に意識できる分 querySelectorAll + forEach の方がマシという意見もありそう
そしてその感覚は正しい
ときどきフロントエンドを専門としない人から「この画面 #React 使うほどじゃないんだけど何で書けば良い?」と聞かれた時に困ることがある
「どうせリッチになるんだったら最初から #React 使えば?」と言えるケースなら良い
これもこれでよく言ってる
しかし実際たしかにライブラリ無しでやりたいとき、上のような書き方でやりなさいとしか言えないのもイマイチではある
なぜなら上述の通りミスのもとが多いから
jQuery が果たしていた役割としてみんな「ブラウザ間差異の吸収」をあげることが多い
だから polyfill が発展した時代においては不要なのだと
が、実はあまり注目されていないポイントとして「要素の個数を意識させない(1個の要素と複数個の要素を透過的に扱う)」も挙げたほうが良い
そして後者を担保する手段は宣言的UIのライブラリしかない、というケースは実際よくある
React や Vue を使わない人がこの恩恵だけを得る手段は意外と少ない
個人的に、現代で「要素の増減を意識しなくて良い」を最も満たせる標準 API は Custom Element だと思っている
#WebComponents
先程の例にあげたコードは、Custom Element で書いたらこんな感じになるはず
code:javascript
class FooElement extends HTMLElement {
static { customElements.define('foo-element', this) }
connectedCallback() {
this.addEventListener('click', this.#handleClick)
}
disconnectedCallback() {
this.removeEventListener('click', this.#handleClick)
}
#handleClick = (e) => {
// ...
}
}
これは記述量こそ多めだが、画面内にいくつ <foo-element>(ないし<div is="foo-element">)があるかは意識する必要がない
画面内で <foo-element> が増減したときは connectedCallback でよしなにやってくれる
「これ querySelectorAll + forEach で書かないといけないんだっけ」とかは考える必要がない
foo-element の中の部品に対して querySelectorAll をすることはあるけど
大域的に querySelector を使って DOM 操作をするよりはミスをしにくい
querySelector + addEventListener だけで jQuery を置き換えるのは難しい
が、そこに Custom Element も組み合わさることで簡単かつ(ある程度)治安のよい DOM 操作を書ける
Backbone.View が標準仕様になったようなものと言える
当然 #React とかに比べて大したことはできない
が、私は Stimulus とか htmx とか Alpine.js 使うぐらいなら Custom Element 書けばいいのにと前から思っているタイプである
私は HTML Centric の思想にそこまで良さを感じていない
より正確には、「Custom Element を定義したら独自の HTML 要素が書ける」ぐらいが私にとって意味のある "HTML Centric" の上限だと思っている
それ以上の頑張り(よりすべての挙動を HTML の中で記述できるようにすること)に価値を感じない
にも関わらず #TailwindCSS は好きなので、たぶんイベントハンドラのような処理を JS の記述に寄せたい派なのだと思う
Server Action( React のアレ )も好意的に見ている。htmx とかよりも。
ちょっとでも複雑なものは全部 React や #Vue.js にすればよく、そうでないレベルの単純なものは Custom Element で済むだろ派
なんなら React プロジェクトの中に Custom Element がいてもおかしくない
JSX で Web Components を扱うのはそこまで難しくない
code:typescript
declare global {
export namespace JSX {
interface IntrinsicElements {
// グローバルなJSXの型定義に<my-element>を追加
// 渡して良い属性の型はMyElementPropsで定義
'my-element': MyElementProps
}
}
}
Custom Element をこの方面で評している人意外と少ない気がする