Javascript 'this' to pass a canvas to function on page load - javascript

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>

Related

How do I make the querySelector look in a specific included document only?

On my index page I have a number of includes.
<section><?php include('content/content1.php') ?></section>
<section><?php include('content/content2.php') ?></section>
<section><?php include('content/content3.php') ?></section>
In each of them I have a unique script (and some other things which is not shown here).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Content1</title>
<link rel="stylesheet" href="content/sketch.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.10/p5.js"></script>
</head>
<body>
<div class="frame">
<canvas></canvas>
</div>
<script src="content/content1.js"></script>
</body>
The <canvas> tag is what the querySelector in the javascript calls to.
var canvas = document.querySelector('canvas');
This works, but only for the first content file. It seems the querySelector looks at the whole loaded page, instead of just inside the body of the document where the script is placed. Google console says: "Indentifier 'canvas' has already been declared".
I have tried setting an id on the canvas-element:
<canvas id="canvas1"></canvas>
var canvas = document.querySelector('#canvas1');
But it's not working. How do I get around this?
You can use document.currentScript to get the tag of the currently running script tag. From there, you can navigate to its containing section, and from there, get to the canvas descendant.
You should also put everything into an IIFE to avoid global variable collisions.
(() => {
const canvas = document.currentScript.closest('section').querySelector('canvas');
// ...
})();

How to link JavaScript frameworks/extensions in HTML?

I have seen a lot of frameworks and extensions recently and all for a lot of different things. It is hard for me to do things using just raw JavaScript or raw jQuery. I want to use these JS extensions/frameworks but the problem is that I don't know how to link them to my HTML document.
For Example: <script src="some link of fw/ext" type="text/javascript"></script> I put this in the <head> of my HTML and then write my code in a different file named "script.js" (made using notepad) but the code doesn't seem to work.
I know this is a problem because I don't link it properly.
I want to work with fabric.js on a project related to HTML5 canvas. I think I need to link both, my code and the framework in some way but I'm not sure as to how should I do it.
My HTML code :
<!DOCTYPE html>
<html>
<head>
<title>Code Example</title>
<link rel="stylesheet" type="text/css" href="stylesheet.css"/>
<script type="text/javscript" src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js"></script>
</head>
<body>
<canvas id="draw" width="250px" height="250px">This if browser doesn't support HTML5
</canvas>
</body>
</html>
My JS Code:
var canvas = new fabric.Canvas('draw');
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.width = 100;
canvas.freeDrawingBrush.color = "#ff0000";
Should work somewhat like this : http://jsfiddle.net/MartinThoma/B525t/5/
You need to load fabric.js first at the header and then you need to load your js file order matters.
<script type='text/javascript' src='//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js'></script>
<script type='text/javascript' src='yourjsfile.js'></script
In case you are not clear on how to add elements to the canvas. Then here is one snippet which could help you.
Also wrap your code inside onload() function so that when the script loads, the #draw element is available
window.onload = function() {
var canvas = new fabric.Canvas('draw');
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.width = 5;
canvas.freeDrawingBrush.color = "#ff0000";
// create a rectangle object
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 20,
height: 20
});
// you need to add the object to canvas
canvas.add(rect);
}

How to add a javascript function inside the body of iframe

i have a html file with iframe and button in it, Is there a way to add a javascript function inside the body of iframe after I click the button?. Here is my code. Thanks
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
<script>
function callMe() {
var frame = $('iframe'),
contents = frame.contents(),
body = contents.find('body');
var script2 = document.createElement("script");
script2.type = "text/javascript";
script2.text = " function setEmployeeId(){var employeeId = 0;};"
$(body).append(script2);
};
</script>
<title>sample</title>
</head>
<body>
<iframe src="page2.html">
</iframe>
<button onClick="callMe();">click</button>
</body>
</html>
The result I want is to be like this.
<html>
<head>
<title>sample</title>
</head>
<body>
<iframe>
<html>
<head>
<title></title>
</head>
<body>
<script>
function setemployeeId() {
var employeeId = 0;
};
</script>
</body>
</html>
</iframe>
</body>
</html>
Hopefully you can clarify a few things for me as we go, but I think I have some bad news for you.
Any content that is loaded in an iframe cannot truly edited unless you own the page that is being loaded, in that case you can just bake in whatever you need into the loaded page.
HOWEVER!
You can still access elements in the iframe by using the contentWindow attribute. That is all laid out for you here: How to pick element inside iframe using document.getElementById
Once you've got the element you want to work with, you can create a function in the parent window and then add a call to the parent window's function using window.parent. That's outlined here: Calling a parent window function from an iframe
So if you wanted to make a button in an iframe alter the contents of the iframe you could use
var elem = document.getElementById('myframe1').contentWindow.document.getElementById('myButton')
and then create a function in your parent window
function changeIt(){
document.getElementById('thingToChangeInIframe_ItsIDintheIframe').property = 'value';
}
and append it to the button with the code
elem.setAttribute('onclick', 'changeIt();');
If you have any clarifications to what you need just comment and we'll work those out. I'm sorry this doesn't use much jQuery but that's not really my forte, but I think the pure javascript is relatively self explanatory.
EDIT: I should clarify that if the iframe is on another domain then your options are pretty much all eliminated. For security reasons you can't mess with the settings on other people's pages when you load them in an iframe.

