I'm trying to load Cocoen through AJAX with JQuery. It works, but only some of the time.
I have this function to ajax load a javascript file, when it's needed:
jQuery.cachedScript = function( url, options )
{
// Allow user to set any option except for dataType, cache, and url
options = $.extend( options || {}, {
dataType: "script",
cache: true,
url: url
});
// Use $.ajax() since it is more flexible than $.getScript
// Return the jqXHR object so we can chain callbacks
return jQuery.ajax( options );
};
Then when I detect something with the "cocoen" class, it loads it in as needed like so:
if ($('.cocoen').length)
{
$.when(
$.cachedScript( "/includes/jscripts/cocoen/js/cocoen.min.js"),
$.cachedScript( "/includes/jscripts/cocoen/js/cocoen-jquery.min.js"),
$.Deferred(function( deferred ){
$( deferred.resolve );
})
).done(function(){
$('.cocoen').cocoen(); // image comparison slider
});
}
I don't know what causes it, but sometimes it seems to do the "done" function before it's properly loaded? As in chrome tools it says:
"Uncaught TypeError: $(...).cocoen is not a function". That is despite the network tab clearly showing both JS files for cocoen being loaded.
What am I doing wrong here? If I refresh, it then works. Seems random, but there must be something I'm not taking into account here.
Related
I'd like to improve the AJAX feedback in my application (after I POST a remote form in my modals, etc.).
I already have some nice feedback showing a loading animation
$(document).ajaxStart(function(){
$('.loading-feedback').show()
})
$(document).ajaxStop(function(){
$('.loading-feedback').hide()
})
But I realize it's not enough : when my AJAX requests trigger a Completed 500 Internal Server Error in 1985ms... well nothing really happens for the user.
I would like, for example, to show an alert message on my frontend.
I already have some code that can display some flash messages I store in AJAX responses, which I'd like to reuse for this (for example, by showing oops, your AJAX request triggered a #{err_code} #{err_description}. Please tell the devs :D
I have the following code which successfully shows bootstrap flashes after AJAX requests (the content/type of the flashes being defined in my controllers)
application_controller.rb
after_filter :ajax_flash
def ajax_flash
return unless request.xhr?
if type = priority_flash
response.headers['X-Message'] = flash[type]
response.headers['X-Message-Type'] = type.to_s
flash.discard # don't want the flash to appear when you reload page
end
end
Related javascript
$(document).ajaxComplete(function(e, request, opts) {
fireFlash(request.getResponseHeader('X-Message'), request.getResponseHeader('X-Message-Type'));
# Where fireFlash is a function that displays a bootstrap alert
});
But now, If there is an error, my controller action will just return.
Is it possible to detect this, and for example to fill a flash
flash[:fatal] = "oops, your AJAX request triggered a #{err_code} #{err_description}. Please tell the devs :D"
That would the be displayed on my frontend ? (note that I don't know where to find err_code and err_description)
You don't need to use Ajax to create flash messages.
Since flash messages are non-persistent and removed from the session when they are rendered you can just create new flash messages by modifying the document.
This is a minimal flash implementation:
$(document).on('flash', function(e, key, message){
var $flash = $('<li class="alert flash js-created"></li>')
.addClass("alert-" + key)
.html(message);
$(this).find('.flashes').append($flash);
});
Turbolinks will clean out the flashes for use since it replaces the entire <body>.
You can add a global ajax failure handler by using ajaxError instead of ajaxStop.
var $doc = $(document);
$doc.ajaxError(function(event, jqxhr, settings, thrownError){
$doc.trigger('flash', ['error', jqxhr.status + thrownError]);
});
Use it this way.
$loader.fadeIn();
jqXHR = $.ajax({
url: "",
type: "POST",
data: {},
dataType: "json"
});
jqXHR.done(function( response ) {
});
jqXHR.fail(function( jqXHR, textStatus ) {
console.log( "Request failed: " + textStatus );
});
jqXHR.always(function( jqXHR, textStatus ) {
$loader.fadeOut();
});
What is the correct way to extend jQuery's getScript method to enable cache only inside that function for the ajax request?
I need to use this in my application to avoid unnecessary request for various scripts. Also is it correct to override this method or should I name the new function differently.
What I came up with is
jQuery.getCachedScript = function( url, callback, options ) {
// Allow user to set any option except for dataType, cache, and url
options = $.extend( options || {}, {
dataType: "script",
cache: true,
url: url,
success: callback
});
// Use $.ajax() since it is more flexible than $.getScript
// Return the jqXHR object so we can chain callbacks
return jQuery.ajax( options );
};
using the example form https://api.jquery.com/jQuery.getScript/. Is it correct, and can I name this getScript to override the jQuery method.
Would it be preferred to instead of using cache: true (or additionally) to use a global array that stores the .js files URLs and check against it for every call. I wrapped all the javascript code in this files in a function that I can call with different arguments using the getScript callback.
I've a jsp page with a form and some jquery code. Jquery code works perfectly, but if I return that page in a popup window by using an ajax call, the jquery code doesn't work any more.
I tried also to use delegation, that is:
$('select[name=myElementName]').on("change", function() {
// some code
});
or
$(document).on("change", 'select[name=myElementName]', function() {
// some code
});
instead of
$('select[name=myElementName]').change(function() {
// some code
});
Ajax call:
var preview = function () {
$.ajax({
type: "POST",
url: myAction.do,
data: "id=" + myid,
success: function (response) {
// some code
var x=window.open('', '_blank', 'titlebar=no,scrollbars=yes,menubar=no,height='+height+',width='+width+',resizable=yes,toolbar=no,location=no,location=0,status=no,left='+left+',top='+top+'');
x.document.open();
x.focus();
x.document.write(response);
return false;
},
error: function () {
return false;
},
});
};
EDIT
On Firefox 26.0 and Chrome 32.0.x, I resolved by using
x.document.close();
after
x.document.write(replace);
Instead, on IE, all the .js included scripts are ignored (for example the jquery-ui-1.9.1.js).
EDIT 2
I resolved with
<body onload="myload()">
and in my jsp I've myload() definition in which I call the scripts.
It is because you are creating new DOM structure but it doesn't have the event handlers attached. The easiest way is to run the event handler in the ajax callback:
$.ajax({
...
success: function (response) {
// some code
var x=window.open('', '_blank', 'titlebar=no,scrollbars=yes,menubar=no,height='+height+',width='+width+',resizable=yes,toolbar=no,location=no,location=0,status=no,left='+left+',top='+top+'');
x.document.open();
x.focus();
x.document.write(response);
// now, place the event handler here
$('select[name=myElementName]', x.document.body).change(function() {
// some code
});
}
});
Don't use document.write it completely overwrites whatever is on the page at the time of writing and leads to race conditions (e.g. the external scripts might have already been loaded, but they also might not, leading to unknown order of the write and script loads). Also, I believe documnt.write is putting serialized text into the document, not DOM objects so it may not trigger events.
Instead, you can open the new window and then manipulate the DOM objects there directly (assuming it's on the same server as your main page):
//Open a new window for the success info
var newWindow = window.open(newUrl);
//Now grab some element
var someItem = newWindow.document.getElementById( "someId");
//Manipulate dom either by "someItem.innerHTML" or "someItem.appendChild(...)"
If you are calling an AJAX server routine and putting the entire response w/o processing it on the client in to a new window, why not opening the window directly with the URL of that AJAX routine and skipping all stuff:
....
var x=window.open(myAction.do + "?id=" + myid,
'_blank',
'titlebar=no,scrollbars=yes,menubar=no,height='+height+',width='+width+',resizable=yes,toolbar=no,location=no,location=0,status=no,left='+left+',top='+top+'');
....
The only diff here is, that the request is a GET and not a POST request, but the data is just one id, which is acceptable, probably?
I had a similar problem in on of my projects. I solved it by writing a success method after the ajax call.
{
$.ajax({
type: "POST",
url: "/abc/",
data:{<data>},
async:false,
dataType:'json',
success: function(response)
{
success=1;
Id=response;
return;
}
});
if (success)
{
#your code here
var a='/xyz/?Id='+Id
window.open(a,'_blank');
window.location.href='/registration/'
}
return false;}
instead of using document.write, try fetching your success data(records arrived in success function) in a hidden DIV and clone it into your popup that should work
I am calling a JS using jquery getScript().
Sometimes i could see that the files are already loaded (cached resource).
So,On refreshing the cached page is not removed and also the same file is loaded again.
Because of the multiple includes of the same file i am getting errors.
How to avoid that ?
$.getScript("http://localhost:8888//../../demo.js", function()
{
console.log('Script is loaded.');
});
By default, $.getScript sets the cache setting to false. Try setting it to true to see if this solves your problem:
$.ajaxSetup({
cache: true
});
Add the above before your call like:
$.ajaxSetup({
cache: true
});
$.getScript("http://localhost:8888//../../demo.js", function() { console.log('Script is loaded.'); });
directly from jquery docs:
Caching Responses
By default, $.getScript() sets the cache setting to false. This
appends a timestamped query parameter to the request URL to ensure
that the browser downloads the script each time it is requested. You
can override this feature by setting the cache property globally using
$.ajaxSetup():
$.ajaxSetup({ cache: true }); Alternatively, you could define
a new method that uses the more flexible $.ajax() method.
Examples: Example: Define a $.cachedScript() method that allows
fetching a cached script:
jQuery.cachedScript =
function( url, options ) {
// Allow user to set any option except for dataType, cache, and url options = $.extend( options || {}, {
dataType: "script",
cache: true,
url: url });
// Use $.ajax() since it is more flexible than $.getScript // Return the jqXHR object so we can chain callbacks return
jQuery.ajax( options ); }; // Usage $.cachedScript( "ajax/test.js"
).done(function( script, textStatus ) { console.log( textStatus );
});
I believe if it is cached the browser will not go make a new request for it, it will know to load the cached version, so you are good just firing off your $.getScript as you have it.
It may appear in the network tab of chrome developer tools again, but the time will be 0 and the Size (Content) value will say '(from cache)' This would be a good way to test what is actually going on.
Assuming your demo.js file contains at least one function or variable, you could check for presence before loading again:
if (typeof(your_variable) === "undefined") {
$.getScript("http://localhost:8888//../../demo.js", function() { console.log('Script is loaded.'); });
}
(where your_variable is the name of a function or variable inside demo.js)
Cashingvis good feature I solved multiple time loading js when I I load through jquery before. My issue was when I call a file loading by jquery I have a jquery file in that loading file now it loads only once so then events i now envoje only once. Thanks a lot have a nice day.
I have a very large javascript file I would like to load only if the user clicks on a certain button. I am using jQuery as my framework. Is there a built-in method or plugin that will help me do this?
Some more detail:
I have a "Add Comment" button that should load the TinyMCE javascript file (I've boiled all the TinyMCE stuff down to a single JS file), then call tinyMCE.init(...).
I don't want to load this at the initial page load because not everyone will click "Add Comment".
I understand I can just do:
$("#addComment").click(function(e) { document.write("<script...") });
but is there a better/encapsulated way?
Yes, use getScript instead of document.write - it will even allow for a callback once the file loads.
You might want to check if TinyMCE is defined, though, before including it (for subsequent calls to 'Add Comment') so the code might look something like this:
$('#add_comment').click(function() {
if(typeof TinyMCE == "undefined") {
$.getScript('tinymce.js', function() {
TinyMCE.init();
});
}
});
Assuming you only have to call init on it once, that is. If not, you can figure it out from here :)
I realize I am a little late here, (5 years or so), but I think there is a better answer than the accepted one as follows:
$("#addComment").click(function() {
if(typeof TinyMCE === "undefined") {
$.ajax({
url: "tinymce.js",
dataType: "script",
cache: true,
success: function() {
TinyMCE.init();
}
});
}
});
The getScript() function actually prevents browser caching. If you run a trace you will see the script is loaded with a URL that includes a timestamp parameter:
http://www.yoursite.com/js/tinymce.js?_=1399055841840
If a user clicks the #addComment link multiple times, tinymce.js will be re-loaded from a differently timestampped URL. This defeats the purpose of browser caching.
===
Alternatively, in the getScript() documentation there is a some sample code that demonstrates how to enable caching by creating a custom cachedScript() function as follows:
jQuery.cachedScript = function( url, options ) {
// Allow user to set any option except for dataType, cache, and url
options = $.extend( options || {}, {
dataType: "script",
cache: true,
url: url
});
// Use $.ajax() since it is more flexible than $.getScript
// Return the jqXHR object so we can chain callbacks
return jQuery.ajax( options );
};
// Usage
$.cachedScript( "ajax/test.js" ).done(function( script, textStatus ) {
console.log( textStatus );
});
===
Or, if you want to disable caching globally, you can do so using ajaxSetup() as follows:
$.ajaxSetup({
cache: true
});