Doubts on js scopes and external classes - javascript

I am new to oop in js. I'm working on a HTML5 game using Box2dWeb and I decided that I should it make it completely oop based. So I created a physics class like this in a separate file called physics.js
(function(window)
{
function Physics(element, scale){
//init world logic
}
Physics.prototype.debug = function(){
//debug draw logic
};
Physics.prototype.step = function(dt){
//step logic
};
//making this class(or object) visible on the global scope
//so i can create vars of this type anywhere in the application
window.Physics = Physics;
}(window));
Now I have a main game.js file where I init all my game physics, graphics, assets, etc. Here are the contents:
(function(){
function init(){
var physics = new Physics(document.getElementById("b2dCanvas"), 30);
console.log(physics);
physics.debug();
}
window.addEventListener("load", init);
}());
This file, initializes my Physics object without any problem. Ultimately this is what i want. Great! But, prior to this, the file was like this, without the init() function.
(function(){
var physics = new Physics(document.getElementById("b2dCanvas"), 30);
console.log(physics);
physics.debug();
}());
This apparently threw the error Uncaught TypeError: Cannot call method 'getContext' of null. This meant, the physics constructor was being called(and naturally element at this point was null) without me invoking it. How was that possible? My self-executing function in game.js should have initialized the Physics object right? What am I missing?

Your script was running before the HTML was fully parsed, and before your canvas element was added to the DOM. When you added window.addEventListener("load", init);, you made it run when the window.onload event was triggered. At that moment, the DOM was fully parsed.
In modern browsers, you could also replace that with
document.addEventListener("DOMContentLoaded", init);
That event will trigger when the DOM is ready, but doesn't wait for other resources like images (unlike onload).
Or, just add all your scripts just before closing the body tag. At that point, every HTML tag in the body will already have been inserted into the DOM.

This meant, the physics constructor was being called(and naturally
element at this point was null) without me invoking it. How was that
possible?
Becase you are calling it, using this syntax:
(function(){...}());
^^
call
That will call the function when the script is parsed as part of the normal page script parsing, and will not wait for the document/DOM to be ready for you to manipulate. You'll want to either use some sort of document 'load' listener, or remove that particular syntax and call the function manually when you're sure the DOM is ready.

My guess is that you're calling the function before the DOM is ready. Put the script include at the bottom of your code or even better just wrap all init a window load listener.

