Debugging scripts added via jQuery getScript function
JavascriptJqueryVisual StudioDebuggingJavascript Problem Overview
I have a page that dynamically adds script references via jQuery's $.getScript
function. The scripts load and execute fine, so I know the references are correct. However, when I add a "debugger" statement to any of the scripts to allow me to step through the code in a debugger (such as VS.Net, Firebug, etc.), it doesn't work. It appears that something about the way jQuery loads the scripts is preventing debuggers from finding the files.
Does anybody have a work-around for this?
Javascript Solutions
Solution 1 - Javascript
Ok, so it turns out that the default implementation of the $.getScript()
function works differently depending on whether the referenced script file is on the same domain or not. External references such as:
$.getScript("http://www.someothersite.com/script.js")
will cause jQuery to create an external script reference, which can be debugged with no problems.
<script type="text/javascript" src="http://www.someothersite.com/script.js"></script>
However, if you reference a local script file such as any of the following:
$.getScript("http://www.mysite.com/script.js")
$.getScript("script.js")
$.getScript("/Scripts/script.js");
then jQuery will download the script content asynchronously and then add it as inline content:
<script type="text/javascript">{your script here}</script>
This latter approach does not work with any debugger that I tested (Visual Studio.net, Firebug, IE8 Debugger).
The workaround is to override the $.getScript()
function so that it always creates an external reference rather than inline content. Here is the script to do that. I have tested this in Firefox, Opera, Safari, and IE 8.
<script type="text/javascript">
// Replace the normal jQuery getScript function with one that supports
// debugging and which references the script files as external resources
// rather than inline.
jQuery.extend({
getScript: function(url, callback) {
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = url;
// Handle Script loading
{
var done = false;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function(){
if ( !done && (!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") ) {
done = true;
if (callback)
callback();
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
}
};
}
head.appendChild(script);
// We handle everything using the script element injection
return undefined;
},
});
</script>
Solution 2 - Javascript
With JQuery 1.6(maybe 1.5) you could switch to not using getScript, but using jQuery.ajax(). Then set crossDomain:true and you'll get the same effect.
The error callback will not work. So you might as well not set it up like below.
However, I do setup a timer and clear it with the success. So say after 10 seconds if I don't hear anything I assume the file was bad.
jQuery.ajax({
crossDomain: true,
dataType: "script",
url: url,
success: function(){
_success(_slot)
},
error: function(){
_fail(_slot);
}
})
Solution 3 - Javascript
For those who would like to debug scripts and use them with $.when (James Messinger's answer doesn't work well with $.when) I suggest to use this code:
var loadScript = function (path) {
var result = $.Deferred(),
script = document.createElement("script");
script.async = "async";
script.type = "text/javascript";
script.src = path;
script.onload = script.onreadystatechange = function (_, isAbort) {
if (!script.readyState || /loaded|complete/.test(script.readyState)) {
if (isAbort)
result.reject();
else
result.resolve();
}
};
script.onerror = function () { result.reject(); };
$("head")[0].appendChild(script);
return result.promise();
};
All credits and glory go to Benjamin Dumke-von der Ehe and his article: jQuery script insertion and its consequences for debugging
This works well with $.when and the script is totally visible and debuggable. Thanks.
Solution 4 - Javascript
Try this,
jQuery.extend({
getScript: function(url, callback) {
var head = document.getElementsByTagName("head")[0];
var ext = url.replace(/.*\.(\w+)$/, "$1");
if(ext == 'js'){
var script = document.createElement("script");
script.src = url;
script.type = 'text/javascript';
} else if(ext == 'css'){
var script = document.createElement("link");
script.href = url;
script.type = 'text/css';
script.rel = 'stylesheet';
} else {
console.log("Неизветсное расширение подгружаемого скрипта");
return false;
}
// Handle Script loading
{
var done = false;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function(){
if ( !done && (!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") ) {
done = true;
if (callback)
callback();
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
}
};
}
head.appendChild(script);
// We handle everything using the script element injection
return undefined;
}
});
Solution 5 - Javascript
There is an easy way to debug it with Chrome.
1- Write a console.log("something") on the line that you want to debug.
2- Watch the console for that log.
3- Click on the address link in front of the log.
4- Set break-point on that line.
Solution 6 - Javascript
All the answers are on this page somewhere but I wanted to summarise for future readers.
Checking the (dynamically added) File Downloads
Using Chrome, you can see Javascript files added using $.getScript (http://api.jquery.com/jQuery.getScript/) under the Network panel on the XHR tab; note they don't appear under the JS tab.
Debugging the File
-
Setting a break point in code. As mentioned in other answers\comments you can insert a
debugger;
statement in the Javascript code. This will invoke the browser's debugger. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger for more information.
-
Using a Source Map to make it appear in the browser's Sources panel (test in Chrome).Add
//# sourceURL=whatevername.js
to the end of your file. [The file appears under (no-domain) in Chrome source panel].
See: https://stackoverflow.com/questions/9092125/how-to-debug-dynamically-loaded-javascript-with-jquery-in-the-browsers-debugg and https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map for more information.
- Override $.getScript to always use an external reference as per the accepted answer (I haven't tested).
Solution 7 - Javascript
To avoid a lot of extra coding you can try this. In the file where you've declared your $('document').ready() (or any other file your debugger will access), add something like...
$.debug = function(name) {
var n = name;
}
Add a breakpoint to the assignment line in your debugger. Then, in any other js-file you load with $.getScript() you can add ...
$.debug("some string to identify this point of code");
Whenever this line is executed your debugger will stop and wait for your command. Step out of the $.debug function and that's it!
Solution 8 - Javascript
In Firefox 38.6.0 with Firebug 2.0.14 when I go to the Script tab, I see an entry in the drop down menu like jquery-1.11.1.js line 338 > eval
and that contains the loaded script. Plus, looking at the code in this version of jQuery, it looks like internally $.getScript()
is using $.get()
and ultimately $.ajax()
, the only difference being the eval()
part for the script, which is handled by the jQuery globalEval()
function:
// Evaluates a script in a global context
// Workarounds based on findings by Jim Driscoll
// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
globalEval: function( data ) {
if ( data && jQuery.trim( data ) ) {
// We use execScript on Internet Explorer
// We use an anonymous function so that context is window
// rather than jQuery in Firefox
( window.execScript || function( data ) {
window[ "eval" ].call( window, data );
} )( data );
}
},
Solution 9 - Javascript
This combines the OP solution with that of Eric. Override the necessary jquery to treat gets as always crossdomain and they'll show up perfectly without breaking a single thing in the jquery promise implementation.
jQuery.extend({
get: function (url, data, callback, type) {
// shift arguments if data argument was omitted
if (jQuery.isFunction(data)) {
type = type || callback;
callback = data;
data = undefined;
}
return jQuery.ajax({
url: url,
type: "GET":,
dataType: type,
data: data,
success: callback,
crossDomain: true
});
}
});