I will appropriate your help in creating my library in JavaScript and using Dojo.
It seems to me like a beginner problem but I can't seem to find the problem.
I am trying to create a library that is named 'edm'
The EDM.js is very simple:
define([
"dojo/_base/declare"
], function(declare) {
var EDM = declare("EDM", {
constructor: function() {
console.log("Hi EDM lib")
},
sayHi: function() {
console.log("Hi EDM lib")
}
});
edm = new EDM();
return edm;
});
The index.html to load it:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.9.2/dojo/dojo.js"
data-dojo-config="async: true"></script>
<script src="../src/EDM.js" type="text/javascript"></script>
</head>
<body>
<script src="main.js" type="text/javascript"></script>
</body>
</html>
and the main.js to use the edm is:
var newObject = edm;
The idea is basically to be able to use edm as prefix for the functions in EDM.js file.
However, when loading the index.htm file I get this error: "Uncaught ReferenceError: edm is not defined"
I will appropriate any direction.
Thanks
You should use the Dojo AMD loader in stead of loading the file yourself. Remove the following <script> tag:
<script src="../src/EDM.js" type="text/javascript"></script>
Then write the following code in main.js:
require([ "../src/EDM" ], function(edm) {
var newObject = edm;
});
The advantages of doing it this way is the following:
No need to add <script> tags that slow down the page when loading
No additional objects are added to the global scope
Of course, because no objects are on global scope, you cannot retrieve the edm object directly. That's why you got that ReferenceError.
To answer your question from the comments: yes and no. Actually, you're already adding edm to the global scope in EDM.js because you didn't put var in front of the following statement:
edm = new EDM();
However, the define() callback is only executed if it's called from the Dojo AMD loader inside a require() function. So that actually means that you have to load the AMD module first and then you will be able to access edm from global scope.
So in your case, the following will work as well:
require([ "../src/EDM" ], function(bla) {
var newObject = edm;
});
As you can see in the example above, we call our module bla, however, because you added it to global scope in EDM.js, it's now available. But, in order to add it to the global scope you have to properly load the module.
But be careful, adding objects to global scope is usually not encouraged, because they can be manipulated from any code which makes it hard to debug. There are a lot of other reasons why global variables are not a good idea, you can probably find a lot more information about it on the web.
Related
According to SO question Global javascript variable inside document.ready, I should be able to use global variables inside jQuery's document ready function. How can I do the same when my variable is declared inside a module?
<script type="module">
import settings from './config/settings.js';
var DAPP_VERSION = settings.version;
</script>
<script type="text/javascript">
var OTHER = 4;
</script>
<script type="text/javascript">
$(document).ready(function() {
console.log(OTHER);
console.log(DAPP_VERSION);
});
</script>
The above gives me:
Uncaught ReferenceError: DAPP_VERSION is not defined
config/setting.js contains:
export default {
version: "1.1",
rounds: 3,
cars: 20,
lapLenght: "short"
}
When you use the type="module" declaration the variables are not attached to the global scope automatically but to the module scope (that is different).
You have to attach them explicitely to window in order to avoid ambiguity and make them visible across the whole application.
window.DAPP_VERSION = settings.version;
http://jsfiddle.net/R6FNs/572/
I've been trying to implement basic Javascript with Webpack but have found a lot of trouble trying to get basic methods to work. I know it is probably a problem with my js loading before the DOM but all my fixes to this issue have been futile.
For instance, I'm trying to just run let taskinput = document.getElementById('new-task'); and when I search the taskinput variable in the console I get a return of Uncaught ReferenceError: taskinput is not defined(…).
I've tried quite a few different solutions to make this code operate after the DOM has loaded but nothing seems to work.
I tried the basic attempt of just putting my javascript file at the bottom of the DOM.
I've tried a basic js method like so:
document.onreadystatechange = function() {
if (document.readyState === "complete") {
initApplication();
}
function initApplication() {(... placed code in here ...)}
I've tried using jqueries
$( document ).ready(function() { });
I've tried injecting my javascript file into my HTML with the html-webpack-plugin
Is there something I'm missing with Webpack?
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sample Site</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<button id="new-task">click</button>
<script src="/assets/app.bundle.js"></script>
</body>
</html>
app.js
'use strict';
document.onreadystatechange = function() {
if (document.readyState === "complete") {
initApplication();
}
}
function initApplication() {
onButtonClick();
}
let onButtonClick = function() {
let taskInput = document.getElementById('new-task');
taskInput.onclick = alert('hey');
}
For instance, I'm trying to just run let taskinput = document.getElementById('new-task'); and when I search the taskinput variable in the console I get a return of Uncaught ReferenceError: taskinput is not defined(…).
First of all, module code in webpack bundles is run in its own scope, effectively in a closure, and not in the global scope (which you can access with window). Secondly, even if you go to your console and declare a variable with let, for example let a = 1, even though your console operates in the global (window) scope, if you try to do window.a you will get undefined because let variables do not get attached to the window scope. See this
Is there something I'm missing with Webpack?
Probably the fact that the code in your bundles, generated by webpack, does not run in the global scope, as explained above.
If you want to expose a variable to the global scope in order to be able to use it, for debugging purposes, in the console, declare it as window.variableName. Or you can add a breakpoint in your code, by adding debugger, after the variable you want to check out, without exposing it to the global scope.
Using the browser console you can only access variables which are declared globally, and if you define a JavaScript variable inside a function, it doesn't become a global variable. Also, variables declared using let never become global variables.
If you want to declare a variable globally, you can make it a property of window, for example:
window.taskinput = document.getElementById('new-task');
This might be useful for debugging, but avoid doing it in production code, as using global variables is considered a bad practice.
I've got some problems splitting up my jquery source code into more than one file. My real source code is a bit more complicated but the following simple example shows my problem very good. At first I would like to show you a working example with only one javascript file. Afterwards, I will describe what I tried in order to split the javascript into two files.
My html code looks like this ("./jquery" is a symbolic link to my local jquery download):
<html>
<head>
<script src="./jquery"></script>
<script src="./file1.js"></script>
</head>
<body>
<div id="content"></div>
</body>
</html>
The jquery source code in file1.js looks like this:
$(document).ready(function() {
var Test = (function() {
var content = $('#content');
var init = function() {
content.html('<p>test</p>');
};
return {
init: init
}
})();
Test.init();
});
After opening the page, "test" is displayed so that this example works as expected.
But now I want to put the whole Test part into another file file2.js. My html is basically the same but gets an additional line:
<script src="./file2.js"></script>
file1.js now contains only the call of the init function:
$(document).ready(function() {
Test.init();
});
and file2.js contains the definition of Test:
var Test = (function() {
var content = $('#content');
var init = function() {
content.html('<p>test</p>');
};
return {
init: init
}
})();
When I open the page, "test" is not displayed any more. In order to make sure that the init function is called at all, I added a console.log("test"); to the init function which is working fine. Therefore, I suppose that the function might be called before the DOM is ready, but actually I am pretty clueless. Maybe someone can give me a hint how to make that run.
Best regards and thanks in advance!
You can do several things according to your preferences...
1. Move your scripts to the end of the HTML file intead than in header...
<html>
<head>
</head>
<body>
<div id="content"></div>
</body>
<script src="./jquery"></script>
<script src="./file2.js"></script>
<script src="./file1.js"></script>
</html>
Think this problem secuencially... if you don't want to declare a var in each module referring an element in your DOM you need that the element exists first, then you can declare the "global" var to the module content. This way your original file2.js works.
Another way is to declare the content "global" to your module but init this in your init function...
var Test = (function() {
var content;
var init = function() {
content = $('#content');
content.html('<p>test</p>');
};
return {
init: init
}
})();
Now you can use the content variable in all of your module's functions.
Hope this helps, let me know.
file1 depends on file2. Make sure file1 comes ordinally after file2 in your html.
AngularJS offers dependency injection, modules, services, factories and all sorts of other goodness. Takes a bit to get used to, but well worth it IMO: much cleaner abstraction of javascript from DOM, data from presentation etc.
I appreciate your question is JQuery specific, but especially if you're starting a new site, I suggest you give Angular a try.
Modify your file2.js as follows:
var Test = {
content : $('#content'),
init : function() {
Test.content.html('<p>test</p>');
}
//, include other functions here
};
Modify your file1.js as follows:
$(document).ready(function(){
Test.init();
})
Now declare file2.js before your declare file1.js because file1.js is referencing a function from file2.js .
I can't seem to get the dojo example about declare to work. Example link : http://dojotoolkit.org/reference-guide/1.9/dojo/_base/declare.html#id3
Here is how I set it up:
/index.html:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.9.2/dojo/dojo.js" data-dojo-config="async: true"></script>
<script src="/my/Employee.js"></script>
<script src="/my/Person.js"></script>
<script>
var dojoConfig = {
parseOnLoad: true,
packages: [{
"name": "my",
"location": location.pathname.replace(/\/[^/]+$/, "") + "/my"
}
]
};
</script>
<script>
require(["my/Person"], function (Person)
{
var folk = new Person("phiggins", 42, "Tennessee");
});
require(['my/Employee'], function (Employee)
{
var matt = new Employee("Matt", 33, "California", 1000);
console.log(kathryn.askForRaise(), matt.askForRaise()); // 3600, 20
});
</script>
<title></title>
</head>
<body>
</body>
</html>
/my/Person.js
define(["dojo/_base/declare"], function (declare)
{
return declare(null, {
constructor: function (name, age, residence)
{
this.name = name;
this.age = age;
this.residence = residence;
}
});
});
/my/Employee.js
define(["dojo/_base/declare", "my/Person"], function (declare, Person)
{
return declare(Person, {
constructor: function (name, age, residence, salary)
{
// The "constructor" method is special: the parent class (Person)
// constructor is called automatically before this one.
this.salary = salary;
},
askForRaise: function ()
{
return this.salary * 0.02;
}
});
});
I tried to set a break point in all the call back method (return declare ...) and it never gets in there. It never gets into the require block's callback also.
Any help is appreciated
There are just a couple of small issues with your HTML file.
In your script tag that loads Dojo, you have added the data-dojo-config attribute. This is one of two ways to tell Dojo how it should behave - the other way is to define a global dojoConfig object. You've done both! So, remove the data-dojo-config attribute.
Now, Dojo needs to know about the config object just when it loads. So the dojoConfig has to be defined before the script tag that loads Dojo. Since async:true was just removed, add it to the config object instead.
Also, as Buffalo has mentioned, there's no need to include the Person and Employee modules with script tags. Dojo uses a pattern called AMD (Async Module Definitions) for loading modules. Basically that's the define and require functions you see here and there. These load the modules by very conveniently inserting the script tags for you (as long as you've told them where to find your namespace in the dojoConfig).
So the beginning of your head should be something like this:
<script>
var dojoConfig = {
parseOnLoad: true,
async: true,
packages: [{
"name": "my",
"location": location.pathname.replace(/\/[^/]+$/, "") + "/my"
}]
};
</script>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.9.2/dojo/dojo.js"></script>
That's actually all you need to make Dojo load your classes as it should. However, I think there's another small gotcha you may want to be aware of:
First, you require the Person module. Dojo will do its module loading magic behind the scenes and call function(Person) {...} when it's done. Next, you do the same with the Employee module and call function(Employee) {... }. You should keep in mind that you cannot guarantee that function(Person) is called before function(Employee). If, for example, you loaded Person and 100 other modules, function(Employee) would be called first.
(I mention this because I suspected that kathryn was a typo, and that you intended you use the folk object instead. I see now that that probably wasn't the case, but I'll leave it here anyway).
At least one change you'll want to make is to remove the lines
<script src="/my/Employee.js"></script>
<script src="/my/Person.js"></script>
One of the main selling points AMD modules is that you can have reusable modules without needing to add script tags everywhere in your html files.
Another debugging tip would be to check the inspector for any console errors or network errors.
I have an HTML file with a couple of external JavaScript files. Here's an example, somewhat simplified from the real HTML and JavaScript files:
<head>
<title>Control Page</title>
<script language="JavaScript" src="control.js"></script>
<script language="JavaScript">
var myWindow;
var controlForm;
function onPageLoad() {
myWindow = document.getElementById('iframe1');
controlForm = document.getElementById('ControlForm');
}
</script>
</head>
<body onLoad="onPageLoad()">
....
</body>
</html>
and then in my JavaScript file control.js:
function messageArrival(message) {
chatwindow.contentWindow.document.write(message)
}
function makeNetMeetingCall() {
controlForm.Status.value = ....
}
....
My question: When I validate the external JavaScript file, it complains about the variables that are defined in the main HTML file because they aren't declared anywhere in the *.js file. For example, MyEclipse's JavaScript editor complains that it sees variables used that are not defined in any visible scope. How can I declare these variables in the JavaScript file so that it's clear the variables are expected to be defined externally, something similar to "extern" in C.
This sounds more like an issue with MyEclipse's JS editor than your JS.
Unfortunately, I'm not familiar with MyEclipse. However, I do know of a good JS validator, JSLint, which accounts for global variables by having you define them in the comments:
/*global getElementByAttribute, breakCycles, hanoi */
You could declare the variables in the global scope of your control.js file.
var controlForm;
function makeNetMeetingCall() {
// ...
}
this won't interfere with the code that finally defines these objects, as it executes first. But even if it didn't, without an initializer it wouldn't overwrite anything.
If you need proof about the overwriting, just did this in the JS shell:
$ js
js> var x = 1;
js> print( x );
1
js> var x;
js> print( x );
1
js>
In reality, the problem is that your "IDE" isn't aware of them. It hasn't connected the HTML page JS variables with the external javascript file. One way around this, is to put the variable declaration into the external javascript file.
You need to declare the variables before the controls.js script is included:
<script language="JavaScript">
var myWindow;
var controlForm;
function onPageLoad() {
myWindow = document.getElementById('iframe1');
controlForm = document.getElementById('ControlForm');
}
</script>
<script language="JavaScript" src="control.js"></script>
That should stop eclipse from complaining, but the execution will be the same.