querySelectorAll()?

I have a SVG with three rectangles


Why are the rectangles appearing in the node-list of the console?

<script>
const rects = document.querySelectorAll("rect");
console.log(rects);
<!--
for(let i=0;i<rects.length; i++) {
  console.log(`Rectangle ${i} is ${rect[i].getTotalLength()`);
  }
  -->
</script>
</defs>
<rect x="100" y="0" width="100" height="100"  opacity="1" fill-opacity="0" stroke="#ff0043" stroke-width="1" stroke-opacity="1" />	
<rect x="300" y="0" width="100" height="300"  opacity="1" fill-opacity="0" stroke="#ff0043" stroke-width="1" stroke-opacity="1" />	
<rect x="500" y="0" width="100" height="400"  opacity="1" fill-opacity="0" stroke="#ff0043" stroke-width="1" stroke-opacity="1" />	
</svg>

Please validate your HTML code to ensure that it is valid.
https://validator.w3.org/

sure

<svg 
xmlns="http://www.w3.org/2000/svg" 

xmlns:xlink="http://www.w3.org/1999/xlink"> 

<defs> 
<style> 
path,rect { 
stroke:#000; 
stroke-dasharray: 2072; 
stroke-width:3; 
animation: my_animation 3s; 
animation-fill-mode: forwards; 
fill: red; 
} 

@keyframes my_animation{ 
0%{ 
stroke-dashoffset: 2072; 
fill-opacity: 0; 
} 
80%{ 
stroke-dashoffset: 0; 
fill-opacity: 0; 
} 
100%{ 
fill-opacity: 1; 
} 
} 



</style> 
<script> 
const rects = document.querySelectorAll("rect"); 
console.log(rects); 
<!-- 
for(let i=0;i<rects.length; i++) { 
console.log(`Rectangle ${i} is ${rect[i].getTotalLength()`); 
} 
--> 
</script> 
</defs> 
<rect x="100" y="0" width="100" height="100" opacity="1" fill-opacity="0" stroke="#ff0043" stroke-width="1" stroke-opacity="1" /> 
<rect x="300" y="0" width="100" height="300" opacity="1" fill-opacity="0" stroke="#ff0043" stroke-width="1" stroke-opacity="1" /> 
<rect x="500" y="0" width="100" height="400" opacity="1" fill-opacity="0" stroke="#ff0043" stroke-width="1" stroke-opacity="1" /> 
</svg>

Your script is before the rects. They dont exist at time of execution. Wrap your script in a onload, or move it below the rects.

3 Likes

I moved it below, heres the output


I’m trying to find the total length of each rect for the animation, but when I uncomment the for loop I get

heres the svg

<svg  
	xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<style>
rect {
  stroke:#000;
  stroke-dasharray: 2072;
  stroke-width:3;
  animation: my_animation 3s;
  animation-fill-mode: forwards;
  fill: red;
}

@keyframes my_animation{
  0%{
    stroke-dashoffset: 2072;
    fill-opacity: 0;
  }
  80%{
    stroke-dashoffset: 0;
    fill-opacity: 0;
  }
  100%{
    fill-opacity: 1;
  }
}



</style>

</defs>
<rect x="100" y="0" width="100" height="100"  opacity="1" fill-opacity="0" stroke="#ff0043" stroke-width="1" stroke-opacity="1" />	
<rect x="300" y="0" width="100" height="300"  opacity="1" fill-opacity="0" stroke="#ff0043" stroke-width="1" stroke-opacity="1" />	
<rect x="500" y="0" width="100" height="400"  opacity="1" fill-opacity="0" stroke="#ff0043" stroke-width="1" stroke-opacity="1" />	
<defs>
<script>
const rectangles = document.querySelectorAll('rect');
console.log(rectangles);
console.log(rectangles.length);

for(let i=0;i<rectangles.length; i++) {
  console.log(`Rectangle ${i} is ${rectangles[i].getTotalLength()});
  }

</script>
</defs>
</svg>

what is wrong with the for loop?

What’s wrong with the attribute name?

This is an odd quirk of how SVG handles script tags.

You can’t use a less than symbol; SVG thinks you’re starting a tag; so it sees the code as starting a tag that reads:

<rectangles.length; i++)
and throws a fit because that’s not a valid attribute definition for an HTML tag.

The solution is to inverse the logic: Check to see that i is not greater than or equals to the length. (or the contrapositive instead: Check that the length is greater than i)

The script should be moved to the end of the body, just before the </body> tag.
That’s where scripts belong, and fixes all of the problems that have been occurring.

Oh no, but this isn’t html, so @m_hutley has the right approach.

Here you go. Use the forEach method instead of the for loop, and the code now works.

rectangles.forEach(function (rectangle, i) {
  console.log(`Rectangle ${i} is ${rectangle.getTotalLength()}`);
});
1 Like