Setting up renderer's canvas element by html id

Hello there,
Sorry to bother with what seems to be a very basic question, yet I’ve been struggling to find a solution to my problem…

So, basically I’m trying to load my animation script in the head of my html file (as it seems to be a common practice (?)) and render that animation in a canvas element with a given id attribute by passing it to the renderer’s constructor.

Here’s my html file:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>My first three.js app</title>
		<style>
			body { margin: 0; }
                        canvas { display: block; }
                </style>
		<script src="js/three.min.js"></script>
		<script src="js/main.js"></script>
	</head>
	<body>
		<canvas id="myCanvasId"></canvas>
	</body>
</html>

and my js/main.js file:

var myCanvas = document.getElementById("myCanvasId")

var scene = new THREE.Scene();

var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );

var renderer = new THREE.WebGLRenderer({antialias: true, canvas: myCanvas});

renderer.setSize( window.innerWidth, window.innerHeight );

document.body.appendChild( renderer.domElement );

var geometry = new THREE.BoxGeometry( 1, 1, 1 );

var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );

var cube = new THREE.Mesh( geometry, material );

scene.add( cube );

camera.position.z = 5;

var animate = function () {

requestAnimationFrame( animate );

cube.rotation.x += 0.01;

cube.rotation.y += 0.01;

renderer.render( scene, camera );

};

animate();

However, this doesn’t work. It returns a TypeError ‘v is null’ (and it seems to me that v is the dom element passed to the renderer’s constructor)…
Also I am not sure of two things:

  1. Do I still have to run document.body.appendChild( renderer.domElement ) since I am adding the canvas element explicitly in the html file ?
  2. Does the call to the render method (ie the animate function) have to be made in the body ?

In case you are wondering, I am fairly new to JS and HTML so some guidance and best practices would be very much appreciated !!!

No, that’s only necessary if the renderer creates the internal canvas by itself.

I’m not sure what you mean with body. The first call of animate(); starts the animation loop. Inside the loop, it’s necessary to call renderer.render( scene, camera ); since this command actually produces the frame.

Thank you for your reply Mugen87 !
So let me rephrase my problem… Basically if I run the code as shown above minus the document.body.appendChild( renderer.domElement ); line, nothing shows up because I think that the call to renderer.render(scene,camera) looks for the canvas to render to and that canvas is null when I put my script in the head of my html page… However if I put my script in the body everything works fine.
Hence the question: should I do the call to animate(); in the body (and leave the rest of my script in the head) ?

EDIT: well actually if I call animate(); on its own in the body it doesn’t work either…

Ah, now I see what you mean. Yes, if the DOM is not ready, it’s not possible to correctly selected DOM elements. If you put your script tag into the head of your html page, you can wrap the JS code into an event listener to avoid this issue:

window.onload = function() {
  init();
  animate();
};

It works just fine :smiley:
Thanks a lot for taking the time to help me !
I ended up learning a bit about how the DOM actually works :sweat_smile:

Adding defer to the script tag should work as well.

<script defer src="js/main.js"></script>
2 Likes

It didn’t work for me. I only get result when

ocument.body.appendChild(renderer.domElement);

is there.