SVG nodes targeting
All IDs and classes of every SVG elements are prefixed with a unique string to avoid collisions. If you need this prefix, you can extract it like so:
import imageSrc, { prefix: imagePrefix } from "@/image.svg";This works with any import method.
One of the purposes of this is targeting SVG elements in scripts or styles.
The demo below changes element’s colors when user clicks on it. Each element has its own color map. This is implemented via accessing by a class name.
Rendered demo
Demo's source code
src
assets
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 256 86" style="enable-background:new 0 0 256 86;" xml:space="preserve" width="100%" height="100%"><g> <g> <rect class="left-element" x="8.9" y="6.6" width="114.8" height="72.8" fill="#d7bde2" /> <g> <g> <path d="M19.6,43.9c0-1.3,1-2,2.1-2c0.6,0,1.1,0.3,1.4,0.6l-0.3,0.4c-0.3-0.3-0.6-0.4-1.1-0.4c-0.9,0-1.5,0.6-1.5,1.5 c0,0.9,0.6,1.5,1.5,1.5c0.5,0,0.9-0.2,1.2-0.5l0.3,0.4c-0.4,0.4-1,0.6-1.5,0.6C20.5,45.9,19.6,45.2,19.6,43.9z"/> <path d="M25.6,44.5v-3.8h-1.4v-0.5h2v4.4c0,0.6,0.3,0.8,0.8,0.8c0.2,0,0.5-0.1,0.7-0.2l0.2,0.5c-0.4,0.2-0.6,0.2-1,0.2 C26.1,45.9,25.6,45.4,25.6,44.5z"/> <path d="M29.1,44.8c0-0.9,0.8-1.3,2.8-1.4c0-0.5-0.3-1-1-1c-0.5,0-1,0.2-1.3,0.5l-0.3-0.4c0.4-0.3,1-0.6,1.7-0.6 c1.1,0,1.6,0.6,1.6,1.6v2.4H32l-0.1-0.5h0c-0.4,0.3-1,0.6-1.5,0.6C29.7,45.9,29.1,45.5,29.1,44.8z M31.9,44.8v-1 c-1.6,0.1-2.1,0.5-2.1,1c0,0.4,0.4,0.6,0.8,0.6C31,45.4,31.4,45.2,31.9,44.8z"/> <path d="M33.8,45.3l0.3-0.4c0.4,0.3,1,0.5,1.7,0.5c0.6,0,1-0.3,1-0.6c0-0.3-0.2-0.5-1.2-0.7c-1-0.2-1.5-0.5-1.5-1.1 c0-0.6,0.5-1.1,1.6-1.1c0.6,0,1.2,0.2,1.6,0.5l-0.3,0.4c-0.4-0.2-0.8-0.4-1.3-0.4c-0.6,0-0.9,0.3-0.9,0.6c0,0.3,0.3,0.5,1.1,0.6 c1.3,0.3,1.6,0.6,1.6,1.2c0,0.6-0.6,1.1-1.7,1.1C34.9,45.9,34.2,45.6,33.8,45.3z"/> <path d="M38.5,45.3l0.3-0.4c0.4,0.3,1,0.5,1.7,0.5c0.6,0,1-0.3,1-0.6c0-0.3-0.2-0.5-1.2-0.7c-1-0.2-1.5-0.5-1.5-1.1 c0-0.6,0.5-1.1,1.6-1.1c0.6,0,1.2,0.2,1.6,0.5l-0.3,0.4c-0.4-0.2-0.8-0.4-1.3-0.4c-0.6,0-0.9,0.3-0.9,0.6c0,0.3,0.3,0.5,1.1,0.6 c1.3,0.3,1.6,0.6,1.6,1.2c0,0.6-0.6,1.1-1.7,1.1C39.7,45.9,39,45.6,38.5,45.3z"/> <path d="M43.4,42.1h3.4v0.5h-3.4V42.1z M43.4,43.8h3.4v0.5h-3.4V43.8z"/> <path d="M48.5,41.2l0-0.9h0.9l0,0.9L49.2,43h-0.4L48.5,41.2z M50.3,41.2l0-0.9h0.9l0,0.9L51,43h-0.4L50.3,41.2z"/> <path d="M54.2,44.5v-3.8h-1.4v-0.5h2v4.4c0,0.6,0.3,0.8,0.8,0.8c0.2,0,0.5-0.1,0.7-0.2l0.2,0.5c-0.4,0.2-0.6,0.2-1,0.2 C54.6,45.9,54.2,45.4,54.2,44.5z"/> <path d="M57.5,43.9c0-1.3,0.9-2,2-2c1.1,0,1.8,0.7,1.8,1.8c0,0.2,0,0.3,0,0.4H58v-0.5h2.9l-0.2,0.2c0-0.9-0.5-1.4-1.2-1.4 c-0.7,0-1.3,0.5-1.3,1.5c0,1,0.6,1.5,1.5,1.5c0.5,0,0.8-0.1,1.2-0.4l0.2,0.4c-0.4,0.3-0.9,0.5-1.5,0.5 C58.4,45.9,57.5,45.2,57.5,43.9z"/> <path d="M62.6,42l1.1,0h2.2v0.5h-3.4V42z M63.7,41.6c0-0.9,0.5-1.6,1.6-1.6c0.4,0,0.7,0.1,1.1,0.2l-0.2,0.5 c-0.3-0.1-0.6-0.2-0.9-0.2c-0.7,0-1,0.4-1,1v4.2h-0.6V41.6z"/> <path d="M68.1,44.3v-1.8H67V42l1.1,0l0.1-1.2h0.5V42h1.9v0.5h-1.9v1.8c0,0.7,0.2,1,1,1c0.4,0,0.6-0.1,0.9-0.2l0.1,0.5 c-0.3,0.1-0.7,0.2-1.2,0.2C68.5,45.9,68.1,45.3,68.1,44.3z"/> <path d="M71.9,43h3.4v0.5h-3.4V43z"/> <path d="M76.5,43.9c0-1.3,0.9-2,2-2c1.1,0,1.8,0.7,1.8,1.8c0,0.2,0,0.3,0,0.4H77v-0.5h2.9l-0.2,0.2c0-0.9-0.5-1.4-1.2-1.4 c-0.7,0-1.3,0.5-1.3,1.5c0,1,0.6,1.5,1.5,1.5c0.5,0,0.8-0.1,1.2-0.4l0.2,0.4c-0.4,0.3-0.9,0.5-1.5,0.5 C77.5,45.9,76.5,45.2,76.5,43.9z"/> <path d="M82.7,44.5v-3.8h-1.4v-0.5h2v4.4c0,0.6,0.3,0.8,0.8,0.8c0.2,0,0.5-0.1,0.7-0.2l0.2,0.5c-0.4,0.2-0.6,0.2-1,0.2 C83.2,45.9,82.7,45.4,82.7,44.5z"/> <path d="M86.1,43.9c0-1.3,0.9-2,2-2c1.1,0,1.8,0.7,1.8,1.8c0,0.2,0,0.3,0,0.4h-3.3v-0.5h2.9l-0.2,0.2c0-0.9-0.5-1.4-1.2-1.4 c-0.7,0-1.3,0.5-1.3,1.5c0,1,0.6,1.5,1.5,1.5c0.5,0,0.8-0.1,1.2-0.4l0.2,0.4c-0.4,0.3-0.9,0.5-1.5,0.5 C87,45.9,86.1,45.2,86.1,43.9z"/> <path d="M90.7,42h0.5l0.1,0.5h0c0.2-0.4,0.4-0.6,0.9-0.6c0.4,0,0.6,0.2,0.7,0.7c0.2-0.4,0.5-0.7,0.9-0.7c0.5,0,0.9,0.4,0.9,1.2 v2.8H94v-2.7c0-0.4-0.1-0.7-0.4-0.7c-0.3,0-0.4,0.2-0.6,0.6v2.8h-0.6v-2.7c0-0.4-0.1-0.7-0.4-0.7c-0.3,0-0.4,0.2-0.6,0.6v2.8 h-0.6V42z"/> <path d="M95.6,43.9c0-1.3,0.9-2,2-2c1.1,0,1.8,0.7,1.8,1.8c0,0.2,0,0.3,0,0.4H96v-0.5h2.9l-0.2,0.2c0-0.9-0.5-1.4-1.2-1.4 c-0.7,0-1.3,0.5-1.3,1.5c0,1,0.6,1.5,1.5,1.5c0.5,0,0.8-0.1,1.2-0.4l0.2,0.4c-0.4,0.3-0.9,0.5-1.5,0.5 C96.5,45.9,95.6,45.2,95.6,43.9z"/> <path d="M100.5,42h0.5l0.1,0.6h0c0.4-0.4,0.8-0.7,1.4-0.7c0.9,0,1.3,0.5,1.3,1.6v2.4h-0.7v-2.3c0-0.7-0.3-1.1-0.9-1.1 c-0.5,0-0.8,0.2-1.2,0.7v2.7h-0.7V42z"/> <path d="M106.2,44.3v-1.8h-1.1V42l1.1,0l0.1-1.2h0.5V42h1.9v0.5h-1.9v1.8c0,0.7,0.2,1,1,1c0.4,0,0.6-0.1,0.9-0.2l0.1,0.5 c-0.3,0.1-0.7,0.2-1.2,0.2C106.5,45.9,106.2,45.3,106.2,44.3z"/> <path d="M110.3,41.2l0-0.9h0.9l0,0.9L111,43h-0.4L110.3,41.2z M112.2,41.2l0-0.9h0.9l0,0.9l-0.2,1.8h-0.4L112.2,41.2z"/> </g> </g> </g> <g> <rect class="right-element" x="132.3" y="6.6" width="114.8" height="72.8" fill="#85c1e9" /> <g> <g> <path d="M140.4,43c0-1.3,1-2,2.1-2c0.6,0,1.1,0.3,1.4,0.6l-0.3,0.4c-0.3-0.3-0.6-0.4-1.1-0.4c-0.9,0-1.5,0.6-1.5,1.5 c0,0.9,0.6,1.5,1.5,1.5c0.5,0,0.9-0.2,1.2-0.5l0.3,0.4c-0.4,0.4-1,0.6-1.5,0.6C141.3,45,140.4,44.3,140.4,43z"/> <path d="M146.5,43.7v-3.8h-1.4v-0.5h2v4.4c0,0.6,0.3,0.8,0.8,0.8c0.2,0,0.5-0.1,0.7-0.2l0.2,0.5c-0.4,0.2-0.6,0.2-1,0.2 C146.9,45,146.5,44.6,146.5,43.7z"/> <path d="M149.9,44c0-0.9,0.8-1.3,2.8-1.4c0-0.5-0.3-1-1-1c-0.5,0-1,0.2-1.3,0.5l-0.3-0.4c0.4-0.3,1-0.6,1.7-0.6 c1.1,0,1.6,0.6,1.6,1.6v2.4h-0.5l-0.1-0.5h0c-0.4,0.3-1,0.6-1.5,0.6C150.5,45,149.9,44.6,149.9,44z M152.7,43.9v-1 c-1.6,0.1-2.1,0.5-2.1,1c0,0.4,0.4,0.6,0.8,0.6C151.8,44.5,152.3,44.3,152.7,43.9z"/> <path d="M154.6,44.4l0.3-0.4c0.4,0.3,1,0.5,1.7,0.5c0.6,0,1-0.3,1-0.6c0-0.3-0.2-0.5-1.2-0.7c-1-0.2-1.5-0.5-1.5-1.1 c0-0.6,0.5-1.1,1.6-1.1c0.6,0,1.2,0.2,1.6,0.5l-0.3,0.4c-0.4-0.2-0.8-0.4-1.3-0.4c-0.6,0-0.9,0.3-0.9,0.6c0,0.3,0.3,0.5,1.1,0.6 c1.3,0.3,1.6,0.6,1.6,1.2c0,0.6-0.6,1.1-1.7,1.1C155.7,45,155.1,44.8,154.6,44.4z"/> <path d="M159.3,44.4l0.3-0.4c0.4,0.3,1,0.5,1.7,0.5c0.6,0,1-0.3,1-0.6c0-0.3-0.2-0.5-1.2-0.7c-1-0.2-1.5-0.5-1.5-1.1 c0-0.6,0.5-1.1,1.6-1.1c0.6,0,1.2,0.2,1.6,0.5l-0.3,0.4c-0.4-0.2-0.8-0.4-1.3-0.4c-0.6,0-0.9,0.3-0.9,0.6c0,0.3,0.3,0.5,1.1,0.6 c1.3,0.3,1.6,0.6,1.6,1.2c0,0.6-0.6,1.1-1.7,1.1C160.5,45,159.8,44.8,159.3,44.4z"/> <path d="M164.2,41.2h3.4v0.5h-3.4V41.2z M164.2,42.9h3.4v0.5h-3.4V42.9z"/> <path d="M169.3,40.4l0-0.9h0.9l0,0.9l-0.2,1.8h-0.4L169.3,40.4z M171.2,40.4l0-0.9h0.9l0,0.9l-0.2,1.8h-0.4L171.2,40.4z"/> <path d="M174.2,41.1h0.5l0.1,0.9h0c0.4-0.6,1-1,1.7-1c0.3,0,0.5,0,0.8,0.2l-0.2,0.6c-0.3-0.1-0.4-0.1-0.7-0.1 c-0.6,0-1.1,0.3-1.6,1.1v2.2h-0.7V41.1z"/> <path d="M180.3,41.6h-1.8v-0.5h2.4v3.8h-0.7V41.6z M180,39.8c0-0.3,0.2-0.5,0.5-0.5s0.5,0.2,0.5,0.5c0,0.3-0.2,0.5-0.5,0.5 S180,40.1,180,39.8z"/> <path d="M183.1,45.7c0-0.3,0.2-0.6,0.6-0.9v0c-0.2-0.1-0.4-0.3-0.4-0.6c0-0.2,0.2-0.5,0.5-0.7v0c-0.3-0.2-0.5-0.5-0.5-1 c0-0.8,0.7-1.4,1.5-1.4c0.2,0,0.4,0,0.6,0.1h1.6v0.5h-1c0.2,0.2,0.3,0.5,0.3,0.8c0,0.8-0.7,1.3-1.5,1.3c-0.2,0-0.4,0-0.6-0.1 c-0.2,0.1-0.3,0.3-0.3,0.4c0,0.3,0.3,0.4,0.8,0.4h0.9c1,0,1.5,0.3,1.5,0.9c0,0.7-0.8,1.3-2.1,1.3 C183.8,46.7,183.1,46.3,183.1,45.7z M186.4,45.5c0-0.4-0.3-0.5-0.9-0.5h-0.8c-0.2,0-0.4,0-0.6-0.1c-0.4,0.2-0.5,0.4-0.5,0.6 c0,0.4,0.4,0.7,1.3,0.7C185.9,46.2,186.4,45.9,186.4,45.5z M185.8,42.4c0-0.5-0.4-0.9-0.9-0.9c-0.5,0-0.9,0.4-0.9,0.9 c0,0.6,0.4,0.9,0.9,0.9C185.4,43.3,185.8,42.9,185.8,42.4z"/> <path d="M188.1,39.3h0.7v1.5l0,0.9h0c0.4-0.4,0.8-0.8,1.4-0.8c0.9,0,1.3,0.5,1.3,1.6v2.4h-0.7v-2.3c0-0.7-0.3-1.1-0.9-1.1 c-0.5,0-0.8,0.2-1.2,0.7v2.7h-0.7V39.3z"/> <path d="M193.7,43.5v-1.8h-1.1v-0.5l1.1,0l0.1-1.2h0.5v1.2h1.9v0.5h-1.9v1.8c0,0.7,0.2,1,1,1c0.4,0,0.6-0.1,0.9-0.2l0.1,0.5 c-0.3,0.1-0.7,0.2-1.2,0.2C194.1,45,193.7,44.4,193.7,43.5z"/> <path d="M197.5,42.1h3.4v0.5h-3.4V42.1z"/> <path d="M202.1,43c0-1.3,0.9-2,2-2c1.1,0,1.8,0.7,1.8,1.8c0,0.2,0,0.3,0,0.4h-3.3v-0.5h2.9l-0.2,0.2c0-0.9-0.5-1.4-1.2-1.4 c-0.7,0-1.3,0.5-1.3,1.5c0,1,0.6,1.5,1.5,1.5c0.5,0,0.8-0.1,1.2-0.4l0.2,0.4c-0.4,0.3-0.9,0.5-1.5,0.5 C203,45,202.1,44.3,202.1,43z"/> <path d="M208.3,43.7v-3.8h-1.4v-0.5h2v4.4c0,0.6,0.3,0.8,0.8,0.8c0.2,0,0.5-0.1,0.7-0.2l0.2,0.5c-0.4,0.2-0.6,0.2-1,0.2 C208.7,45,208.3,44.6,208.3,43.7z"/> <path d="M211.6,43c0-1.3,0.9-2,2-2c1.1,0,1.8,0.7,1.8,1.8c0,0.2,0,0.3,0,0.4h-3.3v-0.5h2.9l-0.2,0.2c0-0.9-0.5-1.4-1.2-1.4 c-0.7,0-1.3,0.5-1.3,1.5c0,1,0.6,1.5,1.5,1.5c0.5,0,0.8-0.1,1.2-0.4l0.2,0.4c-0.4,0.3-0.9,0.5-1.5,0.5 C212.6,45,211.6,44.3,211.6,43z"/> <path d="M216.3,41.1h0.5l0.1,0.5h0c0.2-0.4,0.4-0.6,0.9-0.6c0.4,0,0.6,0.2,0.7,0.7c0.2-0.4,0.5-0.7,0.9-0.7 c0.5,0,0.9,0.4,0.9,1.2v2.8h-0.6v-2.7c0-0.4-0.1-0.7-0.4-0.7c-0.3,0-0.4,0.2-0.6,0.6v2.8H218v-2.7c0-0.4-0.1-0.7-0.4-0.7 c-0.3,0-0.4,0.2-0.6,0.6v2.8h-0.6V41.1z"/> <path d="M221.2,43c0-1.3,0.9-2,2-2c1.1,0,1.8,0.7,1.8,1.8c0,0.2,0,0.3,0,0.4h-3.3v-0.5h2.9l-0.2,0.2c0-0.9-0.5-1.4-1.2-1.4 c-0.7,0-1.3,0.5-1.3,1.5c0,1,0.6,1.5,1.5,1.5c0.5,0,0.8-0.1,1.2-0.4l0.2,0.4c-0.4,0.3-0.9,0.5-1.5,0.5 C222.1,45,221.2,44.3,221.2,43z"/> <path d="M226.1,41.1h0.5l0.1,0.6h0c0.4-0.4,0.8-0.7,1.4-0.7c0.9,0,1.3,0.5,1.3,1.6v2.4h-0.7v-2.3c0-0.7-0.3-1.1-0.9-1.1 c-0.5,0-0.8,0.2-1.2,0.7v2.7h-0.7V41.1z"/> <path d="M231.8,43.5v-1.8h-1.1v-0.5l1.1,0l0.1-1.2h0.5v1.2h1.9v0.5h-1.9v1.8c0,0.7,0.2,1,1,1c0.4,0,0.6-0.1,0.9-0.2l0.1,0.5 c-0.3,0.1-0.7,0.2-1.2,0.2C232.1,45,231.8,44.4,231.8,43.5z"/> <path d="M235.9,40.4l0-0.9h0.9l0,0.9l-0.2,1.8h-0.4L235.9,40.4z M237.7,40.4l0-0.9h0.9l0,0.9l-0.2,1.8h-0.4L237.7,40.4z"/> </g> </g> </g></g></svg>import imageSrc, { prefix as imagePrefix } from "@/assets/targeting-demo.svg";import { Fragment, MouseEventHandler, useMemo } from "react";
const leftElementClass = imagePrefix + "left-element";const rightElementClass = imagePrefix + "right-element";
function createColorGetter(colors: string[]) { let index = 0;
return () => { index++;
if (index === colors.length) { index = 0; }
return colors[index]; };}
export default function App() { const getLeftElementColor = useMemo(() => createColorGetter(["#ffd6d6", "#e8ffd6", "#d6efff"]), []); const getRightElementColor = useMemo(() => createColorGetter(["#f3d6ff", "#ffffd6", "#dad6ff"]), []);
const onClick: MouseEventHandler<HTMLDivElement> = (e) => { if (!(e.target instanceof SVGElement)) { return; }
if (e.target.classList.contains(leftElementClass)) { e.target.style.fill = getLeftElementColor(); } else if (e.target.classList.contains(rightElementClass)) { e.target.style.fill = getRightElementColor(); } };
return ( <Fragment> <p className="demo-section-caption">Click on the rectangles to change their colors:</p>
<div className="images"> <div dangerouslySetInnerHTML={{ __html: imageSrc }} onClick={onClick} className="standalone-image" /> </div> </Fragment> );}import React from "react";import ReactDOM from "react-dom/client";import App from "./App.tsx";
ReactDOM.createRoot(document.getElementById("app")!).render( <React.StrictMode> <App /> </React.StrictMode>,);<!doctype html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1" /> </head> <body> <div id="app"></div> <script type="module" src="/src/main.tsx" ></script> </body></html>{ "name": "react-svg-nodes-targeting", "version": "0.0.0", "private": true, "type": "module", "scripts": { "dev": "npm run build -- --watch", "build": "npm run type-check && npm run build-only", "build-only": "vite build --config vite.config.lib.ts", "type-check": "tsc --noEmit", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" }, "dependencies": { "vite-awesome-svg-loader": "*", "vite-file-tree-builder": "*" }}{ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true,
/* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx",
/* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true,
"baseUrl": ".", "paths": { "@/*": ["./src/*"] },
"types": [ // Add types for query imports (for example, "@/some/file.svg?src"). // Unfortunately, it's impossible to fully type it. Use plugin configuration instead. "vite-awesome-svg-loader",
"vite/client", // Vite types "vite-file-tree-builder" // Internal types ] }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }]}{ "compilerOptions": { "composite": true, "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true }, "include": ["vite.config.ts"]}import { fileURLToPath, URL } from "node:url";import { defineConfig } from "vite";import react from "@vitejs/plugin-react";
// Import vite-awesome-svg-loaderimport { viteAwesomeSvgLoader } from "vite-awesome-svg-loader";
export default defineConfig({ plugins: [ react(), viteAwesomeSvgLoader({ urlImportsInLibraryMode: 'emit-files' }), ], resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)), }, },});
Please open file to view its content