0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【実務で使えるSVG】JavaScriptでSVG要素を操作するチートシート

0
Posted at

🎯 やりたかったことは?

実務でSVGを扱っていると、画面の状態に応じて、

「特定の図形だけ色を変えたい」
「クリックされた要素へクラスを付けたい」
「JavaScriptからSVG要素を追加したい」

といった処理が必要になることがあります。

インラインSVGであれば通常のHTML要素と同じようにDOM操作できますが、属性・CSS・名前空間など、SVG特有の部分もあります。

よく使う操作を自分用のチートシートとしてまとめます。


🚀 急いでいる人向け

やりたいこと 例
IDで取得 document.getElementById("samplePath")
CSSセレクタで取得 document.querySelector(".shape")
複数取得 document.querySelectorAll(".shape")
属性を変更 element.setAttribute("fill", "red")
CSSを変更 element.style.fill = "red"
クラス追加 element.classList.add("is-selected")
クラス切り替え element.classList.toggle("is-selected")
クリックイベント element.addEventListener("click", handler)
SVG要素を作成 document.createElementNS(SVG_NS, "circle")
要素を削除 element.remove()
SVG座標系のサイズ取得 element.getBBox()
ブラウザ上の位置取得 element.getBoundingClientRect()

基本形はこれです。

const path =
    document.getElementById("samplePath");

path.setAttribute("fill", "#4CAF50");
path.setAttribute("stroke", "#333");
path.setAttribute("stroke-width", "3");

💡 この記事で分かること

  • JavaScriptからSVG要素を取得する方法
  • setAttribute / style / classList の使い分け
  • SVG要素へクリックイベントを付ける方法
  • 複数要素をまとめて操作する方法
  • JavaScriptから新しいSVG要素を作る方法
  • SVG座標とブラウザ座標の違い
  • <object> や <img> で読み込んだSVGとの違い

今回使用するSVG

次のインラインSVGを操作します。

<svg
    id="sampleSvg"
    width="320"
    height="180"
    viewBox="0 0 320 180"
    xmlns="http://www.w3.org/2000/svg">

    <rect
        id="statusRect"
        class="shape selectable"
        x="20"
        y="20"
        width="100"
        height="60"
        rx="8"
        fill="#e5e7eb"
        stroke="#333"
        data-status="normal" />

    <circle
        id="statusCircle"
        class="shape selectable"
        cx="210"
        cy="50"
        r="30"
        fill="#e5e7eb"
        stroke="#333"
        data-status="normal" />

    <g id="dynamicArea"></g>

</svg>

SVGをHTMLへ直接書いているため、通常のDOM APIで取得できます。


IDでSVG要素を取得する

const statusRect =
    document.getElementById("statusRect");

取得した要素は、通常のHTML要素と同じように操作できます。

statusRect.setAttribute("fill", "#4CAF50");

要素が存在しない可能性がある場合は、先に確認します。

const statusRect =
    document.getElementById("statusRect");

if (!statusRect) {
    return;
}

statusRect.setAttribute("fill", "#4CAF50");

querySelector で取得する

CSSセレクタを使って取得できます。

const firstShape =
    document.querySelector("#sampleSvg .shape");

複数の要素を取得する場合は querySelectorAll を使います。

const shapes =
    document.querySelectorAll("#sampleSvg .shape");
shapes.forEach((shape) => {
    shape.setAttribute("stroke-width", "3");
});

setAttribute() で属性を変更する

SVGの属性を変更する場合は setAttribute() が分かりやすいです。

const rect =
    document.getElementById("statusRect");

rect.setAttribute("fill", "#4CAF50");
rect.setAttribute("stroke", "#1B5E20");
rect.setAttribute("stroke-width", "3");
rect.setAttribute("opacity", "0.8");

座標やサイズも変更できます。

rect.setAttribute("x", "40");
rect.setAttribute("y", "30");
rect.setAttribute("width", "140");
rect.setAttribute("height", "80");

setAttribute() の値は文字列で渡します。


属性を取得する

const fill =
    rect.getAttribute("fill");

console.log(fill);

属性を削除する場合は removeAttribute() を使います。

rect.removeAttribute("opacity");

style から変更する

CSSプロパティとして変更することもできます。

rect.style.fill = "#4CAF50";
rect.style.stroke = "#333";
rect.style.strokeWidth = "3";

ハイフン付きのCSSプロパティは、JavaScriptではキャメルケースで書きます。

stroke-width
↓
strokeWidth

ただし、直接 style を変更するとインラインスタイルとして設定されます。

画面状態によって複数の見た目を切り替える場合は、次の classList の方が管理しやすいことがあります。


classList で状態を切り替える

CSSへ見た目をまとめておきます。

.selectable {
    fill: #e5e7eb;
    stroke: #333;
    stroke-width: 2;
}

.selectable.is-selected {
    fill: #4CAF50;
    stroke: #1B5E20;
    stroke-width: 4;
}

JavaScriptではクラスだけ切り替えます。

const rect =
    document.getElementById("statusRect");

rect.classList.add("is-selected");

削除する場合は、

rect.classList.remove("is-selected");

切り替える場合は、

rect.classList.toggle("is-selected");

です。

見た目の定義をCSS側へ寄せたい場合は、この方法が使いやすいです。


クリックイベントを登録する

const circle =
    document.getElementById("statusCircle");

circle.addEventListener("click", () => {
    circle.classList.toggle("is-selected");
});

クリックされた要素は、イベントの currentTarget からも取得できます。

function handleShapeClick(event) {

    const shape = event.currentTarget;

    shape.classList.toggle("is-selected");
}

circle.addEventListener(
    "click",
    handleShapeClick
);

