Any workaround to this Firefox bug? SVGElement.getScreenCTM incorrect when parent element has a transform - javascript

The Firefox bug in question is https://bugzilla.mozilla.org/show_bug.cgi?id=1610093
It's a long-standing issue whereby the getScreenCTM method of an SVG element returns incorrect values when a parent above the root SVG element has a transform applied.
The following snippet should show the black circle directly under the mouse pointer inside the red box. This works correctly in webkit/blink where the css transform on the parent is properly calculated. In Firefox this bug causes the black circle to be offset by the same values as the css transform on the parent div element.
I've avoided the issue up until now by ensuring there are no transforms further up the dom tree, but I'm now developing this as a component where I won't have control over the parent dom.
Does anyone know an easy workaround for getting a correct transform matrix?
const svgEl = document.querySelector(`svg#test-1`);
const circleEl = document.querySelector(`svg#test-1 circle`);
const handleMouseMove = (e) => {
// transform screen to SVG coordinate space
const mousePoint = svgEl.createSVGPoint();
mousePoint.x = e.clientX;
mousePoint.y = e.clientY;
const mouseCoordsInCanvasSpace = mousePoint.matrixTransform(svgEl.getScreenCTM().inverse())
// set circle to coords, should be mouse center in SVG coord space
circleEl.setAttributeNS(null, 'cx', mouseCoordsInCanvasSpace.x);
circleEl.setAttributeNS(null, 'cy', mouseCoordsInCanvasSpace.y);
}
window.addEventListener('mousemove', handleMouseMove);
body{
padding: 0px;
margin: 0px;
}
svg {
border: 2px solid red;
margin-left: 0px;
}
<!DOCTYPE html>
<html>
<body>
<div style="transform: translate(50px, 50px);">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100" id="test-1">
<circle r="10" />
</svg>
</div>
</body>
</html>

That is an annoying bug! My suggestion is to use an <object> element. The source/data for the object should be the SVG.
In the following example I'm using a data URL as a data source for the <object>, so that we at least can see something here on ST. But I imagine that the SVG should be dynamic somehow. So, to make this work you will need to load the SVG as a source of a file (data="/file.svg"). If you do that you have access to the contentDocument property on <object> and in that way you have direct access to the DOM of the SVG from the "outside". It needs to run from a web server and on the same domain as well.
body{
padding: 0px;
margin: 0px;
}
object {
border: 2px solid red;
margin-left: 0px;
}
<div style="transform: translate(50px, 50px);">
<object width="100" height="100" type="image/svg+xml" data="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgaWQ9InRlc3QtMSI+CjxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij4KPCFbQ0RBVEFbCnZhciBjaXJjbGVFbDsKY29uc3QgaGFuZGxlTW91c2VNb3ZlID0gZSA9PiB7CiAgY29uc3QgbW91c2VQb2ludCA9IG5ldyBET01Qb2ludChlLmNsaWVudFgsIGUuY2xpZW50WSk7ICAKICBjb25zdCBtb3VzZUNvb3Jkc0luQ2FudmFzU3BhY2UgPSBtb3VzZVBvaW50Lm1hdHJpeFRyYW5zZm9ybShlLnRhcmdldC5nZXRTY3JlZW5DVE0oKS5pbnZlcnNlKCkpOwogIC8vIHNldCBjaXJjbGUgdG8gY29vcmRzLCBzaG91bGQgYmUgbW91c2UgY2VudGVyIGluIFNWRyBjb29yZCBzcGFjZSAKICBjaXJjbGVFbC5zZXRBdHRyaWJ1dGVOUyhudWxsLCAnY3gnLCBtb3VzZUNvb3Jkc0luQ2FudmFzU3BhY2UueCk7CiAgY2lyY2xlRWwuc2V0QXR0cmlidXRlTlMobnVsbCwgJ2N5JywgbW91c2VDb29yZHNJbkNhbnZhc1NwYWNlLnkpOwp9OwoKZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGUgPT4gewogIGNpcmNsZUVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2MxJyk7CiAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignbW91c2Vtb3ZlJywgaGFuZGxlTW91c2VNb3ZlKTsKfSk7Cl1dPgo8L3NjcmlwdD4KPGNpcmNsZSBpZD0iYzEiIHI9IjEwIiAvPgo8L3N2Zz4="></object>
</div>
Here is the SVG I use in the <object>:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<script type="text/javascript">
<![CDATA[
var circleEl;
const handleMouseMove = e => {
const mousePoint = new DOMPoint(e.clientX, e.clientY);
const mouseCoordsInCanvasSpace = mousePoint.matrixTransform(e.target.getScreenCTM().inverse());
circleEl.setAttributeNS(null, 'cx', mouseCoordsInCanvasSpace.x);
circleEl.setAttributeNS(null, 'cy', mouseCoordsInCanvasSpace.y);
};
document.addEventListener('DOMContentLoaded', e => {
circleEl = document.getElementById('c1');
document.addEventListener('mousemove', handleMouseMove);
});
]]>
</script>
<circle id="c1" r="10" />
</svg>

Related

Detect what element of svg image within HTML Canvas is clicked using Javascript

