I've got a Closure environment running fine in my development environment. In a new folder & file, however, it is not working.
For example, take this test script:
goog.require('goog.dom');
console.log(goog);
console.log(goog.dom);
goog.dom.getElement('logout').innerHTML = "Heck";
That's all, plus the base.js in the HTML.
The first console.log shows an object with all the right stuff is in there. All the files are loaded into the DOM too.
The second console log, however, says undefined and so the last line doesn't work (obviously). The Chrome console error is Uncaught TypeError: Cannot call method 'getElement' of undefined.
What the heck is going on? It's the same if I try XhrIo. It's not finding the methods on the goog object.
Thanks.
Documentation:
Note: Do not put your goog.require() statements in the same script tag
as the entry point to code that uses the goog.required functions or
classes. A goog.require() call adds code to the document after the
script tag containing the call.
So
<script>
goog.require('goog.dom');
console.log(goog.dom);
</script>
prints undefined. But
<script>
goog.require('goog.dom');
</script>
<script>
console.log(goog.dom);
</script>
or
<script>
goog.require('goog.dom');
addEventListener('load', function() {
console.log(goog.dom);
});
</script>
prints an Object.
FYI, there are other possible workflows. For example, the closurebuilder.py script can help to load all the required files ahead of time.
Related
I'm experimenting with the google closure library, and am working through the
official XhrIo guide.
I ran into some trouble with the xhr-quick2.js example, reproduced below:
goog.require('goog.net.XhrIo');
var xhr = new goog.net.XhrIo();
goog.events.listen(xhr, goog.net.EventType.COMPLETE, function(e) {
obj = this.getResponseJson();
log('Received Json data object with title property of "' +
obj['title'] + '"');
alert(obj['content']);
});
function getData(dataUrl) {
log('Sending simple request for ['+ dataUrl + ']');
xhr.send(dataUrl);
}
When run, I receive the error:
Uncaught TypeError: Cannot read property 'XhrIo' of undefined
If I move the event listener and xhr instantiation to within the getData
function (which is called in the body's onload), all is well.
goog.require('goog.net.XhrIo')
function getData(dataUrl) {
var xhr = new goog.net.XhrIo()
goog.events.listen(xhr, goog.net.EventType.COMPLETE, function(e) {
obj = this.getResponseJson()
log(`Received Json data object with title property of "${ obj["title"] }"`)
alert(obj["content"])
})
log(`Sending simple request for [${ dataUrl }]`)
xhr.send(dataUrl)
}
function log(msg) {
document.getElementById('log').appendChild(document.createTextNode(msg));
document.getElementById('log').appendChild(document.createElement('br'));
}
I assume this is because goog.require hasn't finished importing net when
goog.net.XhrIo is instantiated on line 3 of the first code sample. I suppose
the ideal solution is to run all my code through the closure compiler, but I'm
just experimenting and other parts of the
documentation
imply my workflow is acceptable for development.
Is there anything I can manually source in my html that would eliminate this
problem? Is there some other approach I should take?
It's been a few years since I last played with closure, but I don't think you can just use require without the dependency compiler.
Closure works by scanning your JavaScript files for goog.module and goog.require to write a deps.js file. This file has to be loaded first before your JavaScript files. It lists all the modules used by the code and loads them in the correct order.
If deps.js comes first, then goog.net will be loaded in by the time it gets to your code. The code goog.require('goog.net.XhrIo') on line 1 will at this time be ignored.
According to the docs the goog.require will insert a <script> tag after the current <script> being executed. So if you want to skip the step of using deps.js then you'll have to wrap everything in a document ready callback, or manaully add goog.require to a JavaScript file that is loaded before your code.
I think it's not worth the effort and easier to just use the closure dependency writer to create the deps.js file.
There's a quick tutorial here:
https://www.safaribooksonline.com/library/view/closure-the-definitive/9781449381882/ch01.html
Here's the CLI for writing the deps.js file:
python ../closure-library/closure/bin/calcdeps.py \
--dep ../closure-library \
--path my_stuff.js \
--output_mode deps > deps.js
For uncompiled mode, the required document must be pre-loaded. So in your html document you would have:
<!DOCTYPE html>
<html>
<head>
<script src="path/to/closure/base.js"></script>
<script>
goog.require('goog.net.XhrIo');
</script>
<script src="mysource.js"></script>
</head>
</html>
Any dependencies must be loaded in a separate script tag. Then your code samples above should work.
I'm playing around with Google Drive API, and noticed that they are calling a handleClientLoad function onload of the client.js.
<script src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script>
Trying to avoid creating globals, I thought I would start with creating another js file, that would contain a module pattern and return handleClientLoad.
var module = (function (window, $) {
'use strict';
var module = {
handleClientLoad: function () {
console.log('ok, can access');
}
};
return module;
}(window, jQuery));
And then I assumed I could just call the handleClientLoad by doing module.handleClientLoad, but that doesn't seem to be working.
<script src="scripts/main.js"></script>
<script src="https://apis.google.com/js/client.js?onload=module.handleClientLoad"></script>
Questions:
Is it possible to call the module.handleClientLoad from onload of client.js?
Appending onload and calling a function from a script file seems sloppy and obtrusive, no? Is there a cleaner way to know when the client.js has loaded?
Have you tried debugger, and are you sure module. hanfleClientLoad exists at the time the callback is fired?
You can poll for the existence of gapi.client as a global object. Give it a few milliseconds to initialise before invoking its methods.
I found that jQuery.getScript() worked quite nicely for this. Documentation here.
Instead including the <script> tag in the html page, I simply included the following line in my js file:
jQuery.getScript( "https://apis.google.com/js/api.js", handleClientLoad );
It might require slight tweaking for the way you structured your module, but I think this will be easier than passing a parameter to your <script> tag (at least I found it easier).
I know this is old, but I was messing around with this because this was related to a question on a test I was studying for. You can use onload like this when you call the script:
<script src="https://apis.google.com/js/client.js" onload="handleClientLoad()"></script>
For anyone wanting to know why this won't work:
<script src="https://apis.google.com/js/client.js">handleClientLoad()</script>
It's because any code between a script tag with "src=" in it will be ignored.
And I'm not sure why using onload= in the script tag calling the external script is any more obtuse than appending ?onload=module.handleClientLoad to the source? But that's just me.
In the end, I'm not sure why exactly this was a question on the test, because based on searching, this doesn't seem to be a common thing that anyone does.
Why does a linked JavaScript file sometimes not work when it is included at the top of the page and not at the bottom?
<script type="text/javascript" src"..."></script>
For example, if you want to manipulate DOM items, and those are not yet existing, it won't work. If the JavaScript file is included in the head, the body is not existing yet, but if you include it at the end of the body, those items are valid.
If you don't want to rely on this behaviour, you may define a callback, which is run, when the document is ready, i.e. when the whole of the DOM is loaded already.
This is what e.g. jQuery achieves with $(document).ready(function() {}), or more shortly $(function () {});. In vanilla JavaScript (using modern browsers, so IE9+) this can be achieved using
document.addEventListener("DOMContentLoaded", function() {
// code...
});
The best way to know why is it not working is by checking for JS error. Try to find out what errors you are getting when the script has been included at the top. As mentioned in the other response it can be because of DOM items. You can circumvent this issue by adding a "defer" tag to the script.
It can also be because of some JS object you are expecting to be present when this script runs. For example if your script tag is serving a JSONP request then you must have the function that processes the data. Otherwise you will get a "undefined function" error when the script runs.
JS code is executed instruction by instruction from top to bottom.
The code that calls a function needs to be under that functions definition.
This code works:
var func = function()
{
alert('it works');
};
func();
While this doesn't:
func();
var func = function()
{
alert('it works');
};
It throws an undefined error. The reason for this is that JS compiler is not aware of the func definition at the time it tries to call it.
Same goes for the JS files included in your HTML page. You can include them at the bottom as long as there are not dependencies in above sections, or, if they do not try to manipulate HTML code before page load.
I'm using a sample project from here.
Suppose I need to export some function from my module to provide some JavaScript API to the clients of my service.
But the declarations in my .js files are not visible outside RequireJS!
I add the following block to jquery-require-sample/webapp/app.html:
<script type="text/javascript">
$(document).ready(function() {
$('body').alpha().beta();
});
</script>
It fails: Uncaught TypeError: Object [object Object] has no method 'alpha'.
Is it possible to do what I want?
Based on the code you provided I'm assuming you added your code after the existing script tag in app.html. I think what you're seeing is a timing issue. After you load the page, take a look at the <head> tag and you should see script tags in the following order:
the "require" script
your new script
alpha
beta
so it's running your script before the alpha and beta are run. The reason is because require will process the first script, but not execute the "meat" of main.js until all it's dependencies are run (alpha and beta).
I hope this helps. The following changes to your code may also illustrate what's going on. the setTimeout gives alpha and beta a chance to load:
<script type="text/javascript">
setTimeout(function(){
$(document).ready(function() {
$('body').alpha().beta();
});
}, 5000);
</script>
I'm getting a JS error on displaying a page: Nothing concrete is specified but the line where it seems to be thrown. When looking into the source code of the page, I see the error is thrown inside the following script, but I can't understand why! It's only about loading images!
<SCRIPT language=JavaScript>
<!--
function newImage(arg) {
var rslt = new Image();
rslt.src = arg;
return rslt;
}
function changeImages(a, b) {
a.src = b;
}
newImage("\/_layouts\/images\/icon1.gif");
newImage("\/_layouts\/images\/icon2.gif");
// -->
</SCRIPT>
The error I am getting is when clicking on a drop down context menu on a page, for this line:
newImage("\/_layouts\/images\/icon1.gif");
The object doesn't accept this property or method
Code: 0
I really don't see what could happen... Any tips on what may be happening here?
Have you tried loading your scripts into a JS debugger such as Aptana or Firefox plugin like Firebug?
Why are you escaping the forward slashes. That's not necessary. The two lines should be:
newImage("/_layouts/images/icon1.gif");
newImage("/_layouts/images/icon2.gif");
It is hard to answer your question with the limited information provided:
You are not showing the complete script
You never said what the exact error message is, or even what browser is giving the error.
Which line number is the error supposedly coming from?
I'd recommend using Firebug in firefox for debugging javascript if you aren't already. IE tends to give bogus line numbers.
And as others have already said, the language attribute for script tags is deprecated.
Write proper xml with the " around attributes.
<script type="text/javascript">
function newImage(arg) {
var rslt = new Image();
rslt.src = arg;
return rslt;
}
function changeImages(a, b) {
a.src = b;
}
newImage("/_layouts/images/icon1.gif");
newImage("/_layouts/images/icon2.gif");
</script>
should your script block not be:
<script type="text/javascript">
?
For starters, start your script block with
<script type="text/javascript">
Not
<script language=JavaScript>
That's probably not the root of your problem, but since we can't see your script, that's about all we can offer.
You probably need to enlist the help of a Javascript debugger. I've never figured out how to make the various debuggers for IE work, so I can't help you if you're using IE.
If you're using Firefox or you CAN use Firefox, make sure you have a Tools / Javascript Debugger command. (If you don't, reinstall it and be sure to enable that option.) Next, open up the debugger, rerun the problem page, and see what comes up.
How are you calling changeImages? It looks as though you are not saving a reference to the images returned by newImage. You probably want to save the results of newImage and pass that to the changeImages routine. Then changeImages should look like this:
function changeImages(a, b) {
a.src = b.src;
}
You also may want to ensure that the images have finished loading before calling changeImages.
You've posted the routine that throws the error, without posting the error or showing us how you are calling it. If none of the answers posted fix your problem then please post some detail about how you are calling the method, which specific line the error is on, and what the error message is.
You firebug to debug.
http://www.mozilla.com/en-US/products/download.html?product=firefox-3.0.10&os=win&lang=en-US
https://addons.mozilla.org/en-US/firefox/addon/1843
JSLint is also a nice resource.
http://www.jslint.com/
Using CDATA instead of the <!-- // -->
http://www.w3schools.com/XML/xml_cdata.asp
<script type="text/javascript">
<![CDATA[
]]>
</script>