目次
はじめに
Webブラウザ上での、Scalable Vector Graphics (SVG) のマウスイベントの処理について解説します。
SVGの仕様は多くの点でHTMLと親和性があり、JavaScriptによるマウスイベントの処理は、HTMLでの処理と同様になります。ただしSVGには、<use>, <symbol>, <marker>といったHTMLには無い位置付けの要素があり、それらに対するマウスイベントの処理には注意が必要です。(本稿はWebアプリケーションの一部としてHTML文書にインラインで定義されたSVGを対象にしています)
マウスイベント処理の基本
まずはおさらいも兼ねて、単純なケースでのマウスイベントの処理を確認します。
イベントの種類
マウスイベントの種類はHTML要素に対するものと同じです。
- click
- dblclick
- mousedown
- mouseup
- mouseover
- mouseenter
- mouseout
- mousemove
イベントリスナ
マウスイベントを処理するために、JavaScriptでイベントリスナをSVG要素に追加します。これはHTML要素に対するものと同じです。(以下、イベントリスナとあるのは、マウスイベントに対するイベントリスナを指します)
次の例では、四角形を描画する<rect>要素に、イベントリスナを追加しています。
<!doctype html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Mouse Event Test</title>
</head>
<script>
document.addEventListener("DOMContentLoaded", () => {
const rectA = document.getElementById("rectA");
rectA.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
});
</script>
<body>
<svg width="600" height="600" xmlns="http://www.w3.org/2000/svg">
<rect id="rectA" x="0" y="0" width="100" height="100" fill="green" />
</svg>
</body>
コンソールへの出力内容
console.log(`${e.currentTarget.id} / ${e.target.id} - click`);
- e.currentTarget.id – イベントリスナの登録されている要素のID
- e.target.id – イベントが検出された要素のID
Webブラウザでの実行結果を示します。
描画された四角形をクリックすると、コンソールに次のメッセージが出力されます。(コンソールはWebブラウザの開発者ツールで見ることができます)
rectA / rectA - click
イベントのバブリング
イベントのバブリングは、DOMツリーの下位の要素で発生したイベントが、上位の要素に伝播していくものです。SVGでもこのバブリングはサポートされています。
次の例では、<svg>要素にイベントリスナを追加しています。
<!doctype html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Mouse Event Test</title>
</head>
<script>
document.addEventListener("DOMContentLoaded", () => {
const rectA = document.getElementById("rectA");
rectA.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
const baseSVG = document.getElementById("baseSVG");
baseSVG.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
});
</script>
<body>
<svg id="baseSVG" width="600" height="600" xmlns="http://www.w3.org/2000/svg">
<rect id="rectA" x="0" y="0" width="100" height="100" fill="green" />
</svg>
</body>
Webブラウザでの実行結果を示します。
描画された四角形をクリックすると、コンソールに次の2つのメッセージが出力されます。まず<rect>要素のイベントリスナが呼び出され、次にその上位となる<svg>要素のイベントリスナが呼び出されていることが分かります。
rectA / rectA - click
baseSVG / rectA - click
四角形の外側をクリックすると、<svg>要素のイベントリスナのみが呼び出されます。
baseSVG / baseSVG - click
描画ツリー
SVG要素はHTML要素と共にDOMツリーに収容されます。SVG要素については、これとは別に描画ツリーが作られそこにも収容されます。ただし全てのSVG要素が対象ではなく、直接描画されるものが対象となります。
この直接描画できるものとして描画可能要素 (Renderable Element)という分類が定義されています。またその反対として決して描画されない要素 (Never-rendered Eelement)という分類が定義されています。ただし決して描画されない要素であっても、<marker>要素や<mask>要素のように、他の描画可能要素の一部として描画に影響する要素もあります。
<symbol>要素は特殊で両方に含まれています。これについては次の<use>要素のイベント処理で説明します。
描画可能要素のうち実際に描画されたものが描画ツリーに収容されます。そしてマウスイベントを受け取れるのは、この描画ツリーに収容されている要素になります。
描画可能要素であっても、次の条件に合うものは描画されず、描画ツリーには含まれません。
- スタイル属性として display: none が指定されている
- <switch>の条件により選択されていない
- 他の描画されていない要素の子孫である
例: <defs>要素の定義に含まれている場合
<use>要素のマウスイベント処理
<use>要素は別途定義されたSVG要素を参照して描画します。参照されるSVG要素は、<use>要素の子要素となりますが、通常の親子関係とは異なるためイベント処理でも注意が必要です。
例として次のようなSVGとJavaScriptのコードの処理をみてみます。
<!doctype html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Mouse Event Test</title>
</head>
<script>
document.addEventListener("DOMContentLoaded", () => {
const innerSvg = document.getElementById("innerSvg");
innerSvg.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
const outerSvg = document.getElementById("outerSvg");
outerSvg.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
const rectA = document.getElementById("rectA");
rectA.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
const useA = document.getElementById("useA");
useA.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
const useB = document.getElementById("useB");
useB.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
});
</script>
<body>
<svg id="outerSvg" width="100vw" height="100vh" xmlns="http://www.w3.org/2000/svg">
<defs id="defA">
<rect id="rectA" width="100" height="100" />
</defs>
<use id="useA" href="#rectA" x="100" y="0" fill="green" />
<svg id="innerSvg" x="100" y="125">
<use id="useB" href="#rectA" fill="blue" />
</svg>
</svg>
</body>
SVG要素を収容したDOMツリーは次のようになります。
Webブラウザでの実行結果を示します。
要素の再利用
<use>要素は、別途定義された描画可能要素を参照することで、再利用できます。例の場合は、要素①を要素②, ③として再利用しています。
シャドーツリー
<use>要素で参照される要素は、本来ならば<use>要素の子要素ではありません (XML構文上の子要素ではない)。しかし<use>要素の場合は、この参照先の要素を擬似的に子要素とみなし、シャドーツリーとしてDOMツリーに収容します。要素②, ③はこのシャドーツリーに該当します。
イベントリスナ
JavaScriptのコードでは、IDとしてrectAを持つ要素を探してイベントリスナを追加しています。これはDOM上の要素①に対して行われます。ただし要素①は実際には描画されず、マウスイベントを受け取ることもありません。
要素②, ③の属性は要素①から複製されます。また要素①の属性に変更が行われた場合、その変更は要素②, ③に同期されます。これにはイベントリスナへの参照を含みます。これにより要素①に追加したイベントリスナで要素②, ③に対するマウスイベントを処理することができます。
ただしイベントリスナの中では、要素②, ③のどちらに対して呼び出されたのか判別することができないので注意が必要です。判別が必要な場合は、<use>要素のイベントリスナで処理する必要があります。
ChatGPTとの問答
筆者は調べものによくChatGPTを利用しています。
イベントリスナの件についてChatGPTに質問したところ「要素②, ③にはイベントリスナを持つことができない」と答えてきました。しかし「W3Cの仕様書には、イベントリスナへの参照も含めて複製されると書かれている」と指摘したところ「そちらの方が正しい、ただしブラウザの実装によるので確認が必要」と答えを変えてきました。
こういう途中から訂正の入る問答はよく経験します。こちらの質問が悪いのか、ハルシネーションが起きてるのか、いずれにしろ回答の信頼性をどう評価するのか悩ましいところです。
因みに「ブラウザの実装による」については、Chrome, Firefox, Edge, Safariで、要素②, ③に対してイベントリスナが呼び出されることを確認しています。
イベントのバブリング
<use>要素はイベントバブリングの対象となります。例では要素②, ③から最上位の<svg>要素までそれぞれの経路を通ってイベントが伝搬します。
要素②をクリックした場合:
rectA / rectA - click
useA / useA - click
outerSVG / useA click
要素③をクリックした場合:
rectA / rectA - click
useB / useB - click
innerSVG / useB - click
outerSVG / useB - click
シャドウツリーとイベントターゲット
要素②, ③の出力例では、<use>要素以降ではイベントターゲット (イベントを検出した要素) が書き代わっています。
シャドウツリー内では、イベントターゲットは実際にイベントが発生した要素です。しかし<use>要素およびその上位の要素のイベントリスナでは、イベントターゲットは<use>要素となります。シャドウツリー内の要素の情報は隠されてしまいます。
コンテナ要素のマウスイベント処理
SVG要素にはコンテナ要素 (Container Element) に分類される要素があり、他のSVG要素を子要素として持つことができます。このうち描画可能要素 (Renderable Element) に分類されるものは、描画ツリーに収容され、イベントバブリングの対象となります。
<a>, <g>, <svg>, <switch>, <symbol>
ただし<symbol>要素は特殊で、描画対象となる (描画ツリーに収容される) のは、<use>要素の中で参照された場合のみです。
一方、コンテナ要素であっても描画ツリーに収容されないものもあります。これらはイベントバブリングの対象となりません。
<clipPath>, <marker>, <mask>, <pattern>
これらは特定の描画要素から参照されますが、その子要素とはならないため、描画ツリーには収容されません (これらは<use>要素から参照されることはありません)。したがってこれらはマウスイベントを受け取ることができません。
<g>要素とマウスイベント
<g>要素は複数の描画可能要素をまとめて描画します。<g>要素はイベントバブリングの対象となります。
例として次のようなSVGとJavaScriptの処理をみてみます。
<!doctype html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Mouse Event Test</title>
</head>
<script>
document.addEventListener("DOMContentLoaded", () => {
const outerSvg = document.getElementById("outerSvg");
outerSvg.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
const groupA = document.getElementById("groupA");
groupA.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
const rectA = document.getElementById("rectA");
rectA.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
});
</script>
<body>
<svg id="outerSvg" width="600" height="600" xmlns="http://www.w3.org/2000/svg">
<g id="groupA">
<rect id="rectA" width="50" height="50" fill="green" />
<rect id="rectB" x="100" width="50" height="50" fill="blue" />
</g>
</svg>
</body>
Webブラウザでの表示を示します。
rectA (緑色の四角形) をクリックすると次のように出力されます。
rectA / rectA - click
groupA / rectA - click
outerSvg / rectA - click
rectB (青色の四角形) をクリックすると次のように出力されます。
groupA / rectB - click
outerSvg / rectB - click
rectBにはイベントリスナは追加されていませんが、マウスイベントはrectBを経由してgroupAに送られていることが判ります。
<marker>要素とマウスイベント
<marker>要素は<line>要素などから参照され、参照元の端点を描画します。この<marker>要素はマウスイベントを受け取ることができません。
例として次のようなSVGとJavaScriptの処理をみてみます。
<!doctype html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Mouse Event Test</title>
</head>
<script>
document.addEventListener("DOMContentLoaded", () => {
const outerSvg = document.getElementById("outerSvg");
outerSvg.addEventListener("mousedown", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - mousedown`) });
const lineA = document.getElementById("lineA");
lineA.addEventListener("mousedown", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - mousedown`) });
const pathA = document.getElementById("pathA");
pathA.addEventListener("mousedown", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - mousedown`) });
const markerA = document.getElementById("markerA");
markerA.addEventListener("mousedown", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - mousedown`) });
console.log("Added event listners");
});
</script>
<body>
<svg id="outerSvg" width="600" height="600" xmlns="http://www.w3.org/2000/svg">
<defs id="defA">
<marker id="markerA" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="24" markerHeight="24"
orient="auto-start-reverse">
<path id="pathA" d="M 0 0 L 10 5 L 0 10 z" fill="black" stroke="black" style="opacity: 0.2" />
</marker>
</defs>
<line id="lineA" x1="10" y1="10" x2="250" y2="250" stroke="lightblue" stroke-width="5"
marker-end="url(#markerA)" />
</svg>
</body>
Webブラウザでの表示を示します。
例えばこの例で端点をマウスでドラッグして、ラインの端点を移動したいとします。このような場合、端点上でマウスボタンを押し下げたイベント (mousedown) を検出したくなるでしょう。
ではこの端点を描いている<marker>要素にイベントリスナを追加してはどうでしょうか? 残念ながら<marker>要素はその定義をしているだけで、描画ツリーには収容されません。このためイベントリスナは呼び出されません。
では<line>要素にイベントリスナを追加してはどうでしょうか? ライン上のマウスボタンの押し下げは検出できますが、あくまでライン上でのみ機能します。<marker>要素で描かれた端点のマウスイベントは検出できません。
ネット上調べてみると、<marker>要素上でマウスイベントを受け取る方法として、<marker>要素の上に同じ形で透明の図形を描画し、この透明な要素にイベントリスナを追加するという方法が紹介されています。(じゃあ最初から<marker>要素ではなくその要素で端点を描画しても良さそうですが …)
重なった要素のマウスイベント処理
SVGで描画した要素が重なっている場合に、下側の要素でマウスイベントを受け取りたい場合はないでしょうか? (特殊なユースケースかも知れませんが …)
HTMLの場合は、構文上入れ子になっている要素は、視覚的にも入れ子になって表示されます。このためイベントバブリングで上側 (子) の要素から下側 (親) の要素にたどることは可能です。しかしSVGの場合は、構文上入れ子になっていても、座標的にはバラバラであったり、逆に視覚的に重なっていても、構文上は無関係であったりします。また描画要素の多くは、そもそも入れ子になった定義を許していません。
このような場合に使える方法として、重なりの上側の要素に、次のように設定する方法があります。
pointer-events: none
noneが指定されると、その要素はマウスイベントを受け取らず、(存在すれば) 下側の要素がマウスイベントを受け取ることができます。
pointer-eventsを切り替えるサンプルを次に示します。rectA (緑色), rectB (青色) の領域が表示されています。rectAの一部はrectBと重なって隠されています。ボタンをクリックすると、rectB (青色の領域) の透過・非透過が切り替わります。
重なり領域をクリックすると、コンソールにクリックされた要素がログされます。
非透過 (ボタン表示は[透過へ])
rectB / rectB - click
透過 (ボタン表示は[非透過へ])
rectA / rectA - click
この場合rectB (青色の領域) はどこもクリックできません。
このサンプルのコードを次に示します。
<!doctype html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Mouse Event Test</title>
</head>
<script>
document.addEventListener("DOMContentLoaded", () => {
const rectA = document.getElementById("rectA");
rectA.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
const rectB = document.getElementById("rectB");
rectB.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
const buttonA = document.getElementById("buttonA");
buttonA.addEventListener("click", (e) => {
console.log("buttonA: click");
if (rectB.style.pointerEvents === "none") {
rectB.style.pointerEvents = "auto";
buttonA.textContent = "透過へ";
}
else {
rectB.style.pointerEvents = "none";
buttonA.textContent = "非透過へ";
}
});
});
</script>
<body>
<button id="buttonA">透過へ</button>
<br />
<svg width="600" height="600" xmlns="http://www.w3.org/2000/svg">
<rect id="rectA" x="0" y="10" width="100" height="50" fill="green" />
<rect id="rectB" x="0" y="10" width="50" height="100" fill="blue" />
</svg>
</body>
マウス位置のSVG座標への変換
SVGの上でマウスを操作して、その位置に応じて処理をしたい場合があります。例えばオブジェクトを掴んでドラッグ&ドロップするような場合です。
マウスの位置は、マウスイベントから取得できます。次の例では、取得したマウスの位置に円を描画して、マウスに円が追従するようにしています。
<!doctype html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Mouse Point Xform</title>
</head>
<script>
document.addEventListener("DOMContentLoaded", () => {
const svg = document.getElementById("svg");
svg.addEventListener("mousemove", e => {
const x = e.clientX;
const y = e.clientY;
const circle = document.getElementById("circle");
circle.setAttribute("cx", x);
circle.setAttribute("cy", y);
});
});
</script>
<body>
<div>上部のオフセット</div>
<br />
<svg id="svg" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="100%" height="100%" fill="lightyellow" />
<circle id="circle" cx="100" cy="100" r="15" fill="blue" />
</svg>
</body>
ただこのままだと、円は常にマウスの下方に離れた状態で描画されます。これはマウスイベントに含まれるマウスの位置は、DOM文書を収めるクライアント座標系でのマウスの位置であり、SVGのユーザー座標系のマウスの位置とは一致しないからです。(座標系についてはこちらを参照のこと)
クライアント座標系はブラウザの表示エリアの左上隅を原点とする座標系です。この例のように<svg>要素の前に別の要素 (<div></div><br />) がある場合は、対象の<svg>要素の原点はその分オフセットされます。またviewBoxを使ってスケーリングが行われている場合も、クライアント座標系と<svg>要素のユーザー座標系の座標は一致しません。
このためマウスイベントの位置情報から<svg>要素上の位置情報を得るには、クライアント座標系からSVGのユーザー座標系への変換を行う必要があります。これは次のように行います。
svg.addEventListener("mousemove", e => {
const svgPoint = new DOMPoint(e.clientX, e.clientY);
const windowPoint = svgPoint.matrixTransform(svg.getScreenCTM().inverse());
const circle = document.getElementById("circle");
circle.setAttribute("cx", windowPoint.x);
circle.setAttribute("cy", windowPoint.y);
});
matrixTransform() 関数は、異なる座標系の間の変換を行います。getScreenCTM()はSVGのユーザー座標系からクライアント座標系への変換を行う行列を返します。ここでは逆にクライアント座標系からユーザー座標系に変換したいため、逆行列を取得しています。
筆者は行列計算による座標変換を高校生の時に習いましたが (何十年も昔)、最近は行列計算はほとんど習わないと聞きました。拙宅の子供たちは「なにそれ」とか言ってますし … 読者の皆さんも同じでしょうか? かくいう筆者ももう中身は忘れました。幸い用意されている関数を使えば計算の中身は知らなくても良いので一安心です。
ネストされた<svg>要素のマウスイベント
<svg>要素でマウスイベントを処理したい場合があります。例えば描画された<rect>要素をマウスでドラッグしたい場合です。この場合は<rect>要素にmousemoveのイベントリスナを登録してもうまく行きません。マウスの移動に合わせて<rect>要素を移動したとしても、マウスの操作を素早く行うと<rect>要素の外にマウスが移動して、マウスの移動を検出できなくなる場合があるからです。このような場合、イベントリスナを背景の<svg>要素に登録することで、マウスの移動を取りこぼしなく検出できます。
<svg>要素でマウスイベントを検出できることを確認してみます。次のコードを実行すると、空の<svg>要素がブラウザに表示されます。
<!doctype html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Mouse Event Test</title>
</head>
<script>
document.addEventListener("DOMContentLoaded", () => {
const svgA = document.getElementById("svgA");
svgA.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
});
</script>
<body>
<svg id="svgA" width="400" height="400" xmlns="http://www.w3.org/2000/svg">
</svg>
</body>
この画面をクリックすると次のようなメッセージがログに出力されます。
svgA / svgA - click
つまり<svg>要素でマウスイベントが検出されていることが分かります。
ただしこの方法には注意が必要です。本来<svg>要素はグラフィック要素ではないため、それ自体はマウスイベントを検出できないからです。
実はW3CのSVG 2.0の仕様書には下記のように書かれています。
This specification does not define the behavior of pointer events on the outermost svg element for SVG images which are embedded by reference or inclusion within another document, e.g., whether the outermost svg element embedded in an HTML document intercepts mouse click events; future specifications may define this behavior, but for the purpose of this specification, the behavior is implementation-specific.
これによると最外側の<svg>要素がマウスイベントを検出できるかどうかは実装依存となっています。筆者が確認できた範囲では、Chrome, Edge, Firefox, Safariでマウスイベントが検出できました。つまり実装依存ながら現状は検出できている、ということになります。(これが偶々か必然かは見解の分かれるところかも知れません)
では<svg>要素が入れ子になっている場合はどうでしょうか?
次のコードでは2つの<svg>要素が入れ子になっており、各<svg>要素の左半分には矩形が描画されます。
<!doctype html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Mouse Event Test</title>
</head>
<script>
document.addEventListener("DOMContentLoaded", () => {
const svgA = document.getElementById("svgA");
svgA.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
const svgB = document.getElementById("svgB");
svgB.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
});
</script>
<body>
<svg id="svgA" width="600" height="600" xmlns="http://www.w3.org/2000/svg">
<rect id="rectA" width="50%" height="100%" fill="green" />
<svg id="svgB" y="200" width="100%" height="400">
<rect id="rectB" width="50%" height="100%" fill="yellow" />
</svg>
</svg>
</body>
それぞれの領域をクリックしたときにコンソールに出力される内容を次に示します。
子の<svg>要素では、右側の描画要素がない領域でクリックした場合は、最外側の<svg>要素で直接検出されています。左側の描画要素上でクリックした場合は、<rect>要素上でクリックが検出され、それがイベントのバブリングで内側の<svg>要素を経由して最外側の<svg>要素まで伝搬しています。
もし内側の<svg>要素上でマウスイベントを検出したい場合、このままでは描画要素の存在しない領域では検出ができません。
この問題の解決策は、<svg>要素の大きさに合わせて透明な<rect>要素を描画しておくことです。この<rect>要素にはイベントリスナを登録する必要はありません。この<rect>要素上でマウスイベントが検出されると、イベントバブリングで<svg>要素にイベントが伝達されます。
<!doctype html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Mouse Event Test</title>
</head>
<script>
document.addEventListener("DOMContentLoaded", () => {
const svgA = document.getElementById("svgA");
svgA.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
const svgB = document.getElementById("svgB");
svgB.addEventListener("click", (e) => { console.log(`${e.currentTarget.id} / ${e.target.id} - click`) });
});
</script>
<body>
<svg id="svgA" width="400" height="400" xmlns="http://www.w3.org/2000/svg">
<rect id="rectA" width="50%" height="100%" fill="green" />
<svg id="svgB" y="200" width="100%" height="200">
<rect id="wall" width="100%" height="100%" fill="transparent" />
<rect id="rectB" width="50%" height="100%" fill="yellow" />
</svg>
</svg>
</body>
それぞれの領域をクリックしたときにコンソールに出力される内容を次に示します。
終わりに
筆者は、ある技術を使いこなすには、その基本的な考え方や枠組みを理解することが、早道と思っています。そこでW3Cの仕様書を読んでみたのですが、SVGの仕様はなかなか手強い印象です。まあSVGの処理系を開発するわけではないので、そこまで深い理解がなくても大抵はOKなんですけど …
変更履歴
日付 | 内容 |
2024/08/23 | 「ネストされた<svg>要素のマウスイベント」を追加 サンプルコードを更新 (コンソール出力の形式など) |
2024/08/20 | 「マウス位置の座標変換」を追加 |
2024/08/19 | 「シャドウツリーとイベントターゲット」で要素②を参照としていたものを③に訂正 |
2024/07/13 | 初版リリース |