I'm trying to work on a project where a 2d map can be moved around, zoomed and interacted with that is within a canvas. I have got the others to work apart from the interaction. I want to be able to detect when and what element is clicked from the svg (the svg is within a canvas).
I'm using some free svg that I found online. When I try to detect the clicking using the 'click' event I only receive the canvas. Is there an easy way to get the actual element that I clicked on from within the svg that is within the canvas?
As commented before: drawing an svg to a canvas and create clickable/interactive elements is rather complicated.
Alternative: make the svg itself zoomable
You should consider using the svg directly and add a svg pan and zoom library like svg-pan-zoom
Admittedly, you most certainly have to tweak all event listeners to get the desired behavior.
const map = document.querySelector("#svgMap");
const states = map.querySelectorAll(".land");
// Expose to window namespase for testing purposes
window.zoomTiger = svgPanZoom(map, {
zoomEnabled: true,
controlIconsEnabled: true,
fit: true,
center: true,
//preventMouseEventsDefault: true,
//onPan: onPanFn,
beforePan: beforePanFn,
//onUpdatedCTM: onUpdatedCTMFn,
onZoom: onZoomFn,
beforeZoom: beforeZoomFn
});
let isPanning = false;
function beforeZoomFn(){
console.log('beforeZoomFn')
isPanning = true;
}
function onZoomFn(){
console.log('onZoomFn')
setTimeout(e=>{
isPanning = false;
console.log('dragend after zoom')
},100)
}
function beforePanFn(){
isPanning = true;
console.log('pan')
}
map.addEventListener("mouseup", e=>{
setTimeout(e=>{
isPanning = false;
console.log('dragend')
},100)
});
states.forEach(function(state, i){
state.addEventListener('click', function(e){
if(!isPanning){
let current = e.currentTarget;
reset_colors(current);
current.classList.toggle('on');
}
})
})
function reset_colors(exclude){
const activeStates = map.querySelectorAll('.on');
activeStates.forEach(function(state, i){
if(state!==exclude){
state.classList.remove('on');
}
});
}
body {
padding: 5px 10px;
}
svg {
display: block;
height: 20em;
width:20em;
border:1px solid #ccc;
}
.land{
cursor:pointer;
fill:#ccc;
}
.land.on {
fill: red;
}
<!--
https://github.com/bumbu/svg-pan-zoom#demos
-->
<script src="https://cdn.jsdelivr.net/npm/svg-pan-zoom#3.5.0/dist/svg-pan-zoom.min.js"></script>
<svg id="svgMap" viewBox="0 0 600 500" class="svg-states">
<g id="states">
<path id="AU-NT" title="Northern Territory" class="land" d="M195.29,27.56l0.63,-1.15l-1.5,-1.01l0.76,-1l1.53,0.05l-0.03,-2.3l5.08,1.63l4.76,-0.32l1.42,0.45l1.75,-1.2l1.49,0.03l0.61,1.16l-0.37,0.8l0.66,-0.86l0,-1.68l1.45,-0.52l1.85,0.36l-1.2,-0.63l-0.12,-0.51l0.08,-3.17l0.61,-0.96l-2.19,-2l-1.27,-0.43l-3.1,0.96l-1.26,-1.76l-1.14,-0.61l-1.02,0.09l1.12,-1.54l1.2,-0.55l1.15,1.13l0.82,1.94l0.76,-0.56l-0.96,-2.37l1.41,0.26l0.7,-0.37l1.77,1.48l1.83,2.68l0.59,0.13l1.61,-2.05l0.47,0.04l2.88,4.18L222,17.2l0.55,0.8l1.54,0.65l1.26,-0.15l1.46,-1.11l1.18,0.2l-0.75,1.3l1.12,0.64l1.17,-0.75l1.47,1.12l0.23,1.27l1.77,-0.26l1.59,0.32l2.77,-0.78l1.08,1.5l2.31,1.49l1.6,0.31l1.05,-2.02l2.8,-0.15l0.83,-0.67l-0.87,0.23l0.12,-1.1l1.74,-0.25l1.38,-1.1l-0.04,-0.77l0.4,0.01l0.37,0.43l-2.18,2.1l0.46,0.54l-1.29,1.62l0.72,0.99l1.61,-1.42l1.51,-0.44l-0.92,1.16l0.37,0.56l0.8,-0.37l0.19,0.33l-0.87,1.26l0.61,1.04l2.18,-0.18l0.58,-0.45l0.76,-1.93l-2.02,-0.8l2.97,-2.35l1.21,-0.22l-0.25,0.83l1.46,3.03l0.91,-0.31l-0.69,-0.71l0.88,-0.22l1.1,0.56l1.11,1.5l-2.53,2.05l-1.1,1.77l-0.25,1.34l-1.37,-0.51l0.55,3.05l-0.95,2.19l-0.1,-0.35l-0.88,0.3l-0.12,-2.14l-1.7,2.05l-1.12,-0.6l-1.84,1.03l-0.39,0.67l0.47,0.65l-0.77,1.05l-0.2,1.25l0.89,1.61l1.07,-0.68l-1.4,5.06l-2.99,3.28l-0.9,2.33l-1.8,0.92l1.13,2.63l7.99,4.81l1.17,2.34l2.7,1.29L259,63.9l2.87,0.12l3.68,2.39l4,1.39l1.32,2.1l1.54,1.31l0,0l0,109.55l0,0l-97.64,0.07l0,0L174.7,52.34l0,0l2.01,1.22l0.06,2.26l0.71,-0.88l-0.29,-2.96l2.52,1.02l2.33,3.05l0.6,-0.23l-1.15,-1.79l0.23,-2.05l1.67,0.29l1.55,-1.07l-1.94,0.61l-0.98,-0.63l-0.96,-1.54l1.27,-0.29l0.75,-0.74L181.86,49l-1.3,-0.24l-1.97,-1.52l0.13,-0.89l1.33,-2.13l2.61,-1.64l1.04,-5.31l0.77,-0.8l0.48,0.91l1.05,-0.19l2.42,-2.05l-1.61,-1.97l0.27,-2.46l0.45,-0.2l0.47,0.43l1.11,-0.58l0.29,-2.45l1.68,-0.75l1,1.14l0.96,-0.07l-0.92,-0.27l-0.31,-1.17l0.37,-1.59l-0.33,-0.25l2.07,0.27L195.29,27.56zM195.97,19.82l-5.05,-3.27l-1.4,-4.28l0.3,-0.99l1.96,2.05l1.43,0.45l2.11,-0.91l1.14,0.49l1.6,-1.01l0.78,1.55l-0.33,-1.21l0.85,-1.12l1.59,0.64l1.2,1.58l0.09,0.83l-2.77,2.91L195.97,19.82zM260.56,44.63l0.64,-0.31l-0.28,1.4l-0.57,0.22l-5.55,-0.91l0.55,-3.66l0.31,-0.53l1.81,-0.55l-0.31,-0.7l0.91,-0.76l0.47,0.03l-0.5,0.81l0.4,1.16l1.03,0.18l0.8,-1.13l0.4,0.63l-1.64,1.47l-0.64,2.51l1.76,0.49L260.56,44.63zM190.91,18.71l-1.8,-0.69l-2.51,0.64l-0.78,-0.39l0.52,-1.37l0.91,0.32l0.4,-0.39l-0.21,-2.01l0.89,-1.55l0.96,-0.2l1.6,4l1.58,0.93l-0.25,0.55L190.91,18.71zM256.16,14.5l2.94,-4.79l-0.53,2.04l-1.77,2.48L256.16,14.5zM213.74,13.27l-1.02,-3.23l0.83,-0.32l0.52,1.71L213.74,13.27zM262.14,63.06l-1.3,-1.47l0.82,-1.19l0.9,1.94L262.14,63.06zM252.88,41.08l-0.31,-1.01l-0.11,0.72l-0.62,-0.21l0.1,-0.93l0.95,-0.75l0.95,0.73L252.88,41.08zM252.68,17.08l0,-0.51l0.99,-0.79l0.82,-0.04l1.44,-1.12L254.74,16L252.68,17.08zM256.39,61l-0.06,-1.08l0.93,-0.28l-0.19,1.23L256.39,61z"/>
<path id="AU-WA" title="Western Australia" class="land" d="M14.47,180.78l0.2,-1.84l-2.43,-2.57l-2.47,-5.41l-1.15,-0.7l-0.98,-2.78l0.03,-1.61l-2.44,-3.85l-0.06,-2.35l1.61,-5.6l0.75,-1.5l1.54,-1.21l0.27,-1.01l-0.09,-2.91l0.63,-1.92l-0.12,-1.14l-1.64,-3.85l3.01,-7.33l1.05,-1.56l1.44,-0.5l0.14,0.28l-1.07,4.09l0.7,1.48l-0.34,2.16l0.47,0.48l2.37,-1l3.02,-7.02l3.17,-1.76l1.19,-0.09l4.33,-1.9l1.82,-2.15l2.03,-1.1l1.29,-1.94l2.55,-1.19l0.97,-1.12l2.46,-0.73l2.52,-1.81l1.83,0.79l2.07,-1.11l2.99,1.26l3.72,-0.63l1.79,-0.78l3.56,-2.99l0.61,0.29l6.15,-1.02l1.94,-2.81l1.09,-0.83l1.18,-0.11l4.27,1.28l2.21,-1.14l5.99,-1.07l7.49,-3.28l2.71,-2.26l2.3,-3.11l1.64,-3.53l1.5,-2.02l0.25,-1.78l1.51,-0.37l4.32,-3.6l0.26,-1.43l-1.18,-0.54L101,86.33l-0.12,-2.14l-0.64,-1.66l0.37,-3.56l2.22,-2.79l0.74,-0.47l1.38,-0.01l-0.55,-1.23l0.46,-0.62l2.02,-0.31l0.09,-1.82l1.45,-1.11l0.28,-0.99l0.77,-0.28l0.74,0.74l-0.77,0.23l-0.36,1.25l1.52,1.46l5.2,9.95l-0.13,-2.6l0.6,-1.82l-0.49,-1.5l0.2,-0.86l0.69,-0.02l2.17,2.39l0.65,0.02l-0.91,-0.81l-0.39,-1.56l1.03,-1.18l-1.36,-0.06l-1.72,-2.42l0.06,-1.23l-1.22,0.02l-0.83,-0.75l2.34,0.34l0.74,-1.12l-0.02,-1.01l-1.6,-0.58l0.12,-1.31l1.69,-0.37l0.87,0.68l-0.8,0.68l1.72,1.34l0.79,-1.5l1.91,0.45l0.98,1.34l1.12,0.06l0.56,-0.64l0.99,0.48l4.53,0.12l-3.02,-0.89l-2.33,0.02l-0.27,-1.21l0.59,-1.34l0.44,-0.1l0.52,0.79l0.81,-0.56l0.24,-2.2l1.22,-1.23l-1.2,-0.07l-0.88,1.5l-1.46,-0.81l-0.31,-2.2l0.93,-2.13l1.35,0.43l0.81,-0.42l0.08,-2.42l0.46,-0.11l5.17,2.99l-1.14,-1.01l0.23,-1.44l-2.01,0.38l0.28,-1.2l0.73,0l0.43,-0.74l-1.76,0.85l-0.62,-0.86l0.82,-0.67l0.87,0.12l1.06,-1.24l0.94,1.82l-0.03,-1.44l1.74,1.36l1.23,-0.26l-2.73,-2.06l0.65,-0.52l-1.16,-1.72l2.17,-2.5l2.74,0.29l-0.1,-2.68l0.42,-0.96l1.2,0.57l-0.94,3.98l0.87,-2.52l1.31,0.67l-0.2,1.2l0.84,0.79l1.46,-1.43l1.18,-4.29l-0.71,-1.82l-0.69,-0.44l2.17,0.39l-0.09,0.84l-0.56,0.24l0.37,1.22l1.04,0.72l0.5,-2.03l1.83,-0.95l-0.51,1.25l1.58,1.69l2.05,-2.87l-0.47,-1.96l1.22,-0.49l1.06,-0.25l0.67,0.54l1.2,2.17l0.28,-0.79l2.94,0.6l1.04,1.47l1.49,1l3.25,4.52l0.47,-0.23l1.84,1.58l0.2,1.09l-1.29,3.36l0.04,3.31l-0.57,1.14l0.83,-0.72l0.38,-2.46l1.36,1.03l0.32,1.06l0.05,-1.08l-1.08,-2.67l1.24,-1.7l0.51,1.47l1.03,0.03l-0.66,-2.76l1.61,-0.46l5.04,1.26l0,0l0.06,128.49l0,0L174.7,251l0,0l-8.88,4.31l-9.21,3l-7.03,0.63l-5.51,-0.99l-1.2,0.59l-1.05,-0.23l-4.96,3.58l-2.31,0.67l-6.03,3.84l-5.04,1.13l-1.91,1.98l-1.63,5.43l-2.02,1.76l-0.38,1.1l-2.11,1.64l-1.87,-0.14l-2.18,1.61l-0.73,-1.72l-0.9,-0.38l-1.88,0.64l-5.33,0.08l-1,0.69l-0.12,0.66l-1.44,0.15l-0.42,-0.14l-0.09,-1.62l-0.84,-0.89l-2.31,0.97l-3.47,-1.07l-6.5,0.51l-3.5,0.79l-1.34,0.74l-4.47,-0.59l-2.08,0.64l-2.14,1.59l-1.61,3.03l-1.54,1.51l-1.27,0.75l-3.22,-0.67l-1.87,1.27l0.16,0.75l-0.38,0.43l-2.63,1.1l-2.12,2.39l-2.17,1.16l-2.65,0.48l0.21,0.55l1.5,0.33l-0.67,0.38l-2.25,-1.02l-1.02,0.28l-0.25,0.91l-3.01,-1.58L43.87,294l-0.82,0.54l-4.48,-0.75l-2.64,-1.76l-2.64,-0.61l-1.73,-2.78l-3.46,-2.94l-2.76,-1.12l-1.29,0.85l-1.23,-1.31l-0.6,-7.43l0.47,-2.18l2.43,1.68l1.92,-0.41l3.43,-4.63l-1.28,-7.64l1.09,-1.9l-0.68,1.62l0.94,1.56l0.47,-2.54l-0.61,-10.92l-7.15,-15.29l-0.26,-2.89l-0.94,-2.67l0.44,-7.12l-0.98,-3.49l-0.57,-1.35l-2.34,-2.78l-0.38,-2.93l-0.88,-1.78l-3.78,-4.87l-0.65,-2.93l0.5,-1.76l-0.21,-1.27l-2,-4.92l-3.81,-6.15l-3.31,-3.52l-0.61,-1.93l0.69,-2.54l0.5,3.02l0.34,-1.92l1.61,1.99l0.35,2.7l1.05,1.77l1.53,-0.61l0.78,-1.16l0.06,-2.06l-3.37,-3.15l-0.63,-2.6l-1.1,-1.78l0.85,-2.03l2.47,3.05l0.4,1.13l-0.57,1.75l0.35,1.95l0.92,-0.34l0.87,-1.68l0.87,3.6l1.2,1.61l1.36,-1.07l0.29,-0.97l-0.54,-1.32l0.37,-2.33L14.47,180.78zM3.24,182.51l-2.75,-4.33L0,176.3l0.51,-1.67l0.54,0.13l-0.17,0.86l2.39,5.66L3.24,182.51zM126.09,58.75l-0.58,-1.35l0.98,-0.72l0.94,1.57L126.09,58.75zM26.72,120.51L26,120.37l0.01,-0.61l1.47,-1.72l0.23,1.22L26.72,120.51zM132.78,49.85l-0.57,-0.31l0.07,-0.85l0.76,-1.17l0.5,0.56L132.78,49.85z"/>
<path id="AU-ACT" title="Australian Capital Territory" class="land" d="M392.28,306.01L391.02,305.37L391.02,305.37L389.29,302.43L389.31,300.59L389.83,298.03L392.68,295.93L396.19,298.12L393.88,298.81L393.36,299.53L392.74,302.02L392.9,305.16L392.9,305.16z"/>
<path id="AU-NSW" title="New South Wales" class="land" d="M402.43,327.48l-19.25,-9.7l-0.98,-0.08l0.99,-2.48l-1.68,-2.93l-0.56,-4.42l-1.18,-0.75l-1.82,-0.78l-0.93,0.36l-1.53,-0.29l-1.01,0.17l-1.01,1.26l-2.8,0.73l-1.46,-0.1l-4.52,-1.69l-2.2,0.99l-5.35,-0.78l-3.1,-2.19l-1.77,0.7l-1.31,-0.39l-2.7,0.35l-0.58,1.62l0.17,1.36l-2.34,0.43l-1.34,-0.78l-2.28,-2.67l-0.56,-1.28l-2.81,-2.59l-3.52,-2.26l-1.89,-0.68l-0.35,-1.69l-1.69,-0.72l-0.5,-1.1l-0.16,-4.01l-2.31,-1.26l-2.89,-0.62l-0.82,-0.81l-0.81,0.48l-0.84,2.17l-0.92,-0.18l-0.93,-2.25l-0.94,-0.87l-0.05,-1.88l-1.51,-2.54l-2.12,-1.16l-1.7,0.35l-1.61,-0.42l-2.1,1.48l-3.19,-1.83l-1.87,-0.14l-0.84,-0.6l0,0l0.05,-63.7l0,0l86.31,0l5.57,-4.95l1.2,-0.33l1.4,0.69l6.37,-1.08l1.88,1.44l3.45,-0.29l2.74,1.25l2.77,2.39l0.13,1.88l0.68,1.09l1.06,-0.68l0.98,-2l1.87,-0.83l1.07,1.1l2.08,-1l0.35,-2.01l-0.98,-2.22l2.27,-1.44l2.16,-0.71l1.28,-1.38l1.19,0.8l1.78,0.39l3.82,-0.09l0.83,-1.1l2.64,-0.73l0.81,-0.66l0,0l0.78,0.71l0.15,0.81l-0.35,2.67l0.7,2.08l-0.2,2.48l-1.71,2.24l-1.2,3.52l0.3,0.87l-1.21,6.89l-2.55,7.71l-0.14,3.35l0.75,1.43l-2.46,8.95l-3.66,6.04l0.19,3.48l-4.37,3.57l0.19,1.13l-3.61,1.52l-2.58,4.88l-1.3,0.64l-0.11,0.59l0.46,0.41l-0.35,1.39l-1.24,1.66l-0.73,4.43l-1.88,0.58l1.04,0.56l-2.83,3.59l-1.02,2.97l0.44,0.54l-0.54,2.46l-0.92,1.21l1.14,1.9l-0.17,0.88l-0.29,0.25l-0.36,-0.39l-0.02,-0.88l-0.98,0.4l0.2,1.12l-1.77,1.32l-4.1,9.49l-0.29,5.19l-0.63,0.82l-1.9,7.2l0.05,1.97l1.08,1.25l0.14,0.94l-0.01,0.45l-0.77,0.24L402.43,327.48zM392.74,302.02l0.62,-2.48l0.52,-0.73l2.31,-0.68l-3.5,-2.19l-2.85,2.1l-0.52,2.56l-0.02,1.85l1.73,2.94l0,0l1.35,0.59l0.53,-0.8L392.74,302.02z"/>
<path id="AU-SA" title="South Australia" class="land" d="M254.01,283.28l-1.08,1.74l-0.88,0.39l-0.93,1.59l-1.12,0.7l-0.67,1.33l-0.77,2.33l0.75,0.06l1.45,-1.02l0.15,0.69l-0.17,2.59l-0.52,0.22l-1.84,-1.95l-0.68,0.1l-0.48,1.13l-0.39,-0.01l-3.44,-4.32l-1.2,-0.78l-1.12,0.39l1.04,-2.08l0.05,0.82l1.42,1.52l0.57,0.39l1.08,-0.35l-1.23,-0.85l-1.31,-7.14l-2.47,-3.1l-2.09,-1.7l0.19,-2.15l-1.63,-3.81l-1.24,-0.48l-2.87,0.81l-0.66,-0.74l-2.11,-5.47l0.87,0.06l0.58,1.05l0.96,-1.67l-0.22,-1.14l-1.76,-1.73l-2.84,1.13l0.33,-1.76l0.73,-0.08l-1.35,-1.88l-1.78,-1.26l0.12,-0.47l-0.74,-0.23l-1.31,1.43l-3.46,-0.24l-4.14,-2.99l-2.18,-0.26l-2.79,1.3l-1.43,-0.14l-4.63,-3.88l-6.74,-3.15l-3.85,1.73l-7,-0.39L174.7,251l0,0l0.06,-70.18l0,0l97.64,-0.07l0,0l32.57,0L305,217.3l0,0l-0.05,63.7l0,0l-0.28,-0.37l-0.04,54.4l0,0l-2.93,0.11l-1.83,-0.98l-1.89,-1.9l-2.55,-4.52l-1.38,-0.52l-2.63,-3.98l0.34,-0.7l-0.54,-1.81l0.2,-1.25l1.19,-1.71l-0.42,-3.24l-1.9,-4.63l-3,-4l-3.14,-3.08l3.49,2.76l2.78,4.4l-1.02,-2.95l-5.07,-5.1l-0.02,-1.18l0.67,-0.27l0.82,0.71l0.07,1.9l1.02,-0.11l0.23,-4.21l-1.56,-0.77l-0.84,0.83l-1.79,0.39l-0.11,0.89l0.83,0.46l-0.49,0.63l-2.93,-0.25l-2.01,1.52l-3.92,0.21l-0.89,-0.67l0.65,-1.15l1.44,-0.72l1.66,-1.79l0.28,-3.05l0.53,-1.16l-0.2,-3.9l-2.4,-3.27l-0.6,-2.16l-1.38,-2.35l-0.93,1.6l0.13,1.14l-1.03,1.06l-0.38,1.31l-0.15,2.44l-1.38,5.56l-0.8,0.65l-2.75,-0.81l-2.04,0.77l-0.72,0.93l-1.51,-0.23l-1.47,0.97l-0.66,-0.69l1.14,-1.38l0.95,-3.24l0.91,0.36l1.89,-0.3l1.12,0.72l0.54,-0.29l0.7,-3.69l-0.06,-4.88l-0.45,-1.65l1,-0.85l0.64,-2.76l3.75,-4.22l-1.46,-3.64l0.33,-0.98l2.19,-0.94l-0.08,-0.66l-0.93,-0.89l-1.5,-5.68l-0.58,-0.44l-0.09,2.09l0.5,0.58l0.07,1.37l-0.36,1.86l-1.95,0.48l-1.16,1.26l-3.02,6.97l-0.74,0.62l-1.32,0.05l-0.77,-0.47l-0.93,1.64l-4.89,3.05L254.01,283.28zM271.44,303.46l1.65,0.45l0.54,1.42l-0.79,0.71l-1.29,-0.63l-1.74,-0.13l-1.53,0.72l-0.06,1.2l-1.73,1.01l-1.06,-1.08l-1.43,-0.27l-0.82,0.8l-4.75,0.17l-1.89,-1.86l0.46,-1.97l5.85,-1.43l2.14,-0.95l2.27,0.19l0.64,0.61l0.14,1.28l2.1,0.75l0.64,0.05L271.44,303.46z"/>
<path id="AU-VIC" title="Victoria" class="land" d="M398.1,330.52l-2.92,0.82l-10.86,0.36l-5.97,2.2l-4.2,3.51l-5.42,5.71l-4.8,0.91l-2.3,-0.02l-0.38,0.57l1.21,2.21l1.25,-0.88l0.23,-0.84l0.39,0.18l0.16,3.76l-0.97,0.99l-0.81,-0.69l-0.79,-2.07l-1.14,-1.43l-0.84,-0.36l-0.68,0.6l-1.07,-0.09l-0.96,-2.84l-0.96,-0.15l-1.31,0.58l-2.08,-1.91l0.18,-1.29l1.34,-1.08l-1.21,-2.03l-2.05,0.15l-0.46,1.54l-1.98,2.04l-1.4,0.12l-1.52,-1.77l2.43,-0.51l1.59,-2.8l-0.35,-1.29l-1.97,-2.39l-4.24,3.19l-1.72,0.35l0.06,0.5l1.5,0.26l1.35,-0.65l0.75,0.21l0.05,1.01l-0.56,0.86l-3.21,0.54l-3.27,2.21l-2.38,2.93l-1.2,0.33l-2.05,1.93l-5.37,-3.08l-2.66,-0.85l-2.91,-2.25l-1.69,-0.67l-2.38,0.5l-2.12,-1.49l-2.32,-0.34l-1.42,0.75l0.33,1.08l-0.86,0.24l-1.27,-0.62l-2.83,-2.92l-2.44,-1.32l0,0l0.04,-54.4l0.28,0.37l0,0l0.84,0.6l1.87,0.14l3.19,1.83l2.1,-1.48l1.61,0.42l1.7,-0.35l2.12,1.16l1.51,2.54l0.05,1.88l0.94,0.87l0.93,2.25l0.92,0.18l0.84,-2.17l0.81,-0.48l0.82,0.81l2.89,0.62l2.31,1.26l0.16,4.01l0.5,1.1l1.69,0.72l0.35,1.69l1.89,0.68l3.52,2.26l2.81,2.59l0.56,1.28l2.28,2.67l1.34,0.78l2.34,-0.43l-0.17,-1.36l0.58,-1.62l2.7,-0.35l1.31,0.39l1.77,-0.7l3.1,2.19l5.35,0.78l2.2,-0.99l4.52,1.69l1.46,0.1l2.8,-0.73l1.01,-1.26l1.01,-0.17l1.53,0.29l0.93,-0.36l1.82,0.78l1.18,0.75l0.56,4.42l1.68,2.93l-0.99,2.48l0.98,0.08l19.25,9.7l0,0l-2.09,0.65l-1.22,1.86L398.1,330.52zM352.26,340.07l-0.78,-0.3l0.13,-1.52l1.74,0.36l0.44,0.52L352.26,340.07zM352.24,341.99l-0.9,-0.68l-1.64,0l0.76,-0.74l1.01,-0.2L352.24,341.99z"/>
<path id="AU-QLD" title="Queensland" class="land" d="M402.43,140.14l0.66,0.82l-1.28,-3.39l0.85,-2.22l0.86,0.03l1.32,2.67l4.26,2.27l-0.64,-2.37l0.3,-0.61l0.79,0.14l0.77,1.15l-0.24,1.31l1.47,2.09l-0.69,4.67l0.61,1.26l-0.13,1.68l0.72,1.46l1.69,0.61l1.57,2.78l1.13,0.41l1.66,1.78l1.33,0.68l-0.09,0.6l0.83,-0.4l0.28,-0.89l0.48,0.14l2.71,2.71l2.15,4.63l3.65,2.42l0.43,2.21l1.78,2.95l2.57,0.52l0.42,1.76l-0.58,1.51l0.29,2.07l0.69,0.44l0.58,1.42l1.6,0.99l-1.23,4.03l0.92,9.36l-1.33,1.14l0.33,1.66l2.08,2.02l0.68,2.33l0.92,1.29l0.32,0.87l-0.24,1.63l1.04,2.06l0,0l-0.81,0.66l-2.64,0.73l-0.83,1.1l-3.82,0.09l-1.78,-0.39l-1.19,-0.8l-1.28,1.38l-2.16,0.71l-2.27,1.44l0.98,2.22l-0.35,2.01l-2.08,1l-1.07,-1.1l-1.87,0.83l-0.98,2l-1.06,0.68l-0.68,-1.09l-0.13,-1.88l-2.77,-2.39l-2.74,-1.25l-3.45,0.29l-1.88,-1.44l-6.37,1.08l-1.4,-0.69l-1.2,0.33l-5.57,4.95l-86.31,0l0,0l-0.02,-36.54l-32.57,0l0,0l0,-109.55l0,0l3.15,2.11l3.79,0.42l2.21,1.09l1.82,0.26l1.42,1.97l0.23,1.46l1.06,1.58l1.92,0.41l1.95,1.6l2.15,0.62l2.22,1.56l4.1,-0.45l4.74,-2.51l1.34,-5.09l2.58,-3.35l1,-2.26l1.52,-4.82l-0.28,-1.89l0.65,-4.14l2.23,-5.7l-1.07,-2.94l-0.49,-3.15l0.86,-3.76l-1.33,-2.17l-0.09,-1.34l0.79,-3.28l1.59,-3.09l-1.03,-3.28l1.67,-1.47l0.53,-1.47l0.65,0.2l0.82,2.19l0.09,-0.47l-0.61,-1.78l-1.8,-2.33l0.32,-0.26l-0.93,-0.21l0.25,1.11l-0.63,0.2l-0.36,-0.42l1.97,-3.51l0.8,-2.48l0.47,-0.21l0.27,1.32l1.16,-0.23l-0.84,-1.58l1.91,-6.08l0.27,-4.66l2.12,-0.59l1.1,-2.04l1.83,0.45l-1.05,1.18l-0.08,1.01l1.08,-0.85l1.47,1.25l0.47,1.03l0.84,3.42l-0.07,4.87l1.18,1.04l1.46,-0.26l1.04,0.93l-1.05,1.69l-0.27,2.14l2.15,0.87l0.04,1.16l1.64,1.1l-0.73,2.57l1.65,0.33l0.08,4.71l0.79,1.42l-0.69,3.6l0.77,2l0.92,0.97l1.05,4.34l1.14,0.82l1.35,0.01l4.59,-2.6l0.45,-1.01l0.43,0.1l0.68,1.02l0.39,2.47l0.68,0.81l2.39,0.68l0.68,1.51l3.87,2.14l-0.88,2.11l0.6,2.2l-0.44,1.29l1.21,3.23l0,2.03l1.12,1.88l-0.65,4.02l4.39,5.32l1.36,-0.47l0.26,0.32l-0.79,1.95l1.62,3.31l0.79,3.7l-0.11,2.99l-1,2.12l0.08,1.09l2.2,2.88l1.32,0.5l-0.61,4l3.12,2.93l2.15,0.49l1.33,1.33l2.02,0.45l0.73,0.69l3.18,0.11l-0.33,-1.19l1.67,2.6l0.43,2.2l0.81,1.12l1.09,0.02l-0.09,-1.33l0.66,0.01l0.98,2.1l3.34,0.61l0.86,1.04l-0.26,0.44l1.51,1.49l0.57,-0.25l-0.15,-1.39l2.39,1.75l1.04,0.19l1.8,3.48l-1.56,-0.9l-0.53,0.22l-0.8,0.99l0.67,1.8l5.27,4.19l0.27,2.3l2.2,3.52l-0.14,2.21l0.99,3.54l1.91,3.19l-0.29,1.69l1.58,-1.3L402.43,140.14zM436.13,178.32l-0.91,-0.7l-0.59,-2.15l1.32,-2.63l-0.16,-1.29l2.07,-2.83l-0.03,-0.94l-0.86,-1.18l1.33,-1.38l0.09,2.44l0.88,1.31l-2.65,6.24L436.13,178.32zM284.87,73.53l0.18,-1.81l1.54,-1.55l2.47,-0.55l2.09,0.34l0.06,0.66l-0.47,0.22l-1.18,-0.23l-1,0.4l-0.51,1.46L284.87,73.53zM416,154.42l-1.75,-2.14l-0.52,-1.75l0.48,-0.11l1.73,1.31l0.83,2.26L416,154.42zM362.4,93.15l-0.75,-0.29l-0.21,-1.04l-1.25,-1.39l1.99,0.61l0.9,1.3L362.4,93.15zM439.66,201.72l0.29,-3.84l1.17,0.02l-0.94,3.76L439.66,201.72zM439.86,197.24l-0.72,-2.02l0,-1.64l0.93,-0.51L439.86,197.24zM317.82,6.96l-0.68,-0.55l-0.05,-0.69l1.1,-0.52l0.46,1.21L317.82,6.96zM318.87,1.42l-0.98,-0.67l0.42,-0.64l0.6,-0.11l0.61,0.61L318.87,1.42zM287.91,77.87l-0.31,-0.65l1.11,-1.04l0.56,0.4l0.18,0.83l-1.28,-0.03L287.91,77.87z"/>
<path id="AU-TAS" title="Tasmania" class="land" d="M369.2,414.49l-0.9,0.45l-0.2,-0.45l-1.4,-0.22l-0.94,-0.71l-3.29,-0.32l-0.7,-0.68l-1.31,0.4l-0.78,-0.26l-1.15,-1.81l0.64,-0.45l2.61,0.67l0.02,-0.98l-0.77,-0.64l-0.42,0.76l-3.12,-0.54l-2.62,-4.71l-0.91,-0.2l-0.56,-0.9l-1.15,-3.62l-1.02,-0.68l-0.59,-5.21l0.2,-0.26l2.2,1.98l0.47,2.18l0.9,-2.52l-1.02,-0.36l-1.93,-2.12l-0.35,-2.13l-3.18,-4.67l-2.92,-7.17l-0.73,-4.09l0.88,-0.8l0.02,-1.66l0.66,-0.44l2.43,0.29l1.42,1.05l1.72,-0.28l9.97,5.07l3.32,-0.1l0.94,0.53l-0.05,-0.5l1.62,-1.17l0.57,0.18l0.24,0.95l1.48,0.27l-1.3,-0.84l-0.05,-0.69l2.15,-1.2l1.41,0.47l2.27,-0.46l0.66,0.59l0.78,-0.47l1.34,-2.04l0.69,-0.17l1.77,0.86l0.69,-0.4l0.76,-1.69l1.17,0.34l2.1,1.89l0.6,1.35l-0.69,2.46l0.57,2.41l-0.47,1.72l0.44,2.29l-0.54,2.92l1.08,5.94l-0.57,0.81l-0.39,-0.33l0.37,-1.68l-1.24,-2.84l-1.26,2.5l-0.82,5.93l-0.65,1.34l0.12,1.52l-0.78,0.81l-0.44,1.48l0.62,0.2l0.12,-0.68l0.96,0.98l-0.52,2.59l0.48,2.15l-1.04,-0.66l-1.19,0.94l-1.72,-2.69l-0.14,-0.71l0.95,-1.17l0.82,1.67l1.28,-0.36l-0.8,-1.4l-2.91,-1.5l-0.6,0.45l0.55,1.75l-0.29,0.58l-1.07,0.35l-0.21,-2.25l-0.98,-0.63l0.34,0.86l-1.16,3.39l0.06,2.01l-0.75,0.19l-0.87,-0.56l-0.94,-1.87l-0.46,0.27l-0.01,1l1.37,1.25L369.2,414.49zM382.36,365.85l-0.98,-0.54l-0.22,-1.36l-1.24,-2.33l-1.55,-1.38l1.32,-1.74l0.93,-0.4l2.15,2.87l1.33,0.92l0.47,3.17L382.36,365.85zM336.69,363.9l-0.38,-0.28l0.06,-1.87l-0.62,-0.81l0.03,-0.77l0.19,-2.27l0.85,-0.63l0.02,-1.18l0.46,-0.13l1.42,1.24l0.41,3.76l-0.44,1.51L336.69,363.9zM384.64,369.1l-0.23,-0.97l-2.52,0.32l-0.97,-0.42l0.02,-0.55l0.72,-0.59l1.49,0.19l1.52,-0.77l1.48,1.76L384.64,369.1zM373.39,412.93l-1.38,-0.17l-0.57,-1.19l1.92,-2.37l0.7,1.83L373.39,412.93zM499.07,600l-0.48,-0.05l-0.01,-1.54l0.62,-2.78l0.75,-0.81L499.07,600zM374.03,409.2l-0.75,-1.57l0.7,-1.22l0.82,2.48L374.03,409.2zM381.12,401.66l0.05,-1.96l1.19,-0.43l0.44,1.11l-0.76,0.01L381.12,401.66zM347.19,372.32l-0.25,-0.83l0.65,-0.77l0.98,1.12L347.19,372.32z"/>
</g>
</svg>

