How to get offsetLeft/offsetTop of **<g id="iqaluit"> and <circle id="circle"> elements in javascript.
I tried this: but it gives an error TypeError: bar is null.
JS
let bar = document.getElementById('iqaluit');
console.log(bar.offsetLeft, bar.offsetTop)
let circle = document.getElementById('circle');
console.log(circle.offsetLeft, circle.offsetTop);
HTML
<div class="section-map">
<canvas class="svgfix__canvas" width="500" height="488"> </canvas>
<svg id="MAP" data-name="MAP" data-province="MAP" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 1072.93">
<g id="City_Labels_Canada" data-name="City Labels Canada">
<g id="iqaluit" data-name="Iqaluit" data-province="nunavut" class="city nohover">
<circle id="circle" cx="839.32" cy="390.34" r="3.1"></circle>
<text x="847" y="394" data-name="Iqaluit" class="en de fr es">Iqaluit</text>
</g>
</g>
</svg>
</div>
#Dio, you're on the right track. The svg elements do not support the properties you are looking for but you can access the elements, just a little differently from how you attempted, with the document. See below.
Also, the suggestion from #Syed Qasim Ahmed provides an object, but I'm not sure what you're trying to achieve with offsetLeft/offsetTop, so I can't say if it's equivalent or not.
const map = document.getElementById('MAP');
const bar = map?.querySelector('#iqaluit');
/**
* You can see the available
* methods and properties
* with a for in loop
*/
if (bar) {
for (let i in bar) {
console.log('what is i?', i);
console.log('what is bar[i]?', bar[i]);
}
const getBoundingClientRect = bar.getBoundingClientRect();
console.log({ getBoundingClientRect });
}
<div class="section-map">
<canvas class="svgfix__canvas" width="500" height="488"> </canvas>
<svg id="MAP" data-name="MAP" data-province="MAP" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 1072.93">
<g id="City_Labels_Canada" data-name="City Labels Canada">
<g id="iqaluit" data-name="Iqaluit" data-province="nunavut" class="city nohover">
<circle id="circle" cx="839.32" cy="390.34" r="3.1"></circle>
<text x="847" y="394" data-name="Iqaluit" class="en de fr es">Iqaluit</text>
</g>
</g>
</svg>
</div>
I'm new at coding.
I'm studying the way to make an animated portfolio like Sean Halpin or Stephanie Walter one. I want to put a face, in which, blinking eyes and a moving the mouth would be the animated elements. Basically, I want to play with the visibility of the closed eyes and open mouth. As an example, I drew a face as follows:
<svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 429 429">
<defs>
<style>
.cls-1 {
fill: #fff;
}
.cls-2 {
fill: none;
stroke: #000;
stroke-miterlimit: 10;
}
</style>
</defs>
<g id="face">
<path class="cls-1" d="M611,608.5a214,214,0,1,1,151.32-62.68A212.6,212.6,0,0,1,611,608.5Z" transform="translate(-396.46 -180)" />
<path d="M611,181a212.9,212.9,0,1,1-83.1,16.78A212.11,212.11,0,0,1,611,181m0-1c-118.46,0-214.5,96-214.5,214.5S492.5,609,611,609s214.5-96,214.5-214.5S729.43,180,611,180Z" transform="translate(-396.46 -180)" />
</g>
<g id="eyes">
<g id="eye_r">
<circle class="cls-1" cx="319.15" cy="128.63" r="48.5" />
<path d="M715.61,260.62a48,48,0,1,1-48,48,48.06,48.06,0,0,1,48-48m0-1a49,49,0,1,0,49,49,49,49,0,0,0-49-49Z" transform="translate(-396.46 -180)" />
</g>
<g id="iris_r">
<circle cx="319.15" cy="128.63" r="19" />
</g>
<g id="eye_l">
<circle class="cls-1" cx="109.85" cy="128.63" r="48.5" />
<path d="M506.32,260.62a48,48,0,1,1-48,48,48.06,48.06,0,0,1,48-48m0-1a49,49,0,1,0,49,49,49,49,0,0,0-49-49Z" transform="translate(-396.46 -180)" />
</g>
<g id="iris_l">
<circle cx="109.85" cy="128.63" r="19" />
</g>
<line id="closed_eye_l" class="cls-2" x1="62.04" y1="128.5" x2="158.04" y2="128.5" />
<line id="closed_eye_r" class="cls-2" x1="270.69" y1="128.23" x2="366.69" y2="128.23" />
</g>
<g id="closed_mouth">
<ellipse cx="214.5" cy="309" rx="108.5" ry="11.5" />
<path d="M611,478c29.08,0,56.41,1.25,77,3.51,30.68,3.38,31,7.32,31,7.49s-.35,4.11-31,7.49C667.37,498.75,640,500,611,500s-56.41-1.25-77-3.51c-30.69-3.38-31-7.32-31-7.49s.35-4.11,31-7.49c20.55-2.26,47.88-3.51,77-3.51m0-1c-60.2,0-109,5.37-109,12s48.8,12,109,12,109-5.37,109-12-48.8-12-109-12Z" transform="translate(-396.46 -180)" />
</g>
</svg>
So, I thought three ways to do this:
Place the svg inside an tag, calling then a function that takes into consideration the loading of the file. An example of what I'm saying is found in the following resource: https://www.petercollingridge.co.uk/tutorials/svg/interactive/javascript/, in the "External SVG + External JavaScript" part. It didn't work. The contentDocument ALWAYS returns "null".
In my example, it would be:
HTML:
<object id="face" data="path/to/face.svg" type="image/svg+xml"></object>
JS:
<script type="text/javascript">
window.addEventListener("load", function() {
var svgObject = document.getElementById('face').contentDocument;
var svg = svgObject.getElementById('closed_eye_r');
svg.style.visibility="hidden";
});
</script>
Inline SVG - call a "transform" property. Sean Halpin does it but I'm not sure what he does.
HTML: https://www.seanhalpin.design/
JS: https://www.seanhalpin.design/js/scripts.js
Inline SVG, use getElementById and apply functions to animate the internal parts of the SVG file.
Questions:
a. Is Inline SVG a good practice?
b. Which is the best way to animate an SVG?
I hope to have been clear. Let me know if something is not well presented, I want to learn to edit questions in order to make them as clear as possible.
Thanks!
Why i can not get values of cx and cy ?
its print some array. i need only 2 values
<!DOCTYPE html>
<html>
<body>
<svg xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="100" r="50" fill="red" id="cir"/>
</svg>
<script>
console.log(document.getElementById("cir").cx);
console.log(document.getElementById("cir").cy);
</script>
</body>
</html>
The cx and cy properties are SVGAnimatedLength objects, not strings or numbers.
To get the actual value for cx, you need to do:
cx.baseVal.value
<!DOCTYPE html>
<html>
<body>
<svg xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="100" r="50" fill="red" id="cir"/>
</svg>
<script>
console.log(document.getElementById("cir").cx.baseVal.value);
console.log(document.getElementById("cir").cy.baseVal.value);
</script>
</body>
</html>
Use console.log() instead of Console.log()
console is defined by the browser, Console is not (JS, as most languages, is case-sensitive)
I know that it is possible to attach plain-old Javascript event handlers to elements within an SVG tag. I am looking to duplicate very many instances of an object, so I am planning to use the <def> and <use> tags to simplify things a bit. However each of my children items will need to handle "click" and other events. I could define those events for each child, but it would be nice if I could somehow define them ONCE in the initial items, and then just "reuse" that click event. Is this possible?
https://jsfiddle.net/khyp1o0w/
<svg width="400" height="400" viewBox="0 0 400 400">
<defs>
<rect id="someDef" x="0" y="0" width="60" height="30" fill="#f00" />
<!-- ^^ would like to define an event here... -->
</defs>
<use x="150" y="150" xlink:href="#someDef" />
<use x="250" y="250" xlink:href="#someDef" />
<!-- ^^ ... that is used by these guys -->
</svg>
EDIT: Adding example SVG.
IIRC elements targeted by a use element should be internally copied into the <use> element just like if the <use> were an iframe, and the content were cloned.
So this means that events attached on the original node won't be copied onto the copies, unless this event is part of the node markup (inlined). But chrome don't really follow specs here and won't even make it work... So here is an example for FF :
// this won't work
document.getElementById('someDef').addEventListener('click', function() {
this.setAttribute('stroke', randomColor());
});
<svg width="400" height="400" viewBox="0 0 400 400">
<defs>
<script type="application/javascript">
function randomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[~~(Math.random() * 15)];
}
return color;
}
</script>
<!-- this will work in FF -->
<rect id="someDef" onclick="this.setAttribute('fill',randomColor())" x="0" y="0" width="60" height="30" fill="#f00" />
</defs>
<use x="150" y="150" xlink:href="#someDef" />
<use x="250" y="250" xlink:href="#someDef" />
</svg>
You could of course use event delegation by listening for click events on your root svg element, but this would work only for direct target elements, not for nested ones :
var svg = document.querySelector('svg');
svg.addEventListener('click', function(e) {
var usedTargetID = e.target.getAttributeNS("http://www.w3.org/1999/xlink", 'href');
switch (usedTargetID) {
case '#direct':
clickDirect(e);
break;
case '#nested':
clickNested(e);
break;
default:
return;
}
});
function clickDirect(e) {
// what you seem to want
e.target.setAttribute('fill', randomColor());
}
function clickNested(e) {
// will set both nested...
e.target.setAttribute('fill', randomColor());
}
function randomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[~~(Math.random() * 15)];
}
return color;
}
<svg width="400" height="400" viewBox="0 0 400 400">
<defs>
<rect id="direct" x="0" y="0" width="60" height="30" />
<g id="nested">
<!-- you can only access this -->
<!-- not its content individually -->
<rect x="80" y="0" width="60" height="30" />
<rect x="20" y="70" width="60" height="30" />
</g>
</defs>
<use x="150" y="50" xlink:href="#direct" />
<use x="250" y="150" xlink:href="#nested" />
</svg>
Or you could also add an event per element, but this would have the same limitations as delegation...
Suppose i have an svg element like so
<svg id="svgCanvas" class="pan" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none">
<g id="viewport">
//Filled with an arbitrary amount of lines and cirles - examples below.
<line x1="632" y1="357.5" x2="682" y2="270.89745962155615" class="line" style="stroke: rgb(128, 128, 128); stroke-width: 1.3px;"></line>
<circle cx="82.08376766398476" cy="367.0988235405059" r="16.5" stroke="blue" fill="white" class="circle"></circle>
</g>
</svg>
how would i go about clearing everything from the that group, whilst also keeping the group element itself?
Just using DOM methods without the need for any framework you could go for:
var el = document.getElementById("viewport");
while (el.firstChild) {
el.removeChild(el.firstChild);
}
document.addEventListener('load', function(){
document.getElementById("viewport").innerHTML = "";
});