I'm using regular JS (I know it isn't the best, but I'm a beginner)
So I'm writing code for a canvas project. However when using the getContext() function the JS console says
Uncaught TypeError: canvas.getContext is not a function
at master.js:4
I've looked around and found that nothing has fixed my problem.
var canvas = document.getElementsByClassName('myCanvas');
console.log(canvas);
var context = canvas.getContext('2d');
console.log(context);
The HTML is this:
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="master.js" charset="utf-8"></script>
<link rel="stylesheet" href="master.css">
</head>
<body>
<canvas class="myCanvas">Your browser doesn't support HTML canvas</canvas>
</body>
</html>
Does anyone know what I am doing wrong?
getElementsByClassName returns an array of elements. Thus, it does not have the method you are looking for. Try the following:
var canvas = document.getElementsByClassName('myCanvas')[0];
console.log(canvas);
var context = canvas.getContext('2d');
console.log(context);
Your code is also stored in the HTML head, so your canvas element will not be found. Either put your <script> tag in the HTML <body> tag or set a function inside document.onload as follows:
document.onload = function() {
var canvas = document.getElementsByClassName('myCanvas')[0];
console.log(canvas);
var context = canvas.getContext('2d');
console.log(context);
}
Related
I am trying to implement the HTML canvas tag in Visual Studio Code, but it is not working.
Here is my code:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script src="jspsych-6.3.1\jspsych.js"></script>
<script src="jspsych-6.3.1\plugins\jspsych-html-button-response.js"></script>
<link href="jspsych-6.3.1\css\jspsych.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<canvas id="myCanvas" width="200" height="100"></canvas>
<script>
var finalTimeline = [];
var example = {
type: 'html-button-response',
stimulus: function() {
var c = document.getElementById("myCanvas");
var ctx= c.getContext("2d");
ctx.beginPath();
ctx.arc(95, 50, 40, 0, 2 * Math.PI);
ctx.stroke();
}
};
finalTimeline.push(example);
jsPsych.init({
timeline: finalTimeline
});
</script>
</body>
</html>
When I try to run the code, I receive the following message in my debugger console: "Uncaught TypeError: Cannot read properties of null (reading 'getContext')."
I know that other people on this website have encountered the same problem, but after looking through some of their threads, it seems that their issue was that they had not added the canvas element to their DOM, i.e., they had not placed the element within the body tags. However, my canvas element is within the body tags, so I am not sure why my computer is not recognizing it.
Has anyone encountered this issue before? If so, would you please share your thoughts on resolving it?
Edit: The problem seems to be related to the fact that the canvas element is being called inside a jsPsych function. When I implement the code using regular JavaScript, it works fine.
I am trying to pass the canvas HTML element as a parameter, and I thought 'this' would work but I can't quite get it to. Could someone help me use the 'this' keyword to pass the canvas to main() upon page-load, please?
Doesn't work:
<html>
<head>
<title>Draw on Canvas</title>
</head>
<body onload=main(this.firstChild)><canvas></canvas></body>
<script>
function main(canv) {
cntx = canv.getContext("2d");
cntx.rect(10, 10, 100, 100);
cntx.fill();
}
</script>
</html>
Works, but would like to use the 'this' keyword instead:
<html>
<head>
<title>Draw on Canvas</title>
</head>
<body onload=main(document.body.firstChild)><canvas></canvas></body>
<script>
function main(canv) {
cntx = canv.getContext("2d");
cntx.rect(10, 10, 100, 100);
cntx.fill();
}
</script>
</html>
Doesn't work (onload not defined for canvas element):
<html>
<head>
<title>Draw on Canvas</title>
</head>
<body><canvas onload=main(this)></canvas></body>
<script>
function main(canv) {
cntx = canv.getContext("2d");
cntx.rect(10, 10, 100, 100);
cntx.fill();
}
</script>
</html>
Works, and uses 'this', but want the code to run without clicking:
<html>
<head>
<title>Draw on Canvas</title>
</head>
<body><canvas onclick=main(this)></canvas></body>
<script>
function main(canv) {
cntx = canv.getContext("2d");
cntx.rect(10, 10, 100, 100);
cntx.fill();
}
</script>
</html>
I suggest you consider a different approach as you risk complicating the expressiveness of your overall scripting logic if you're mixing it into your HTML tags. More to your point, while you can't use onload in an HTML tag context to get any this beyond the window, you can create JS functions that are defined to execute after window.onload in any fashion you want.
You're already using JavaScript to define your canvas attributes, why not create the canvas in JS at the same time!
You can also see how this could be extended to open up your options on creating/appending more canvasses on the fly.
If this doesn't work for you, let me know if this was an abstracted question for an issue that I might be able to help with more directly.
<html>
<head>
<title>Draw on Canvas</title>
</head>
<body>
<script>
function createCanvasRect(x, y, width, height) {
var canv = document.createElement('canvas'),
cntx = canv.getContext('2d');
cntx.rect(x, y, width, height);
cntx.fill();
return canv;
}
function load() {
var canvas = createCanvasRect(10, 10, 100, 100);
document.body.appendChild(canvas);
}
window.onload = load;
</script>
</body>
</html>
Your problem is with the use of onload.
Typically, a listener attached in-line is called as if wrapped in an outer function with it's this set to the element on which the listener is called. However, that's not the case for onload listeners attached to the body element. Their execution is delayed and they are called with the global / window object set to this.
So you can't use this the way you're trying to do it.
The following demonstrates that a the body's load listener is called with this as the global object, but the div's click listener is called with the div element as this.
<script>
// Reference to global/window object
var global = this;
</script>
<body onload="console.log(this === global)">
<div onclick="console.log(this.tagName)">clickable div</div>
</body>
Try this. I put javascript out from HTML for more cleaner code.
function main() {
console.log(this);
var cntx = this.getContext("2d");
cntx.rect(10, 10, 100, 100);
cntx.fill();
}
window.onload = function() {
main.call(document.getElementById('main'));
}
<html>
<head>
<title>Draw on Canvas</title>
</head>
<body>
<canvas id="main"></canvas>
</body>
</html>
Well so many ways to do things.
Element referencing in Javascript
To access a DOM element you need to id it by giving it a unique id.
<canvas id="myCanvas"></canvas>
Note id Must be unique, if another element has the same id the browser will enter quirks mode (see below)
You can then access it directly using its id as a variable name.
var ctx = myCanvas.getContext("2d");
There are some that prefer to use the slower and more painful.
var ctx = document.getElementById("myCanvas").getContext("2d");
Or others use
var ctx = document.querySelector("#myCanvas").getContext("2d");
Get it in order
All these methods have one problem. When a browser parses a page it adds elements to the DOM one at a time from the top down.
If you add some script above the elements you want to use, the script will not find the elements as they have not yet been created
<canvas id="topCan"></canvas>
<script> // this code is run when the browser finds it
var ctx = topCan.getContext("2d"); // works as the element has been created
var ctx1 = botCan.getContext("2d"); // no work as the element does not yet exist
</script>
<canvas id="botCan"></canvas>
So to make sure you add the script after the elements (and before the closing body tag see quirks mode below)
Sometimes its just plain inconvenient for you to put the script after the content. That is when you would put the code inside a function you call on the load event. The code in the function will not run until all the elements have been added to the page.
How you listen to that event is up to you, see below
Organizing an event
It is considered bad form to assign an event handler directly
myCanvas.onclick = function(){};
and even worse if you do
<canvas onclick = "myFuction(this)"></canvas>
with the road of enlightenment packed with those that say the following way is the way
const canvas = document.querySelector("#myCanvas");
canvas.addEventListener("click",myFunction);
function myFunction(){ /* code */}
All the above methods work, none of them are right or wrong, there are a few other ways as well. What method you use is up to you, I always recommend that you use the method you find easiest to remember and use.
Careful of the quirks
But there are some things you should not do. The reason is that some layouts make the browser think its back in the late 90's early 2000's, and you have not followed the rules (rules that nobody actually knew), to stop it's self looking stupid next to its peers it will switch to quirks mode which is not good and will generally slow everything down.
One of the things that can trigger quirks mode is placing a script tag where it should not be.
So NEVER put a script tag outside the body or head tags
The path to world peace is always put script tags where they belong.
<html>
<head>
<script></script> <!-- browser is your friend -->
</head>
<body>
<script></script> <!-- browser thinks your great -->
<p> some content </p>
<script></script> <!-- and you know everything-->
</body>
</html>
The way to darkness. Putting a script tag anywhere as follows
<script></script> <!-- wrong -->
<html>
<script></script> <!-- wrong -->
<head>
</head>
<script></script> <!-- oh so bad -->
<body>
</body>
<script></script> <!-- oh so bad -->
</html>
<script></script> <!-- just playing with fire -->
One way
How I would do depends on the tide so currently
<html>
<head>
<title>Draw on Canvas</title>
</head>
<body>
<!-- id the canvas with a unique id -->
<canvas id = myCanvas></canvas>
<script> // script after the element you are accessing
const ctx = myCanvas.getContext("2d");
ctx.rect(10, 10, 100, 100);
ctx.fill();
</script> <!-- ensure the script is inside the body tag -->
</body>
</html>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Web Game </title>
</head>
<body onkeydown="keyDown(event)" onkeyup="keyUp(event)"></body>
<canvas id="gameCanvas1" width=600 height=400 style ="position:absoloute:top:0;left:0;background-image:url('img/background.jpg')">
Please upgrade your browser to support HTML5.<br/>
One recommendation is to install the latest Chrome or Firefox.
</canvas>
<script>
gamecanvas = document.getElementById("gamecanvas1");
ctx=gamecanvas.getContext("2d");
var img = new Image();
img.src = "img/left.png";
//MainLoop
MainLoop();
function MainLoop(){
grafx.drawImage(img,0,0)};
setTimeout(MainLoop, 1000/60); //about 60fps
}
</script>
</html>
I am trying to load the left.png image. I am making a basic platformer in HTML and JavaScript, but the image isn't being shown. I think the drawImage is being called before the image is loaded but I'm not sure how to fix it. Thanks.
There are two problems I found.
P1:
grafx.drawImage(img,0,0)}; is wrong
it should be
ctx.drawImage(img,0,0);
P2:
use image onload callback rather than setTimeout.
img.onload = function () {
ctx.drawImage(img,0,0);
};
You have several errors in your js code.
your element has an id of gameCanvas1, but you are trying to get an element with an id of gamecanvas1 (document.getElementById("gamecanvas1");)
So the javascript wont find your canvas, which means the image wont be drawn. You also have an extra closing } which is causing errors in your function
Try opening your browsers dev console, you'll see the issues
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Web Game </title>
</head>
<body onkeydown="keyDown(event)" onkeyup="keyUp(event)"></body>
<canvas id="gameCanvas1" width=600 height=400 style ="position:absoloute:top:0;left:0;background-image:url('img/background.jpg')">
Please upgrade your browser to support HTML5.<br/>
One recommendation is to install the latest Chrome or Firefox.
</canvas>
<script>
console.log("i am here ");
var gamecanvas = document.getElementById('gameCanvas1');
var ctx=gamecanvas.getContext('2d');
var img = new Image();
img.src = "img/left.png";
//MainLoop
MainLoop();
function MainLoop(){
ctx.drawImage(img, 10, 10);
setTimeout(function(){
MainLoop();
}, 1000/60); //about 60fps
}
</script>
</html>
I've created an SVG using Raphael that I want to capture and convert to PNG and then display in the open window. I have looked at some other stack overflow answers like this one. I implemented ollieg's answer to that question. Here's an example of what I'm doing:
<html>
<head>
<script src="NBA_test/lib/raphael-min.js"></script>
</head>
<body>
<div id="canvas"></div><br>
<script language="JavaScript">
var test=Raphael("canvas",50,50);
var rect=test.rect(0,0,50,50);
rect.attr({fill: '#fff000'})
window.onload = function() {
var canvas = document.getElementById("canvas");
window.location = canvas.toDataURL("image/png");
}
</script>
</body>
</html>
This should draw a yellow rectangle and output it as a png. The console confirms that the SVG is being captured correctly in the canvas var. However, the toDataURL line throws an error: "TypeError: 'null' is not an object (evaluating 'canvas.toDataURL')" I know my example is a little different in the sense that I don't have an actual canvas tag in my html, just the div that is going to get the canvas. Given that the canvas is being captured correctly, however, I don't understand why the second line throws that error.
Using canvg per the suggestion above and raphael.export.js, I have solved my problem (kind of). My minimal example now works as below:
<html>
<head>
<script src="lib/raphael-min.js"></script>
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/rgbcolor.js"></script>
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/StackBlur.js"></script>
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/canvg.js"></script>
<script src="lib/raphael.export.js"></script>
</head>
<body>
<div id="raph_canvas"></div><br>
<canvas id="html_canvas" width="50px" height="50px"></canvas>
<script language="JavaScript">
var test=Raphael("raph_canvas",50,50);
var rect=test.rect(0,0,50,50);
rect.attr({fill: '#fff000', 'fill-opacity':1, 'stroke-width':1})
window.onload = function() {
var canvas_svg = test.toSVG();
canvg('html_canvas',canvas_svg);
var canvas_html = document.getElementById("html_canvas");
window.location = canvas_html.toDataURL("image/png");
}
</script>
</body>
</html>
The problem now is that my actual app, which is a little more complex, doesn't work, but I will maybe post a separate question about that.
I have a simple HTML5 Snake game that uses canvas that I want to package as a Chrome App. Here is my normal HTML file:
<!DOCTYPE html>
<html>
<head>
<title>Snake</title>
<link rel="stylesheet" href="snake.css">
</head>
<body>
<canvas id="canvas" width="700" height="700"></canvas>
<script type="text/javascript" src="food.js"></script>
<script type="text/javascript" src="snake.js"></script>
<script type="text/javascript" src="point.js"></script>
<script type="text/javascript" src="game.js"></script>
<script type="text/javascript">
window.onload = function() {
var canvas = document.getElementById("canvas"),
game = new Game(canvas, new Snake(canvas), new Food(canvas));
game.init();
game.run();
}
</script>
</body>
</html>
However the script in the HTML is not allowed. So I removed it and placed it main.js which creates the window. I provide a callback that is the same as window.onload above because my understanding is that this is the chrome app equivalent:
chrome.app.runtime.onLaunched.addListener(function() {
"use strict";
chrome.app.window.create('snake.html', {
bounds: {
width: 800,
height: 800
}
}, function() {
var canvas = document.getElementById("canvas"),
game = new Game(canvas, new Snake(canvas), new Food(canvas));
game.init();
game.run();
});
});
This runs after the DOM appears to have loaded but document.getElementById always returns null. Looking at the document in the developer tools it looks like only the scripts are loaded even though I can see the canvas in the app window (I have a border around the canvas). Can anyone tell me what is going on? Where is the appropriate place to access the DOM from when window.onload doesn't work?
main.js is running on the background / event page and is not in the same context as the newly created window's DOM. You can interact with that context as documented on the create callback, but it's not how I recommend solving this.
Instead, from your main.js simply create the window, and from the HTML file load script code that executes directly.
window.html:
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas" width="700" height="700"></canvas>
<script ...</script>
<script type="text/javascript" src="window.js"></script>
window.js:
var canvas = document.getElementById("canvas"),
game = new Game(canvas, new Snake(canvas), new Food(canvas));
game.init();
game.run();