How can I access an SVG element embedded in a CSS `background` from JavaScript?

Given this HTML and CSS that embeds an SVG as a background:
<div class="box"></div>
.box {
width: 300px;
height: 200px;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><rect id="rect" width="100" height="100" fill="red"><animate attributeName="x" from="0" to="400" dur="6s" repeatCount="indefinite"/></rect></svg>');
}
I want to use JavaScript to access #rect like so:
// Wait for the SVG to load.
window.onload = function() {
// ...but this still logs null
console.log(document.getElementById("rect"));
}
But this prints null in the console. Is there any way to get a reference to this <rect> via JavaScript?
When loaded as a CSS <image> (like through background-image but also through content and other CSS properties), your SVG document is loaded in an external environment, that scripts don't have access to, just like it is in an HTML <img> by the way.
For all that matters you can even forget there is an SVG Document loaded at all here.
If you want to modify the image, you need to edit the URL directly:
const box = document.querySelector(".box");
// rule is a string
const rule = getComputedStyle(box).backgroundImage;
box.style.backgroundImage = rule.replace("red", "blue");
.box {
width: 300px;
height: 200px;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><rect id="rect" width="100" height="100" fill="red"><animate attributeName="x" from="0" to="400" dur="6s" repeatCount="indefinite"/></rect></svg>');
}
<div class="box"></div>

