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";
export function main() { document.getElementById("app")!.innerHTML += ` <p class="demo-section-caption">Click on the rectangles to change their colors:</p>
<div class="images"> <div id="image" class="standalone-image"> ${imageSrc} </div> </div> `;
const createColorGetter = (colors: string[]) => { let index = 0;
return () => { index++;
if (index === colors.length) { index = 0; }
return colors[index]; }; };
const leftElementClass = imagePrefix + "left-element"; const rightElementClass = imagePrefix + "right-element"; const getLeftElementColor = createColorGetter(["#ffd6d6", "#e8ffd6", "#d6efff"]); const getRightElementColor = createColorGetter(["#f3d6ff", "#ffffd6", "#dad6ff"]);
document.getElementById("image")!.addEventListener("click", (e: MouseEvent) => { 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(); } });}<!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"> import { main } from "./src/main.ts"; main(); </script> </body></html>{ "name": "vanilla-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 --report-unused-disable-directives --max-warnings 0" }, "dependencies": { "vite-awesome-svg-loader": "*", "vite-file-tree-builder": "*", "vite-astro-entry-generator": "*" }}{ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", "lib": ["ES2020", "DOM", "DOM.Iterable"], "skipLibCheck": true,
/* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true,
/* 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"]}import { fileURLToPath, URL } from "node:url";import { defineConfig } from "vite";
// Import vite-awesome-svg-loaderimport { viteAwesomeSvgLoader } from "vite-awesome-svg-loader";
export default defineConfig({ plugins: [viteAwesomeSvgLoader({ urlImportsInLibraryMode: "emit-files" })], resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)), }, },});
Please open file to view its content