The error has nothing to do with the Physics object not being ready. It has to do with the code inside of it trying to reference an element that is not on the page when the code is called.
It is simple you were calling
document.getElementById("b2dCanvas")
before the element was loaded on the page. When getElementById does not find anything it returns null.
So what your code looks like if you were to inspect it
var physics = new Physics(null, 30);
and element is null
function Physics(element, scale){
var ctx = element.getContext("2d"); //<-- where the error occurs since element is null
Hence the error: Uncaught TypeError: Cannot call method 'getContext' of null.
When you listen for the load event, that means the element is there so it loads with no problem. Other way of dealing with it is to add the script to the bottom of the page.

Related

JavaScript - overwriting .onload prototype of HTMLImageElement(s)

Is it possible to bind an onload event to each image, declaring it once? I tried, but can't manage to get it working... (this error is thrown: Uncaught TypeError: Illegal invocation)
HTMLImageElement.prototype.onload = function()
{
console.log(this, "loaded");
};
P.S: I also tried returning this, but doesn't seem to be the issue here... any suggestions / explanations on why my current code isn't working?
You can't set a handler on the prototype, no.
In fact, I'm not aware of any way to get a proactive notification for image load if you haven't hooked load on the specific image element, since load doesn't bubble.
I only two know two ways to implement a general "some image somewhere has loaded" mechanism:
Use a timer loop, which is obviously unsatisfying on multiple levels. But it does function. The actual query (document.getElementsByTagName("img")) isn't that bad as it returns a reference to the continually updated (live) HTMLCollection of img elements, rather than creating a snapshot like querySelectorAll does. Then you can use Array.prototype methods on it (directly, to avoid creating an intermediary array, if you like).
Use a mutation observer to watch for new img elements being added or the src attribute on existing img elements changing, then hook up a load handler if their complete property isn't true. (You have to be careful with race conditions there; the property can be changed by the browser even while your JavaScript code is running, because your JavaScript code is running on a single UI thread, but the browser is multi-threaded.)
You get that error because onload is an accessor property defined in HTMLElement.prototype.
You are supposed to call the accessor only on HTML elements, but you are calling the setter on HTMLImageElement.prototype, which is not an HTML element.
If you want to define that function, use defineProperty instead.
Object.defineProperty(HTMLImageElement.prototype, 'onload', {
configurable: true,
enumerable: true,
value: function () {
console.log(this, "loaded");
}
});
var img = new Image();
img.onload();
Warning: Messing with builtin prototypes is bad practice.
However, that only defines a function. The function won't be magically called when the image is loaded, even if the function is named onload.
That's because even listeners are internal things. It's not that, when an image is loaded, the browser calls the onload method. Instead, when you set the onload method, that function is internally stored as an event listener, and when the image is loaded the browser runs the load event listeners.
Instead, the proper way would be using Web Components to create a custom element:
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
var img = document.createElement('img');
img.src = this.getAttribute('src');
img.addEventListener('load', function() {
console.log('loaded');
});
this.appendChild(img);
};
document.registerElement('my-img', {prototype: proto});
<my-img src="/favicon.ico"></my-img>
There is not much browser support yet, though.
This provides a notification for any image loading, at least in Opera (Presto) and Firefox (haven't tried any other browser). The script tag is placed in the HEAD element so it is executed and the event listener installed before any of the body content is loaded.
document.addEventListener('load', function(e) {
if ((!e.target.tagName) || (e.target.tagName.toLowerCase() != 'img')) return;
// do stuff here
}, true);
Of course, by changing the filtering on tagName it will also serve to respond to the loading of any other element that fires a load event, such as a script tag.
I've written something similar some time ago to check if an image is loaded or not, and if not, show a default image. You can use the same approach.
$(document).ready(function() {
// loop every image in the page
$("img").each(function() {
// naturalWidth is the actual width of the image
// if 0, img is not loaded
// or the loaded img's width is 0. if so, do further check
if (this.naturalWidth === 0) { // not loaded
this.dataset.src = this.src; // keep the original src
this.src = "image404.jpg";
} else {
// loaded
}
});
});

A function that gets called outside the flow of control?

So, I've been trying to understand a specific js library by running it through my browser's debugger, and something happens that just confuses me.
I first encountered this in the Phaser game library, but I've seen it another library as well. I'll use Phaser as an example:
<script>
(function(){
var game = new Phaser.Game(800, 600, Phaser.CANVAS, '');
game.state.add('Game', Game);
game.state.start('Game');
})();
</script>
So that anonymous function finishes setting some things up, and I step over and out of that function, and after a couple more steps (the pointer just sitting at the top of the html doc in the meantime) my program out of nowhere ends up here:
Phaser.Device._readyCheck = function () {
var readyCheck = this._readyCheck;
....
}
It didn't look like anything within my flow of control called that function, so how did I get here? What's calling this function? I've read a bit about 'asynchronous functions' and that sounds like a pretty good explanation, but the stuff I've glanced at on Google don't really explain anything well, so I can't say I understand it enough to be sure. I'm relatively new to JavaScript.
The library sets up event handlers for various events which then get called asynchronously. In this case, event handlers are setup to to watch for the completion of the loading of the DOM, among other things, so that the internal state of the library can be initialized. You can see how it happens in the source
src/system/Device.js:Phaser.Device._readyCheck
and Phaser.Device.whenReady in the same file. With a little searching you can easily find who calls whenReady.
The function you pointed out at the top is a self-executing function. (); causes everything contained in the preceding parentheses to be executed immediately.

How can I use onclick method?

I made object of window by javascript. And I want to add some functions to the windows object. But .onclick, .setattribute, and .addEventListener do not work with my object. Even mouse cursor also can not be changed when I give value of css or javascript. It is on 499 line of my source. Could you tell me why these do not work? Should I change language? Is it bug?
function seSizeValue(theNumOfWins,outlayerId){
var seSizeValue;
var seSizeValueId;
seSizeValueId = "seSizeValueId" + theNumOfWins;
seSizeValue = document.createElement("div");
document.getElementById(outlayerId).appendChild(seSizeValue);
seSizeValue.setAttribute("id",seSizeValueId);
document.getElementById(seSizeValueId).setAttribute("class","seSizeValueCSS");
document.getElementById(seSizeValueId).onclick = function(event) {seSizeChange();};
return seSizeValueId;
}
"""document.getElementById(seSizeValueId).onclick = function(event) {seSizeChange();};"""
This line does not work. I already tried to change '.setattribute', and '.addEventListener'.
There was no error. But it does not work.
https://drive.google.com/file/d/0B4p8lZSEMXcqN0FzQTkyVW8wRGc/view
this is my full source.
It does not work on my source.
Should I use jQuery?
I think so Save the window reference in a variable with a sentence like that
var new_window = window.open
is posible ser manually events in DOM subelements using jquery with On() method o directly with javascript.
Im not sure about that but I suggest a posible solution.
There is no function defined as "seSizeChange" so when the onclick event fires, it's going to run a function that doesn't exist.
Also, when you call this "seSizeValue" function, are you calling it after the specified element has been loaded on to the document?
If not, the code won't be able to find the specified element.
document.getElementById(seSizeValueId).onclick = function(event) {seSizeChange();};
is definitely wrong.
See http://jsfiddle.net/2txugfq5/
Everything is working except fact that 'seSizeChange' is not defined.
John makes good points.
See here http://jsfiddle.net/hqw7ocwj/2/ , works fine. No changes to anything of importance from your code, apart from:
$(document).ready(function() {});
and:
function seSizeChange() {
alert("seSizeChange method fired.");
}
Main points:
1. Ensure your JavaScript is called only after the DOM is ready: $(document).ready(function{});
2. Make sure that the method in the function that the onclick refers to has been defined.

Should I use IIFE or window onload to initialize?

Both of the following code snippets worked:
Using IIFE in js file:
(function initialize() {
txtInput = document.getElementById('txtInput');
txtResult = document.getElementById('txtResult');
txtInput.value = "0";
txtResult.value = "0";
}());
Calling initialize() on window load event in html file:
window.addEventListener('load', initialize, false);
Is one a better approach than other; in terms of performance or otherwise? As it stands right now, I am leaning more towards adding event listener to the window object, because it is more readable.
It depends when you want the code to run. If you want the code to execute ASAP you can use an IIFE but there is really no point using an IIFE if you don't use it to protect your variables and/or not polluting the global scope.
(function initialize() {
// do somthing
}());
or
// do somthing
will execute at the same point in time.
If you want to defer execution there are three points in time usually used by web devs. <script>s at bottom, DOMContentLoad and window.onload.
<script>s at bottom will execute after they are fetched from the server.
DOMContentLoaded basicly execute as soon as </html> has been read by the HTML parser.
very simplified window.onload executes after all CSS, <img>es and <script>s have been loaded.
Note that in reality, with attributes like async and defer on <script>s, this is more complex, . This is why there is a mountain of resource loaders available.
Probably the result of each approach matters, not the performance. The first approach runs immediately while the second one waits until dom is ready. The end result is that in your first approach, you may end up getting undefined for both textInput and textResult, unless you place the script on the bottom of page.
The IIFE in a script element (whether inline or external loaded) just before the closing body element certainly appears most clever. It confuses the hell out of commoners.
document.addEventListener('DOMContentLoaded', function() ... is easy to understand. It's almost plain English: event listens for DOM content loaded. So, poof, the majesty is gone. Note this is distinct from window onload.
I use the event listener, as it adheres to the KISS principle, it's a purpose built tool, and it does what it says it does (which probably includes stuff/functionality I haven't even considered).

JavaScript Module Pattern(function(){})(); vs block statement

I saw an answer to some question earlier that used my "Weird" example, and I was wondering if there was any benefit to either method.
Some HTML:
<span id="them">Hey</span>
<span id="me">Hey</span>
What is the difference between:
(function()//doing this
{
them.innerHTML = "Weird<br>";
})();
me.innerHTML = "Not so weird<br>";//and doing this
And even, why do people use window.onload when they can put scripts at the bottom of the body? Or is it just a matter of personal preference?
Your first code snippet: is a Module Pattern Or Immediately Invoked Function Expression(IIFE)
(function()//doing this
{
them.innerHTML = "Weird<br>";
})();
This when encountered by the Javascript compiler will immediately invoke the function when it encounters (); and keeps the variables and functions within its scope.
You must read Java-script Design Patterns to better understand its use and benefits.
Second code snippet: is just a JavaScript statement.
me.innerHTML = "Not so weird<br>";//and doing this
This when encountered by the JavaScript compiler will immediately execute it.
Remember both snippets executions depends on where its placed.
So, to answer your other question. window.onload is an event fired when the HTML DOM is fully loaded and browser can read all its elements.
There is no difference between your two examples. Your first example creates an anonymous function that immediately executes (called an Immediately Invoked Function Expression). Your second example just executes the same code.
You have to wait until a browser reads all the HTML elements before you can change them with JavaScript. The onload event fires when the page has fully loaded and at that time the browser knows about all the HTML elements. However, a browser won't fire the onload event until after the page has fully loaded, which means the browser will wait until after a large image has fully loaded -- even though the browser has already parsed the rest of the HTML -- making your JavaScript needlessly wait until the image finishes loading. Because the browser knows about all the HTML before the image finishes loading there is no reason to prevent JavaScript from executing earlier.
Once people discovered that onload was waiting too long before allowing the JavaScript to execute, people started putting their JavaScript right before the closing <body> tag (without using onload), so that the JavaScript would execute as soon as all the HTML had been parsed (except for the closing <body> tag), and that way their JavaScript could start executing sooner than when using window.onload.
Now JavaScript libraries like jQuery have an event that fires when the browser knows about all the HTML -- even though the page hasn't fully loaded (e.g. due to images that haven't fully loaded).
In your simple example, there is no difference between the result of the two cases. Both accomplish the same thing.
The reason for using this structure:
(function()//doing this
{
them.innerHTML = "Weird<br>";
})();
Is to create a function scope that can be used to hold private or temporary variables or to create a closure without exposing the variables inside to the outside world.
As for your second question, window.onload fires at a different time than scripts placed at the end of the body because window.onload fires when all synchronous resources needed by the page have finished loading (like scripts and images). It can be used, either to get notified when all these resources are done loading or it can be used by code that cannot be easily located at the end of the body as a safe time when the page is ready though it is usually not necessary to wait that long just for the DOM to be safe.
In the above case there is not advantage in using the first method.
But the first method is preferable in scenarios where you need to create some variables/method but does not want to pollute the global name space
After some thought, there is a benefit over writing the extra (function(){})(); as shown (imagine the code is huge):
(function()
{
var text = "Span 'them' text!";
them.innerHTML = text;
//Many lines of code
})();
(function()
{
me.innerHTML = text;//will throw 'text is undefined'
//Many lines of code
})();
This will be quite handy for debugging many lines of code, the debugger would recognise the error and "point" straight to it.
Whereas with this example:
var text = "Span 'them' text!";
them.innerHTML = text;
//Many lines of code
//...
me.innerHTML = text;
Your "error" (which the debugger is perfectly happy with) would be much harder to track down.

Categories

Resources