バニラJavaScriptでDOM操作を完全に習得する
(phuoc.ng)- 本文では、バニラJavaScriptを使った Document Object Model(DOM)操作の習得について論じています。
- Angular、VueJS、React、Solid、Svelte といった現代的なツールを使う場合でも、Web開発のためにブラウザの DOM API を理解する重要性を強調しています。
- 著者は、基本的なブラウザ API、わかりやすい例、リアルタイムデモ、ヒントとベストプラクティス、実際のユースケース、そして最新ブラウザおよび Internet Explorer との互換性を含む資料集を提供しています。
- これらの資料は、初級・中級・上級の3つのレベルに分かれています。
- 初級レベルには、ユーザーのカラースキーム設定に応じてファビコンを動的に変更すること、Webページの読了時間を計算すること、CSS のルート変数を動的に更新することなどが含まれます。
- 中級レベルには、Shift+Tab キー操作でテキストエリアのインデントを解除すること、全画面モードに入ること、スピン入力を構築することなどが含まれます。
- 上級レベルには、カスタムカーソルを作成すること、HTML 文字列をサニタイズすること、カスタムスクロールバーを作成することなどが含まれます。
- 著者は、モーダルを開くときにレイアウトがずれるのを防ぐことや、テキストノードの境界矩形を取得することなどのヒントも提供しています。
- 本文は、フロントエンド技術コンテンツに関する著者のニュースレター購読への案内で締めくくられています。
1件のコメント
Hacker Newsのコメント
例の一覧が素晴らしく、いくつか目を通しただけでも細やかさとディテールに驚かされる
Reactのようなプラットフォームから、より小さくミニマルな実装へ移ろうとすると、標準JSで複雑なパターンをどう扱うかでしばしば行き詰まるが、実際にコードを見ると「巨大なライブラリに大量のプロパティを渡すよりずっと簡単だな」と思うことがある
ミニマルなJSフレームワークでも、一見素朴な機能要望が積み重なると、結局はフック、イベント、副作用の継ぎはぎの怪物になりがちだ
最近はjQueryも使わず、ESモジュールのようなものはコード整理に使うが、わざわざnpmコマンドを実行する必要はない。これはベンダーから取り込む類いのものではなく、グローバルなブラウザ標準だ
今ではMDNを聖書のように読んでいる。信じて従えばほぼ最後まで導いてくれるし、フレームワークはある地点からむしろ足かせになる。5年前はここまでではなかったが、ブラウザAPIはモジュール、flexbox、gridのような強力で一貫した基本要素へ急速に収束した
たとえば元記事の例の大半は、Reactを使っていても結局自分で書くことになるコードに近い
サイドプロジェクトとして、まだ名前のない「Webデザイナーのためのvim」のようなものを作っている。Webflowのようにブラウザ上でWebサイトやコンポーネントを作るが、すべてコマンド言語で動く仕組みだ
最初はフレームワークなしでやると決めていた。2009年からこの仕事をしていて、フレームワークに触る前から長くJSを使ってきたので、ネイティブDOM APIで十分だと思っていた
ところがDOMの内外でデータをやり取りする作業があまりに退屈でミスも多く、精神衛生のために結局ミニフレームワークを自作して使い始めた。そこでようやく、そもそもフレームワークを使い始めた理由を思い出した。結局は何らかの形でフレームワークを使うことになり、違いはそれが自作の特注品か、コミュニティ製の業界標準かだけだ
インタラクションの少ないサイトならネイティブDOMでもよく、Reactを使っているサイトの大半は実際には不要かもしれない。だが製品に少しでもニュアンスや複雑さがあるなら、フレームワークを使って面倒を避けたい。最終的にSvelteへ移行し、ずっと満足している
インタラクションが必要なWebサイトなら、DOM操作のほうがより根本的で汎用的な技術を使え、ビルド工程が軽いか不要で、実行も速く、明示的だという点でより良い選択になりうる
もちろんグレーゾーンは多いが、この事例はフレームワークの利点が出る範疇にきれいに収まっている
value.startsWith('javascript:')だけでは脆弱性が生じる。URLの前に任意の空白が入る可能性があるので、少なくともvalue.trim().startsWith('javascript:')が必要だそれでもブロックリストより許可リストのほうがよいと思う。
http:,https:、必要ならmailto:,ftp:,sftp:程度だけを許可し、/で始まる相対URLは場合によって許可できるかもしれないただし実際にURLになりうるすべての属性を正確に扱う必要があるため、結局はいくつかのタグとその属性だけを許可する方式のほうがよさそうだ
['http:', 'https:', 'mailto:', 'tel:'].includes(new URL(value, location.origin).protocol)のように処理できる[1] https://caniuse.com/url
この記事はHTMLサニタイズがそんなに簡単だという印象を与えるべきではない。末尾で https://github.com/cure53/DOMPurify に触れてはいるが、こういうものを自分で書こうとすべきではないともっと強く言い、DOMPurifyだけを勧めるほうが適切だと思う
この件でissueを立てた: https://github.com/phuocng/html-dom/issues/281
知っておくと便利な一覧のようで、全部は見ていないが今のところはよさそうだ
最初の例を開いてみると、
window.matchMedia('(prefers-color-scheme: dark)')を不要に2回実行しているsetFaviconをイベントリスナーのコールバックに使うなら、コールバックに渡されるevent引数のevent.matchesを使えばよい個人的にはHTMLで処理するほうがよい。JSを無効にしているユーザーにも対応できるからだ
https://phuoc.ng/collection/html-dom/change-the-favicon-dyna...
theme-colorメタにも同じ方法を適用でき、ディテールにこだわるPWA開発者にはあまり使われていないが優れた機能だ「textareaの自動拡張」手法は、新しいCSSプロパティ form-sizing の登場によって古くなりつつある。
https://chriscoyier.net/2023/09/29/css-solves-auto-expanding...
この新しいCSSプロパティを使わないとしても、要素の
heightを調整する方式は理想的ではなく、ぎこちなくなることがある。より良いアプローチは、隠しミラー要素を使うこと。
https://css-tricks.com/the-cleanest-trick-for-autogrowing-te...
ここにあるもののかなり多くは、より良いネイティブAPIがあるのに、古くてぎこちないAPIを使っている。
たとえば「要素の置き換え」は
ele.parentNode.replaceChild(newEle, ele);になっているが、Chrome 54未満、Firefox 49未満、Safari 10未満を対象にしないなら、単にele.replaceWith(newEle)を使えばよい。replaceWith は、1つのノードを複数のノードやテキストに置き換えることもできる。
同様に「NodeListの走査」も、配列に展開してから
forEachを使えと言っているが、単にfor (let ele of nodelist) ...でよい。いいね。ブックマークした。似た方向性の良い資料として https://youmightnotneedjquery.com/ もある。
Web作業を頻繁にするわけではないが、やるたびにあちらの フレームワーク過剰症 には完全にやる気を削がれる。新しいReactプロジェクトが依存関係数百個で始まるのは制御不能だし、その複雑さの重さには驚かされる。
一方で、標準搭載のクロスプラットフォームなブラウザ技術はこれまでになく強力で、よくある作業をこなすコード量もそれほど多くない。
フロントエンド開発者たち、大丈夫か?
たとえば アプリケーション状態管理 やルーティングは、純粋なDOM APIだけを使うなら自前で実装しなければならない大きな部分だ。
アプリの複雑さ次第では大したことではないかもしれないが、基本的なアプリを超えた瞬間に、こうした追加機能の価値は急速に高まる。ただしnpmが必要なものを使っているなら面倒になる。
基本プラットフォームとその機能を愛するフロントエンド開発者として、ネストされたCSS、コンテナクエリ、ES6以降の全体的に改善されたJS体験のような良いものが来ているのはうれしい。
同時に、ボタンや太字のような些細なものにまでコンポーネントを使う人が多すぎるのには驚きもする。
フレームワーク自体は好きだが、多くのフレームワークやライブラリのマーケティングは、CSSや素のDOM操作が狂ったように難しく、NPMから適当なパッケージを引っ張ってくることが速くて簡単なだけでなく、プロフェッショナルなやり方だという信念に依存している。
Kubernetes, Helm, Terraform, CircleCI のようなものを見るだけでも、何層にも積み重なった用語の量がばかげている。
複数のDOM関数が、自動的に更新される live HTMLCollection を返すと知ったときは本当に驚いた。
フレームワーク流行以前、あるいは再びフレームワークなしで開発する流れが少し人気を得る前後で、この機能が実運用コードでどれほど積極的に使われていたのか気になる。
古いものが再び新しくなる。最初のフロントエンドの仕事は、1999年にWebアプリケーションを作ることだった。
ツリービューのドラッグ&ドロップ、クリックして編集、事実上のシングルページアプリのような凝った機能まで、全部「Vanilla」JSで作った。
最も難しかったのは、NetscapeとInternet Explorer の違いをすり合わせることだった。
なぜ今ではそういうことのやり方を知らないように見えるのだろう ;]
最近いちばん気に入っている素のJavaScript機能は、Chrome 1.0やIEの時代からあった Element.insertAdjacentHTML だ。
とくにバックティックを使う複数行文字列と組み合わせると、有用というだけでは足りない。こんなに昔からあったのに、1年ほど前まで知らなかったのが信じられない。
insertAdjacentHTML(position, text)は、指定されたテキストをHTMLまたはXMLとしてパースし、結果のノードをDOMツリーの指定位置に挿入する。もともとのDOMだけでやるなら、
li要素を作り、a要素を作り、子として追加し、href属性を設定し、テキスト値を入れてからulに挿入しなければならなかった。あるいは大きな文字列にまとめてinnerHTMLで入れるしかなく、既存のリストを変更するのはさらに面倒で冗長だった。