I sat down to write a demo HTML5-ish page that lets a user to perform following operations on a canvas - draw lines, draw a filled rectangle, and reset the canvas. Each operation is represented by a button, clicking on which the operation is carried out.
My initial attempt ended up as a mingled HTML and JavaScript code. So, after a bit of reading, I used the addEventListener()
method to add the events to the buttons, thus eliminating the onclick code in HTML.
Now, I followed two approaches while writing the JavaScript code.
The first, simpler approach:
// Instead of forcing all event handlers to know the canvasId by themselves, I chose to hardwire it in window.onload() function, inside which I also add the event handlers to their respective buttons.
// I need to pass canvasId to the event handlers, thus forcing me to write anonymous functions.
// I won't have a future reference to the event handlers unless, of course, I store them in variables, which I find clumsy.
window.onload = function()
{
var canvasId = "DrawingBoard";
var rectangleButton = document.getElementById("rectangleButton");
rectangleButton.addEventListener("click", function() { drawFilledRectangle(canvasId); }, false);
var linesButton = document.getElementById("linesButton");
linesButton.addEventListener("click", function() { drawLines(canvasId); }, false);
var resetButton = document.getElementById("resetButton");
resetButton.addEventListener("click", function() { resetCanvas(canvasId); }, false);
}
function drawFilledRectangle(canvasId)
{
resetCanvas(canvasId);
var canvas = document.getElementById(canvasId);
var context = canvas.getContext("2d");
context.fillStyle = "#eee";
context.fillRect(50, 25, 150, 120);
}
function drawLines(canvasId)
{
resetCanvas(canvasId);
var canvas = document.getElementById(canvasId);
var context = canvas.getContext("2d");
for (var x = 0.5; x < canvas.width; x += 10)
{
context.moveTo(x, 0);
context.lineTo(x, canvas.height - 1);
}
context.strokeStyle = "#eee";
context.stroke();
}
function resetCanvas(canvasId)
{
var canvas = document.getElementById(canvasId);
var context = canvas.getContext("2d");
context.clearRect(0, 0, canvas.width, canvas.height);
}
The second approach:
// I don't need to pass canvasId to each event handler.
// This way, I don't have to create anonymous functions, meaning that I always have a reference to the function - the function name.
// The structure still makes sense; this way, I have grouped the canvas operations along with the canvas id in a single namespace.
// Whatever you need to do with canvas, you can just add a function to the object literal CanvasOperations.
window.onload = function()
{
var rectangleButton = document.getElementById("rectangleButton");
rectangleButton.addEventListener("click", CanvasOperations.drawFilledRectangle, false);
var linesButton = document.getElementById("linesButton");
linesButton.addEventListener("click", CanvasOperations.drawLines, false);
var resetButton = document.getElementById("resetButton");
resetButton.addEventListener("click", CanvasOperations.resetCanvas, false);
}
var CanvasOperations =
{
canvasId : "DrawingBoard",
resetCanvas : function()
{
var canvas = document.getElementById(CanvasOperations.canvasId);
var context = canvas.getContext("2d");
context.clearRect(0, 0, canvas.width, canvas.height);
},
drawLines : function()
{
CanvasOperations.resetCanvas(CanvasOperations.canvasId);
var canvas = document.getElementById(CanvasOperations.canvasId);
var context = canvas.getContext("2d");
for (var x = 0.5; x < canvas.width; x += 10)
{
context.moveTo(x, 0);
context.lineTo(x, canvas.height - 1);
}
context.strokeStyle = "#eee";
context.stroke();
},
drawFilledRectangle : function()
{
CanvasOperations.resetCanvas(CanvasOperations.canvasId);
var canvas = document.getElementById(CanvasOperations.canvasId);
var context = canvas.getContext("2d");
context.fillStyle = "#eee";
context.fillRect(50, 25, 150, 120);
}
};
Note that I have provided my reasoning of both the approaches in their respective comments.
Which approach is better?
Are my reasons to lean towards the second one right?
What approach should I take while using attributes like
id
or name of HTML elements in JavaScript code?
I think hardwiring should be as less as possible. Is there anything you can point me to for learning? Or is it just intuition thing, to be decided by me? I did Google on this, but couldn't find a right solution; or I might have failed to frame a proper Google search.
1 Answer 1
The second one is indeed "better", in my opinion. Although, "better" is a bit hard to define when both versions work. "More maintainable" might be a more precise way of putting it.
I might take it a step further, and add a constructor (i.e. class) that wraps the canvas element:
function DrawingCanvas(elementId) {
this.element = document.getElementById(elementId);
this.context = this.element.getContext("2d");
}
DrawingCanvas.prototype = {
reset: function () {
this.context.clearRect(0, 0, this.element.width, this.element.height);
},
drawLines: function () { ... draw stuff ... },
drawFilledRectangle: function () { ... draw different stuff ... },
};
I'd also use addEventListener
for the onload
event:
window.addEventListener("load", function () {
var canvas = new DrawingCanvas("DrawingBoard");
document.getElementById("rectangleButton").addEventListener("click", canvas.drawFilledRectangle, false);
document.getElementById("linesButton").addEventListener("click", canvas.drawLines, false);
document.getElementById("resetButton").addEventListener("click", canvas.reset, false);
}, false);
Finally, a word of general advice: Don't use the "curly bracket on new line"-style in JavaScript.
JavaScript has some dumb parts to it, and one of them is that it'll automatically insert a semi-colon at the end of a line, if it thinks it's missing.
So, if you for instance have a function that returns an object literal, you can get into trouble:
function getObj() {
return
{
x: 42
}
}
will be interpreted as
function getObj() {
return; // <- auto semi-colon
}
-
\$\begingroup\$ Yeah. I had wanted to imply "more maintainable" by "better"; the right word just hadn't come to the sleepy brain. :-) You are right about addEventListener() for window's onload, too; it's just that my main focus was the Canvas object. I will certainly read more about the constructor function; I bet that will be useful in future. \$\endgroup\$Bhoot– Bhoot2012年10月07日 08:57:31 +00:00Commented Oct 7, 2012 at 8:57
-
\$\begingroup\$ I wasn't aware of the parsing style of JavaScript. I read a bit on it after reading your braces issue. It seems I will have to drop my all-time favorite brace-on-new-line style. \$\endgroup\$Bhoot– Bhoot2012年10月07日 08:58:39 +00:00Commented Oct 7, 2012 at 8:58
-
\$\begingroup\$ @MrBhoot Brace-on-new-line has always been evil and wrong! Just kidding :) - you could also look into CoffeeScript, which compiles to JS, but where you almost never use curlies, and there are structures such as
class
that translate to JS's constructors/prototypes. But first, read Douglas Crockford's "JavaScript: The Good Parts" if you want to dig deeper. \$\endgroup\$Flambino– Flambino2012年10月07日 10:41:05 +00:00Commented Oct 7, 2012 at 10:41 -
\$\begingroup\$ As you directed, I had a look at CoffeeScript; impressive, indeed! But I will concentrate on pure JavaScript for now, along with a library like jQuery. CoffeeScript - maybe some time later. :-) \$\endgroup\$Bhoot– Bhoot2012年10月08日 05:48:22 +00:00Commented Oct 8, 2012 at 5:48
-
\$\begingroup\$ @MrBhoot Excellent choice! While you can use jQuery or anything other JS library, and simply write your own code in CoffeeScript (it is JS after all, just a different syntax), there's no substitute for really learning pure JS first. Depending on what you're used it, JS has some strange, strange stuff in it (again, I can recommend googling for Douglas Crockford; you can find videos of his talks on the subject). Once you feel you "get it", CoffeeScript is a great next step. \$\endgroup\$Flambino– Flambino2012年10月08日 08:26:19 +00:00Commented Oct 8, 2012 at 8:26
Explore related questions
See similar questions with these tags.