複数の要素へ同じ処理を付ける場合に使いやすいです。

document
    .querySelectorAll(".selectable")
    .forEach((shape) => {

        shape.addEventListener(
            "click",
            handleShapeClick
        );
    });

SVG全体でイベントを受け取る

要素が多い場合は、SVG全体へイベントを登録する方法もあります。

const svg =
    document.getElementById("sampleSvg");

svg.addEventListener("click", (event) => {

    if (!(event.target instanceof Element)) {
        return;
    }

    const shape =
        event.target.closest(".selectable");

    if (!shape || !svg.contains(shape)) {
        return;
    }

    shape.classList.toggle("is-selected");
});

子要素が後から追加された場合でも、親SVG側でイベントを受け取れます。


data-* 属性を使う

SVG要素にも data-* 属性を付けられます。

<rect
    id="statusRect"
    data-status="normal"
    data-item-id="101" />

JavaScriptからは dataset で取得できます。

const rect =
    document.getElementById("statusRect");

console.log(rect.dataset.status);
console.log(rect.dataset.itemId);

値を変更する場合は、

rect.dataset.status = "selected";

です。

要素と業務データのIDなどを紐づけたい場合に使えます。


JavaScriptからSVG要素を作成する

SVG要素を作成するときは、SVG用の名前空間を指定します。

const SVG_NS =
    "http://www.w3.org/2000/svg";

円を作成する例です。

const circle =
    document.createElementNS(
        SVG_NS,
        "circle"
    );

circle.setAttribute("cx", "160");
circle.setAttribute("cy", "120");
circle.setAttribute("r", "20");
circle.setAttribute("fill", "#2196F3");
circle.setAttribute("stroke", "#333");

作成した要素をSVGへ追加します。

const dynamicArea =
    document.getElementById("dynamicArea");

dynamicArea.appendChild(circle);

document.createElement("circle") ではなく、

document.createElementNS(
    SVG_NS,
    "circle"
);

を使うのがポイントです。


作成した要素へイベントを付ける

circle.addEventListener("click", () => {
    circle.remove();
});

追加した円をクリックすると、その要素を削除します。


SVG要素を削除する

const rect =
    document.getElementById("statusRect");

rect.remove();

親要素から削除する場合は、

rect.parentNode.removeChild(rect);

でも削除できますが、通常は remove() の方が簡単です。


テキストを変更する

<text
    id="statusText"
    x="20"
    y="160">
    未選択
</text>
const statusText =
    document.getElementById("statusText");

statusText.textContent = "選択済み";

文字色も変更できます。

statusText.setAttribute(
    "fill",
    "#4CAF50"
);

getBBox() でSVG座標系のサイズを取得する

const rect =
    document.getElementById("statusRect");

const box =
    rect.getBBox();

console.log(box.x);
console.log(box.y);
console.log(box.width);
console.log(box.height);

getBBox() は、SVG内部の座標系を基準にした境界情報を取得するときに使います。

x
y
width
height

を取得できます。


getBoundingClientRect() でブラウザ上の位置を取得する

const clientRect =
    rect.getBoundingClientRect();

console.log(clientRect.left);
console.log(clientRect.top);
console.log(clientRect.width);
console.log(clientRect.height);

こちらはブラウザの表示領域を基準にした座標です。

整理すると、

メソッド 主な基準
getBBox() SVG内部の座標系
getBoundingClientRect() ブラウザの表示領域

マウス座標との比較や画面上の位置取得では、getBoundingClientRect() を使うことが多いです。


属性・style・classの使い分け

自分の場合は、だいたい次のように分けています。

方法 向いている用途
setAttribute() 座標・サイズ・個別属性の変更
element.style 一時的なインラインCSS変更
classList 状態ごとの見た目切り替え
dataset 要素に補助情報を持たせる

例えば、選択状態の見た目なら classList。

shape.classList.add("is-selected");

座標を変更するなら setAttribute()。

shape.setAttribute("x", "100");

という形にすると整理しやすいです。


⚠️ インラインSVG以外は取得方法が違う

この記事の例は、HTMLへ直接書いたインラインSVGです。

<svg>
    ...
</svg>

この場合は、

document.querySelector(...)

で直接取得できます。

一方、次のような読み込み方では扱いが変わります。

img要素

<img src="sample.svg" alt="">

img 要素として読み込んだSVGの内部要素は、親ページのJavaScriptから直接取得できません。

object要素

<object
    id="svgObject"
    type="image/svg+xml"
    data="sample.svg">
</object>

object の場合は、読み込み完了後に contentDocument などを経由して内部SVGへアクセスします。

この方法は別の記事で整理します。


⚠️ CSSに上書きされていないか確認する

JavaScriptから、

element.setAttribute("fill", "red");

としても、CSS側の指定によって想定した色にならないことがあります。

特に、

.shape {
    fill: blue;
}

のような指定がある場合は、DevToolsで最終的にどのスタイルが適用されているか確認します。

状態切り替えが目的なら、JavaScriptから値を直接書くより、クラスを切り替える方が追いやすいことがあります。


📝 まとめ

JavaScriptからSVGを操作するときは、まず次を覚えておけば大体対応できます。

要素取得
→ querySelector / getElementById

属性変更
→ setAttribute

見た目の切り替え
→ classList

イベント
→ addEventListener

要素作成
→ createElementNS

SVGもDOMとして操作できるため、基本的な考え方はHTML要素と同じです。

ただし、新しいSVG要素を作るときの名前空間や、object / img で読み込んだ場合の違いは忘れやすいところでした。

自分も実装時にすぐ確認できるよう、よく使う操作をまとめておきます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?