I get the error "createjs is not defined" even though I linked to the file

Here I have a very simple script.
stage = new createjs.Stage("myCanvas");
circle = new createjs.Shape();
circle.graphics.beginFill("red").drawCircle(0,0,40);
And my HTML
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css" href="mainStyle.css">
<script src="mainScript.js"></script>
<script src="create.js"></script>
</head>
<body>
<canvas id="myCanvas" width=1000 height=500 ></canvas>
</body>
</html>
Yet it does not work. I get this error:
"createjs is not defined"
But in my HTML it is linked to file. Is there anything else that could be causing this?
<script src="create.js"></script>
<script src="mainScript.js"></script>
Your code needs to execute AFTER including create.js - order matters.
There are a couple problems with your code.
First, and to directly answer the question, you have to load the library which constructs the createjs object before you load your script which instantiates it. Change the order of the script tags you have in your head.
Second, your instantiation will execute before the canvas element is added to the DOM. To fix this you need to listen for the document's onload event. Then execute your code.
Third, to get the red circle object to actually show up you need to append it to the canvas and then call the stages update method.
Ex:
window.onload = function(){
stage = new createjs.Stage("myCanvas");
circle = new createjs.Shape();
circle.graphics.beginFill("red").drawCircle(0,0,40);
stage.addChild(circle);
stage.update();
}
For angular projects the above answers might not be sufficient to solve the problem. What I did:
myAngularModule.service('preloadservice', function ($rootScope) {
if (typeof createjs === 'undefined') {
setTimeout(continueAfterWait, 500);
} else {
continueAfterWait();
}
var continueAfterWait = function () {
var queue = new createjs.LoadQueue();
//...
}
}

Graphics Not Displayed

I am simply trying to create some simple vector graphics using the Javascript Library Raphael. There should be a square object and a curved object, but nothing is being displayed. Can anyone help me. Thank you.
<html>
<head>
<script src="raphael.js"></script>
<script src="jquery-1.7.2.js"></script>
<script type="text/javascript"> //all your javascript goes here
var paper = Raphael("sample-2", 200, 100);
var rectPath = paper.path("M10,10L10,90L90,90L90,10Z");
var curvePath = paper.path("M110,10s55,25 40,80Z");
rectPath.attr({fill:"green"});
curvePath.attr({fill:"blue"});
</script>
</head>
<body>
<div id="sample-2" style="width:500px; height:500px;">
</div>
</body>
</html>
Move your script to after the <div id="sample-2" style="width:500px; height:500px;"> tag
Or some people prefer to use the onload handler, using jQuery for simplicity
$(function(){
// Your code that runs after the DOM is loaded
});
The key is that your code is accessing the DOM and it needs to run after the DOM has been built. Calling it from the onload handler or after the DIV you're using makes sure the element is ready to be interacted with.
You are running your Javascript far too early. Your browser will run Javascript as it reads it and if the DOM elements have not been loaded, it will not do anything.
Try this:
<html>
<head>
<script src="raphael.js"></script>
<script src="jquery-1.7.2.js"></script>
</head>
<body>
<div id="sample-2" style="width:500px; height:500px;"></div>
<script type="text/javascript">
//all your javascript goes here
var paper = Raphael("sample-2", 200, 100);
var rectPath = paper.path("M10,10L10,90L90,90L90,10Z");
var curvePath = paper.path("M110,10s55,25 40,80Z");
rectPath.attr({
fill: "green"
});
curvePath.attr({
fill: "blue"
});
</script>
</body>
</html>
Enjoy and good luck!
#JuanMendes it is slightly confusing, in the end the problem is that the js functions are called before the DOM is ready, elemets are still being created. I recommend using $(document).ready(function(){}) so that the scripts are executed only after the DOM has been created. I'm just explaining again because he is asking why he has to do that. For example if he did:
<html>
<head>
<script src="raphael.js"></script>
<script src="jquery-1.7.2.js"></script>
<script type="text/javascript">
$(document).ready(function(){ //all your javascript goes here
var paper = Raphael("sample-2", 200, 100);
var rectPath = paper.path("M10,10L10,90L90,90L90,10Z");
var curvePath = paper.path("M110,10s55,25 40,80Z");
rectPath.attr({fill:"green"});
curvePath.attr({fill:"blue"});
}
)
</script>
</head>
<body>
<div id="sample-2" style="width:500px; height:500px;">
</div>
</body>
</html>
That script should work because the script is executed after the DOM is ready.
P.S. On a side note if you want to manipulate content that is dynamically created on the fly, you need to attach the event handlers as click, blur, hover, etc... with a binding operation so the event is registered. Example:
$('#form').on('blur', '#input', function(){
// code
})
you can check out the Docs for binding at:
http://api.jquery.com/on/
and of .ready() at:
http://api.jquery.com/ready/

Categories