I have a lot of buttons with the class search_camera_btn.
When clicking on the button then it submits a form. This step works. On the other side, it should also trigger a button click event.
I wrote the event listener in a coffeescript file which sends Ajax requests, but it only works on the first click.
I put the code in this gist.
The Javascript works when the button is clicked the first time, but fails on subsequent clicks.
Actually I put a alert message in the begin of click event handler,
But it only alerts at the first time.
And there is no error message in my Firbug console. (I thought it is just didn't fire the click event handler.)
$(".search_camera_btn").click ->
alert "test"
There are many buttons,no matter which button I click.
It always works at the first time click.
Here is my more detail source code. download
Any ideas?
I narrow down the buggy code.That is the "Ready to enter start" message only called at the first time.
But there is no error showed on the Firebug Javascript console and Rails console.
Should I enable some settings in the development mode ?
IW2 = get_camera_list: ->
console.log("Start")
ajax_req = $.ajax
url: "update_camera_list/"
type: "GET"
success: (resp) ->
# console.log resp
res = setTimeout (->
ajax_req
), 500
console.log("End")
return
jQuery ->
$(".search_camera_btn").click ->
console.log("Ready to enter start")
IW2.get_camera_list()
Compiled CoffeeScript:
var IW2;
IW2 = {
get_camera_list: function() {
var ajax_req, res;
console.log("Start");
ajax_req = $.ajax({
url: "update_camera_list/",
type: "GET",
success: function(resp) {}
});
res = setTimeout((function() {
return ajax_req;
}), 500);
console.log("End");
}
};
jQuery(function() {
return $(".search_camera_btn").click(function() {
console.log("Ready to enter start");
return IW2.get_camera_list();
});
});
The reason that the handler is only being fired once is because
the ".click" handler only applies to elements that are currently attached
to the DOM. If you replace the HTML after making the AJAX call, the event handlers will be lost.
You should use an event delegate instead. Try this:
jQuery(function() {
return $("body").on("click", ".search_camera_btn", function() {
alert("Ready to enter start");
return IW2.get_camera_list();
});
});
This statement basically says, if there are any elements in the DOM now, or in the future, that have a class of "search_camera_btn", and are contained within the "body" element, then fire the click handler.
I hope this helps!
Change this:
jQuery ->
$(".search_camera_btn").click ->
console.log("Ready to enter start")
IW2.get_camera_list()
For:
jQuery ->
$(".search_camera_btn").click ->
console.log("Ready to enter start")
IW2.get_camera_list()
return
And let me know if it helps ;)
I would make sure nothing else in your application's javascript is failing (like a simple syntax error can cause this sort of thing). Also have you tried this in different browsers? I've had a similar problem where my Ajax would work fine in Chrome but would only post once in Firefox because of some add-ons/extensions (and then I disabled them and they worked).
Also, I'm not sure if I read your gist correctly but it looks like you're specifying .click in both the jQuery of the application.js and in the btn.js.coffee, and I'm pretty sure that section in application.js should just be watching for the element's function/result and not specifying the click again.
If nothing else works, also check out ajax's .done completion call about halfway down the page here: http://api.jquery.com/jQuery.ajax/ . Then show the picture list as a function after .done so you know your ajax post is always completing before moving on to the next thing. (Ajax problems like this often tend to be server side when a call doesn't complete or there's a loop somewhere)
Related
I would like to "chain" two .click() calls but cannot get it to work.
The code does work when I debug the JS code in my browser (so with a delay it seems to work)
I somehow need the first .click() to load the page (that's what the first event does) and only if that is done, I want the second .click() to execute.
My Code:
$.post("settings?type=mail&nr=" + nr, function(data){
if(data != ""){
alert(unescape(data));
// First click event -> realoads the page
$("#change_settings").click();
// Second click event -> navigates to a tab
// inside the page loaded by the first click event
$("#tab_mail_header" + nr + "").click();
}
});
EDIT: More Code
function load_change_settings_view(e){
e.preventDefault();
$("#content").empty();
// load settings.jsp in a div (test) inside the mainpage
$("#content").load("settings.jsp #test", function(){
// In here are a couple of .click() & .submit() functions but nothing else
});
});
$("#change_settings").click(function(e){
load_change_settings_view(e);
});
EDIT: I currently have this code:
$("#change_settings").click();
window.setTimeout(function () {
$("#tab_mail_header" + nr + "").click();
}, 1000);
I dont really like it though, as it is a timed delay and it may be the case (on a slow client) that that 1 second timeout will not be enough. I don't want to set the timeout too high as this slows down the workflow for users with a faster client...
I looked though a couple of post like these:
JQuery .done on a click event
Wait for a user event
How to wait till click inside function?
Wait for click event to complete
Does anyone have an idea on how to get this to work?
after a few more attempts I ended up with the following solution:
Code Snippet#1
$.post("settings?type=mail&nr=" + nr, function(data){
if(data != ""){
alert(unescape(data));
// create event object
var evt = document.createEvent('MouseEvents');
evt.initEvent('click', true, false);
// call method manually (not called by actual button click like its supposed to be)
// - pass event object
// - additional parameter to specify the tab the user is viewing
load_change_settings_view(evt, "tab_mail_header" + nr);
}
});
Code Snippet#2
function load_change_settings_view(e, p_tab){
e.preventDefault();
$("#content").empty();
// load settings.jsp in a div (test) inside the mainpage
$("#content").load("settings.jsp #test", function(){
// Go to previous tab (if one was selected)
var prev_tab = p_tab;
if(typeof prev_tab != 'undefined'){
$("#" + prev_tab).click();
}
// In here are a couple of .click() & .submit() functions but nothing else
});
});
feel free to comment if you have a better idea on how to solve this problem or if you have any other suggestions
I have been trying to bind beforeunload event by calling the following script so that I can go to the specified URL through AJAX. The problem is that the AJAX is not working the first time as the URL does not get called when the first time I do the page refresh. The second time ajax works. This problem gets fixed when I set async to false but then the alert popup inside success doesn't show up. I need alert box to also show up in success block.
<script type="text/javascript">
$( document ).ready(function() {
// this method will be invoked when user leaves the page, via F5/refresh, Back button, Window close
$(window).bind('beforeunload', function(event){
// invoke the servlet, to logout the user
$.ajax({
cache: false,
type: "GET",
url: "LogoutController" ,
success: function (data) {
alert("You have been logged out");
}
});
});
});
</script>
beforeunload will wait for the event handler to finish its execution before closing the page. Since an ajax call is asynchronous beforeunload is not going to wait for it to finish (your server however should still get the request). This is the expected behaviour and I don't think they is a way around it.
This behaviour can be reproduces using the following code:
window.onbeforeunload = function () {
console.log("bye");
setTimeout(function () {
console.log("bye1");
}, 200);
console.log("bye2")
};
//bye
//bye2
Also, you should note that, according to MDN the specs states that alert() can be ignored:
Since 25 May 2011, the HTML5 specification states that calls to
window.alert(), window.confirm(), and window.prompt() methods may be
ignored during this event.
When this happens on chrome (only browser I checked) you will get the following message in the console:
Blocked alert('test') during beforeunload.
I didn't find any result for my issue.
I have Prestashop 1.5.6, I need to execute my jQuery function after blockcart adds the product.
BlockCart and my code starts with the same event, As I far know the unbind disables the other handlers event, how can I restart the unbind in the footer code?.
So my code don't work because Blockcart starts on first
//---------BlockCart Code---------
overrideButtonsInThePage : function(){
//for every 'add' buttons...
$('.ajax_add_to_cart_button').unbind('click').click(function(){
var idProduct = $(this).attr('rel').replace('nofollow', '').replace('ajax_id_product_', '');
if ($(this).attr('disabled') != 'disabled')
ajaxCart.add(idProduct, null, false, this);
return false;
});
//for product page 'add' button...
$('#add_to_cart input').unbind('click').click(function(){
ajaxCart.add( $('#product_page_product_id').val(), $('#idCombination').val(), true, null, $('#quantity_wanted').val(), null);
return false;
});
//-------- Footer code--------
$('.button.ajax_add_to_cart_button.exclusive, .button.ajax_add_to_cart_button.btn.btn-default').on('click',function(){
var id_product = $(this).attr('data-id-product');
myfunction(id_product);
});
It's possible to detect when blockcart ends the script without a editing the blockCart module with a callback function?
Thanks!
Finally the result was an override the blockCart function.
I did a copy of the overrideButtonsInThePage into my js to override it and I add my code inside the events.
Works perfectly
Depending on how blockcart functions, you can try:
//-------- Footer code--------
$('.button.ajax_add_to_cart_button.exclusive, .button.ajax_add_to_cart_button.btn.btn-default').click(function(){
var id_product = $(this).attr('data-id-product');
setTimeout(function() { myfunction(id_product); }, 0);
});
This will cause your function to execute after the existing stack finishes executing. You can learn more about what this does in Why is setTimeout(fn, 0) sometimes useful?. This will only be helpful if the blockcart handler is synchronous.
You can actually monitor ajax calls by using $.ajaxComplete(); and .ajaxStart()
http://api.jquery.com/ajaxcomplete/
What you can do is compare url the ajax is being sent to. Add to cart URL has GET parameters pre-encoded into it, so distinguishing it from other ajax calls isnt hard. Then on $.ajaxComplete(); you should compare the response result (determin that it was a succes) and then execute your function. This way you wont modify anything, you will just be observing the ajax actions
We're creating a click tracking app, that builds heatmaps. I'm writing a script which users are suppose to insert into their pages for tracking to work.
It works fine on elements, which doesn't require a redirect or form submit. For example, if I click on h1 or p or whatever, it works perfectly correct. But, if I click on a a, request to our server never happens before the normal redirect.
In the last couple of days I tried a lot of ways to do that. First of, I tried a normal AJAX call, since it was a cross-domain request I had to use JSONP, but again, that AJAX call did not have time to execute before the redirect. Adding async: false would have solved the problem, but it doesn't work with JSONP requests. So I decided to add a flag variable which indicates that it is safe to move on with redirect and used an empty while loop to wait until it becomes try in the ajax callback. But the while loop was blocking the execution flow, so callback never got a chance to set that variable to true. Here is some simplified code:
$(document).on('click', function (e) {
//part of the code is omitted
$.ajax({
url: baseUrl,
data: data,
type: "get",
dataType: "jsonp",
crossDomain: true,
complete: function (xhr, status,) {
itsSafeToMoveOn = true;
}
});
while(!itsSafeToMoveOn){}
return true;
});
The next thing I tried is to use unload page event to wait until total ajax calls in progress would become zero (I had a counter implemented) and then to move on with redirect. It worked in Firefox and IE, but in WebKit there was this error:
Error: Too much time spent in unload handler
After that I realized that I don't care about the server response and using img.src for the request would be an ideal fit for this case. So at this point code looks like this:
$(document).click(function (e) {
//part of the code is ommited
(new Image).src = baseUrl + '?' + data;
if (tag === "a" || clickedElement.parents().has("a")) {
sleep(100);
}
return true;
});
That way I increased the overall script performance slightly, but problem with links remains unchanged. The sleep function appears to be also blocking the execution flow and request never happens.
The only idea left is to return false from the event handler and than redirect manually to the clicked element's href or to call submit() on the form, but it will complicate things to much and believe me it's already a huge pain in the ass to debug this script in different browsers.
Does anyone have any other ideas?
var globalStopper = true;
$(document).on('click', function (e) {
if (globalStopper === false)
return true; //proceed with click if stopper is NOT set
else {
globalStopper = false; //release the breaks
$.ajax({
//blahblah
complete: function (xhr, status,) {
$(elem).click(); //when ajax request done - "rerun" the click
}
});
return false; //DO NOT let browser process the click
}
});
Also, instead of adding image, try adding script. And then add the script to the HEAD section. This way the browser will "wait" until it's loaded.
$(document).on('click', function (e) {
var scriptTag = document.createElement("script");
scriptTag.setAttribute("type", "text/javascript");
scriptTag.setAttribute("src", url);
document.getElementsByTagName("head")[0].appendChild(scriptTag);
return true;
}
I would take a look at the navigator sendBeacon API mentioned in this stack overflow answer or directly linked to here.
From the description on the site
navigator.sendBeacon(url, data) - This method addresses the needs of analytics and diagnostics code that typically attempts to send data to a web server prior to the unloading of the document.
You can save information to ajax request in cookies or localStorage and make any worker that will send information. Saving to cookies or localStorage is faster then ajax-request. You can do next:
$(document).click(function (e) {
var queue = localStorage.getItem('requestQueue');
queue.push(data);
localStorage.setItem('requestQueue',queue);
});
$(function(){
setInterval(function(){
var queue = localStorage.getItem('requestQueue');
while (queue.length > 0) {
var data = queue.pop();
$.ajax({
...
success: function(){
localStorage.setItem('requestQueue', queue);
}
});
}
},intervalToSendData);
});
So, when user click on link or send a form, data will be saved to storage and after user go to next page, this worker starts and send data to your server.
The JavaScript is basically executed in single thread. It is not possible to have your callback function executed and at the same time have an infinite loop waiting for a flag variable from it. The infinite loop will occupy the single execution thread and the callback will never be called.
Best approach is to cancel the default handler of your event and bubbling for it (basically return false if you are really building your tracking code with jQuery), and do the necessary actions (redirect page to the necessary address if a link was clicked or trigger other default actions), but this would take a lot of careful work to recreate all the possible combinations of actiona and callbacks.
Another approach is to:
1) Look for something specific to your code in the event data
2) If it is not present - make an AJAX call and in its callback re-trigger the same even on the same element, but this time with your specific bit added to the even data; after the AJAX call return false
3) If your specific bits are present in the data - simply do nothing, allowing the default event processing to take place.
The either approach may bite, however.
So if I understand right, you want your ajax logs completed before the page unloads and follows a link href. This sounds like a perfect case where you could consider using Deferreds in jQuery.
When your user clicks on anything that's supposed to take him away from the page, just check your promise status. If it's not resolved, you could throw a modal window over the page, and ask the user to wait til the progress is complete. Then, add a new pipe to your deferred, telling it to change the location href once everything is complete.
Let me know if this is the scenario. If it is, I'll explain in more detail. No use continuing if I didn't understand your requirement properly
I always used something like this:
$("a.button").click(function() {
data = ...;
url = ...;
$.post(url, data, function() {
$(this).toggleClass('active');
});
});
The problem is when an user has a slow connection and clicks on that button, it doesn't seems to do anything, because the button will change the own status (adding the active class) once the request is complete. Of course I can "fix" this behavior by adding a spinner while the request is loading.
Now check out this one:
$("a.button").click(function() {
$(this).toggleClass('active');
data = ...;
url = ...;
$.post(url, data, function() {
// if request is successful do nothing
// else, if there's an error: $(this).toggleClass('active)
});
});
In other words, I change the button status instantly when the button is pressed and after this, I check for success/error. Is this a good way? What you think about? Are there other ways?
This is more of a UI question than code. Personally I prefer to show the spinner in cases where it could be confusing if there is no response. Since I don't know what class you're toggling and what effect it has on the element, I wouldn't know if toggling before success would be confusing at all.
One way or another, everyone alive knows the loading spinner. It's probably safe to go with that.
You've got the general idea there. You can implement it in other ways, for instance by setting global AJAX ajaxStart and ajaxSuccess functions:
$("a.button").click(function() {
data = ...;
url = ...;
$.post(url, data, function() {
// if request is successful do nothing
});
}).ajaxStart(function () {
$(this).toggleClass('active');
}).ajaxComplete(function () {
$(this).toggleClass('active');
}).ajaxError(function () {
//never forget to add error handling, you can show the user a message or maybe try the AJAX request again
});
These methods register handlers to be called when certain events, such
as initialization or completion, take place for any AJAX request on
the page. The global events are fired on each AJAX request if the
global property in jQuery.ajaxSetup() is true, which it is by default.
Note: Global events are never fired for cross-domain script or JSONP
requests, regardless of the value of global.
Source: http://api.jquery.com/category/ajax/global-ajax-event-handlers/
Use $.ajax success:
From jquery docs:
$.ajax({
url: "test.html",
success: function(){
$(this).addClass("done");
}
});
You could do something like:
$("a.button").click(function() {
var old_text = $(this).text();
var button = $(this);
$(this).text('Processing...');
$(this).attr('disabled', 'disabled'); // disable to button to make sure it cannot be clicked
data = ...;
url = ...;
$.post(url, data, function() {
// after request has finished, re-enable the link
$(button).removeAttr('disabled');
$(button).text(old_text);
});
});
Next thing, you should do something similar for catching errors (re-enable the button).
It always depends the way you've built your site, but in my opinion the active state should only be triggered at the instant you click.
So that should be: onmousedown you add your class and onmouseup you remove it.
The Ajax call could trigger a different function maybe showing a loading dialog/spinner.
There are several ways of building it: individually on each element as you did, or through a general styling function. Same for Ajax with the ajaxStart ajaxComplete functions as Jasper said.
Personally I'm using Ajax intensively, always changing the DOM dynamically, so I use livequery to setup style changing with events automatically when elements with given class(es) appear in the DOM, and I use ajaxStart and ajaxComplete for displaying a loading dialog.