How to get javascript mouseover to highlight other elements in an svg file (context change)

I'm trying to solve a very simple problem: two elements; a button and a rectangle. When you hover over the button, the rectangle changes fill colour. I've tried CSS, but for some reason i can't get the 'className:hover targetClass{}' to work.
The main body is from Visio, and i've tried to edit it (*Edit:removed visio metadata):
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
width="8.26772in" height="11.6929in" viewBox="0 0 595.276 841.89" xml:space="preserve" color-interpolation-filters="sRGB" class="st3">
<style type="text/css">
<![CDATA[
.st1 {fill:#5b9bd5;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st2 {fill:#ffffff;font-family:Calibri;font-size:0.833336em}
.st3 {fill:#FFFF00;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
.button {fill:#5b9bd5;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.button:hover{fill: #FFFF00;}
]]>
</style>
<g id="ThePage">
<title>Page-1</title>
<g id="shape2" transform="translate(283.465,-531.496)">
<title>Multi-Attachment rectangle.2</title>
<desc>Button text</desc>
<rect x="0" y="799.37" width="56.6929" height="42.5197" class="button"/>
<text x="5.25" y="823.63" class="st2" >Button text</text>
</g>
<g id="shape1" transform="translate(255.118,-595.276)">
<title>Multi-Attachment rectangle</title>
<desc>Big box text</desc>
<rect x="0" y="756.85" width="113.386" height="85.0394" class="st1"/>
<text x="32.78" y="802.37" class="st2" >Big box text</text>
</g>
</g>
</svg>
My javascript code that I tried to implement but failed was:
var javaButton = document.getElementsByClassName('button');
for (var i = 0; i < javaButton.length; i++) {
javaButton[i].addEventListener('mouseover', mouseOverEffect);
javaButton[i].addEventListener('mouseout', mouseOutEffect);
}
function mouseOverEffect() {
this.classList.add("rect-highlight");
}
function mouseOutEffect() {
this.classList.remove("rect-highlight");
}
Where "rect-highlight" is in the CSS part of the svg as: .rect-highlight {fill: #ec008c;}.
I know this works, as the button changed colour if I hover over it, however need to get the context from "this" to another element. How do I do that?
*Edit: removed visio metadata
**Edit : added code body
***Edit: added code fences so markdown doesn't eat my code.
Not sure what your issue is, since you did not provide a sample SVG, but if you want to highlight related elements you can use a :hover selector.
You can get the previous and next sibling of the current element by accessing previousElementSibling and nextElementSibling respectively.
const showSiblings = (e) => toggleSiblings(e.target, true);
const resetSiblings = (e) => toggleSiblings(e.target, false);
Array.from(document.querySelectorAll('.box')).forEach(box => {
box.addEventListener('mouseenter', showSiblings);
box.addEventListener('mouseleave', resetSiblings);
});
function toggleSiblings(curr, visible) {
let prev = curr.previousElementSibling;
let next = curr.nextElementSibling;
if (prev) {
prev.classList.toggle('sib-prev', visible);
}
if (next) {
next.classList.toggle('sib-next', visible);
}
}
svg { background: #FF7F7F; }
.box { stroke-width: 4; cursor: pointer; }
.foo { fill: #4C4C7F; stroke: #00007F; }
.foo:hover { fill: #7F7FFF; }
.bar { fill: #7F7F4C; stroke: #7F7F00; }
.bar:hover { fill: #FFFF7F; }
.sib-prev.box {
stroke: #FF0000;
}
.sib-next.box {
stroke: #00FF00;
}
<svg width="260" height="196">
<rect class="box foo" x="6" y="6" width="120" height="40" />
<rect class="box bar" x="6" y="54" width="120" height="40" />
<rect class="box foo" x="6" y="102" width="120" height="40" />
<rect class="box bar" x="6" y="150" width="120" height="40" />
<rect class="box bar" x="134" y="6" width="120" height="40" />
<rect class="box foo" x="134" y="54" width="120" height="40" />
<rect class="box bar" x="134" y="102" width="120" height="40" />
<rect class="box foo" x="134" y="150" width="120" height="40" />
</svg>
When you really need a javascript solution, go for the answers with javascript.
This answer, however, shows a simple non-JS solution using vanilla CSS. You'll see a button and a SVG square. By simply using CSS :hover and :focus on both button and svg the color of the square changes 4 times...
/*
CSS Selector Reference
https://www.w3schools.com/cssref/css_selectors.asp
*/
svg { fill: red; width: 100px }
svg:hover { fill: purple }
button:hover + svg { fill: lime } /* + = immediate sibling selector */
button:focus + svg { fill: blue }
<button>hover, click and unfocus me</button>
<svg viewbox="0 0 100 100"><rect width="100" height="100" x="0" y="0" /></svg>
I wanted to fiddle around with this, because I have an interest in doing cool things with SVG exported from Visio. There may be more elegant ways to solve your problem, as suggested and demonstrated by the other posters, but perhaps my attempt will be helpful anyway.
I've created a jsFiddle that does what you want, I think. Have a look:
https://jsfiddle.net/visioguy/nv3ew0fh/
First, I made a few changes to your the <svg> code:
I gave the top-most the <g> an id="ThePage", so that I could avoid it in the js code.
I set class="button" on the the <g> that contains the rectangle, rather than at the "sub-level" rectangle, where you had it.
I added pointer-events="none" to the elements inside the button shape's group. When you mouse-over the <text> element it caused mouseout to fire, even though the cursor was still within the group. This pointer-events setting stops that behavior, but now you can't select/copy the text. That might be just fine.
There are a few styles in a separate CSS area in the jsFiddle.
Here are the CSS style additions:
/*
Make the SVG big enough to see and give
it a subtle color:
*/
svg {
background: whitesmoke;
width: 500px;
height: 500px;
}
/*
Highlight direct <rect> children of any <g>
that has this class:
*/
.rect-highlight > rect {
fill: red;
}
And here is the javaScript. I used querySelectorAll to find shapes according to CSS rules. If you add other non-button boxes to your <svg>, this code will find them and highlight them:
// Select all <g> elements that are classed as 'button':
let buttonShapes = document.querySelectorAll('.button');
console.log("buttonShapes: ", buttonShapes.length);
// Add the event handlers to all of these button shapes:
for(let buttonShape of buttonShapes) {
buttonShape.addEventListener('mouseenter', mouseEnterEffect);
buttonShape.addEventListener('mouseout' , mouseOutEffect);
}
function mouseEnterEffect(){
// Select all other <g> in the SVG that are not classed as button
// and are not id'd as ThePage:
let otherShapes = document.querySelectorAll('g:not(.button):not(#ThePage)');
console.log("otherShapes:", otherShapes.length);
for(let g of otherShapes) {
g.classList.add("rect-highlight");
}
}
function mouseOutEffect(){
// Select all shapes that are groups <g> and are classed
// with "rect-highlight":
let highlightShapes = document.querySelectorAll('g.rect-highlight');
console.log("highlightShapes:", highlightShapes.length);
for(let g of highlightShapes) {
g.classList.remove("rect-highlight");
}
}
One last note: you can remove all of the extra elements and attributes that Visio adds. Essentially anything that starts with "v:". This is meta data that Visio can read if you decide to re-import the svg into Visio at a later time. Things like Shape Data fields, User-defined cells, layers, text-formatting and other info is stored in these tags.

How can I animate a SVG who is not hard coded?

I can't animate a svg that I import using the img tag. I can't hard code the svg, as my project generates it during pre-processing using webpack. Sadly, it doesn't seems like I could fetch my file through the svg tag, as I'm not aware of any "src" or "href" attribute.
How can I animate a SVG who is not hard coded ?
That really depends on how is powered your animation.
There are basically three ways to generate an animated SVG:
SMIL Animation - unfortunately not really widely supported (but you did tag with [svg-animate], so let's take it as the first case).
CSS Animation - With the emergence of SVG2, I bet these will become more and more common.
Script based Animations.
How do these behave when their <svg> documentElement is embedded in an <img> tag?
SMIL animations in <img>.
In supporting browsers, these would run just normally, as if your SVG was not embedded.
The only restrictions you would face are
You wouldn't receive any user-gesture, so the element.click and alike events won't work
You can't fallback to a script based animation for browsers that don't support SMIL animations (IE...).
Both limitations do not affect SVG loaded in <object>, <embed> or <iframe>, so you could well use it instead if you need it.
var svgStr = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50">
<rect id="rect" x="-30" y="0" width="30" height="50">
<!-- this will work normally, even in an <img> -->
<animate attributeType="XML" attributeName="x" from="-30" to="50"
begin="0s" dur="10s" repeatCount="indefinite"/>
<!-- this will not work in <img>, but will in <object>/<iframe>/<embed> -->
<animate attributeType="XML" attributeName="fill" from="blue" to="red"
begin="rect.click"
dur="1s" repeatCount="1"/>
</rect>
<!-- js-based workaround won't work in <img> but will in <object>/<iframe>/<embed> -->
<script src="https://cdn.rawgit.com/FakeSmile/FakeSmile/23c5ceae/smil.user.js"><\/script>
</svg>`;
loadSVG(document.images[0]);
loadSVG(document.querySelector('object'));
function loadSVG(container) {
var url = URL.createObjectURL(new Blob([svgStr], {type: 'image/svg+xml'}));
container.src = container.data = url;
}
img{
border: 1px solid green;
}
object{
border: 1px solid blue;
}
<img src="">
<object></object>
<div>Try to click the black rectangle in both the <code><img></code> and <code><object></code> tags.
CSS Animations in <img>.
Just like SMIL animations, they should work in supporting browsers, with the same user-gesture limitations, and the same possible ways around (use an other container):
var svgStr = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50">
<rect id="rect" x="0" y="0" width="30" height="50"/>
<defs>
<style>
#rect {
/* this will work normally, even in an img */
animation: move 10s linear infinite;
}
#rect:hover {
/* this will not work in img, but will in object/iframe/embed */
animation: move 10s linear infinite, color 1s 1;
}
#keyframes move {
from {
transform: translate(-30px, 0px);
}
to {
transform: translate(50px, 0px);
}
}
#keyframes color {
from {
fill: blue;
}
to {
fill: red;
}
}
</style>
</defs>
</svg>`;
loadSVG(document.images[0]);
loadSVG(document.querySelector('object'));
function loadSVG(container) {
var url = URL.createObjectURL(new Blob([svgStr], {type: 'image/svg+xml'}));
container.src = container.data = url;
}
img{
border: 1px solid green;
}
object{
border: 1px solid blue;
}
<img src="">
<object></object>
<div>Try to mouse hover the black rectangle in both the <code><img></code> and <code><object></code> tags.
Script based Animations in <img>.
These will simply not work. SVG documents embedded in <img> tag can not be scripted.
To workaround this, use either an <object>, an <embed> or an <iframe> element as container.
var svgStr = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50">
<rect id="rect" x="0" y="0" width="30" height="50"/>
<script type="application/javascript">
// will simply never work in img
var x = 0, rect = document.getElementById('rect');
function anim() {
x = (x + 1) % 80;
rect.setAttribute('x', x - 30);
requestAnimationFrame(anim);
}
anim();
<\/script>
</svg>`;
loadSVG(document.images[0]);
loadSVG(document.querySelector('object'));
function loadSVG(container) {
var url = URL.createObjectURL(new Blob([svgStr], {type: 'image/svg+xml'}));
container.src = container.data = url;
}
img{
border: 1px solid green;
}
object{
border: 1px solid blue;
}
<img src="">
<object></object>
So basically, SVG in <img> comes with a lot of restrictions, that can all be overwhelmed by using an other container.
Now, every container will come with its own restrictions:
<iframe> will not resize to its content, it will also come by default with a border and some other ugly things.
<object> and <embed> will get unloaded by webkit browsers when not visible (display: none), and won't be cached in any browser...
And of course, there is also the possibility to fetch your SVG markup through AJAX and to load it inside your actual HTML page, but I personally can't advice to do so:
You would need to be sure you don't have duplicate id elements,
You would need to be sure all your CSS rules are specific enough that they won't affect other elements in your page,
You would have to be sure you are loading only trusted code, since scripts will run, that is to say you keep a wide open door to XSS attacks.
And since we're here, an other limitation of SVG in <img> is that it can not load any resources outside its own markup, everything needs to be included in it directly, even fonts and raster images.

Partially fill a shape's border with colour

I am trying to create a progress effect whereby colour fills a DOM object's border (or possibly background). The image attached should give you a better idea of what I'm going for. I have achieved the current result by adding an object with a solid background colour over the grey lines and setting its height. This object has mix-blend-mode: color-burn; applied to it which is why it only colours the grey lines underneath it.
This works okay, but ruins the anti aliasing around the circle, and also the produced colour is unpredictable (changes depending on the colour of the lines).
I feel there must be a better way of achieving this, perhaps with the canvas element. Could someone point me in the right direction please?
Thanks in advance!
This should be possible to do with Canvas and may even be possible with CSS itself by playing with multiple elements etc but I would definitely recommend you to use SVG. SVG offers a lot of benefits in terms of how easy it is to code, maintain and also produce responsive outputs (unlike Canvas which tends to become pixelated when scaled).
The following are the components:
A rect element which is the same size as the parent svg and has a linear-gradient fill. The gradient has two colors - one is the base (light gray) and the other is the progress (cyan-ish).
A mask which is applied on the rect element. The mask has a path which is nothing but the line and the circle. When the mask is applied to the rect, only this path would show through the actual background (or fill) of the rect, the rest of the area would be masked out by the other rect which is added inside the mask.
The mask also has a text element to show the progress value.
The linear-gradient has the stop offset set in such a way that it is equal to the progress. By changing the offset, we can always make sure that the path shows the progress fill only for the required length and the base (light gray) for the rest.
window.onload = function() {
var progress = document.querySelector('#progress'),
base = document.querySelector('#base'),
prgText = document.querySelector('#prg-text'),
prgInput = document.querySelector('#prg-input');
prgInput.addEventListener('change', function() {
prgText.textContent = this.value + '%';
progress.setAttribute('offset', this.value + '%');
base.setAttribute('offset', this.value + '%');
});
}
svg {
width: 200px;
height: 300px;
}
path {
stroke-width: 4;
}
#rect {
fill: url(#grad);
mask: url(#path);
}
/* just for demo */
.controls {
position: absolute;
top: 0;
right: 0;
height: 100px;
line-height: 100px;
border: 1px solid;
}
.controls * {
vertical-align: middle;
}
body {
background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<svg viewBox='0 0 200 300' id='shape-container'>
<linearGradient id='grad' gradientTransform='rotate(90 0 0)'>
<stop offset='50%' stop-color='rgb(0,218,235)' id='progress' />
<stop offset='50%' stop-color='rgb(238,238,238)' id='base' />
</linearGradient>
<mask id='path' maskUnits='userSpaceOnUse' x='0' y='0' width='200' height='300'>
<rect x='0' y='0' width='200' height='300' fill='black' />
<path d='M100,0 100,100 A50,50 0 0,0 100,200 L100,300 M100,200 A50,50 0 1,0 100,100' stroke='white' />
<text id='prg-text' x='100' y='155' font-size='20' text-anchor='middle' fill='white'>50%</text>
</mask>
<rect id='rect' x='0' y='0' width='200' height='300' />
</svg>
<!-- just for demo -->
<div class='controls'>
<label>Set Progress:</label>
<input type='range' id='prg-input' min='0' max='100' value='50' />
</div>
If you are new to SVG you can refer to the MDN Docs (links provided below) for more information about the elements, their attributes and values.
SVG Mask Element
SVG Tutorial on Paths

Categories

Resources