Multiple change events - javascript

I have 3 <select> menus, each with change events working on them. Break down of the code looks like this:
$(document).ready(function(){
// some code
$("#selectOne").change(function() {
// some code inc ajax request
$("#selectTwo").change(function() {
// some code inc a different ajax request
});
$("#selectThree").change(function() {
// some code inc a yet another ajax request
});
});
});
The problem with the above is that, while selectOne works fine, and selectTwo seems to work fine also, if I change selectThree, the code for both selectTwo AND selectThree fires at the same time. Depending on the sequence of selection of any of the 3 selects, the response of selectThree.change can be to display and hide each of the previous responses, before settling on displaying an incorrect response.
What I'd like to do is this:
$(document).ready(function(){
// some code
$("#selectOne").change(function() {
// some code inc ajax request
});
$("#selectTwo").change(function() {
// some code inc a different ajax request
});
$("#selectThree").change(function() {
// some code inc a yet another ajax request
});
});
In this scenario, selectOne works fine, but selectTwo and selectThree don't respond to change.
Is there a way of correcting any of this, so that as each element is changed, only the correct change event is fired?

It seems that when your document is loaded there is no select elements with id "selectTwo" or "selectThree". Similarly in the first case, there is no "selectThree" when the eventhandler of "selectOne" executes. That's why the corresponding event handlers does not execute. There is two way you can handle this proplem.
1 - Assing handlers when the elements are created.
2 - Use .on() instead of .change():
$('body').on("change", "#selectOne", function () {});
$('body').on("change", "#selectTwo", function () {});
$('body').on("change", "#selectThree", function () {});

Related

Execute a second .click() event after the first is done

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

Calling jQuery function too many times in Rails app

In my Rails app, I am committing a single UI action (changing a select value), but the corresponding function is being called twice. The function is below.
// doc.js
$(document).on("change", "select.class", (function(){
if ($("select.otherSelect").find(":selected").is( ":disabled" ) == false) {
$.ajax({
//Ajax call
});
console.log("Selector was changed")
}
}));
I know the function is being called multiple times because the text "Selector was changed" appears more than once in the JS console when I change the select element only once. Before writing this, changing the select element six times in succession caused the function to be called once, then twice, then four times, then eight times, then sixteen times, then thirty-two times.
Why is this?
I fixed the issue. To start, I had an entire page whose elements were bound by functions in $(document).ready(function() {. This became problematic because I was using the select to asynchronously replace many elements in the page with new elements that needed to be bound by those functions (but weren't, since the binding only happens once).
To get around this, I copied all the content in $(document).ready(function() { into a function docReady() and then called docReady() both inside $(document).ready(function() { and whenever I asynchronously reloaded the content of the page. This strategy caused my error; now I was binding every element of the page, including the select itself!
Now, I've called the binding functions for the select only once in $(document).ready(function() { and I call the binding functions for the asynchronously generated elements once every time select changes its value.
You can try like this:
$(document).on("change", "select.class", (function(){
if($(this).data('clicked')) {
return;
}
if ($("select.otherSelect").find(":selected").is( ":disabled" ) == false) {
$.ajax({
//Ajax call
});
console.log("Selector was changed")
}
$(this).data('clicked', true);
}); //removed here was extra )

Combining multiple keyups into single event to reduce number of ajax requests

I have a keyup event on a search box that produces suggestions by fetching data from db. It is working fine. But the problem arises when i press keyboard buttons quickly and for every keyup event it gets me the result which produce duplicates.
I tried using
$("#search").on("keyup", function() {
setTimeout(getLocationFromDb, 1000);
});
But that still is producing same result (sending request to server for every keyup event).
I was looking for solutions but couldn't find one. Thanks for help.
EDIT
I am clearing the results beforeSend and appending the results in success.
Your approach isn't that bad - just make sure to clear all old timouts before setting a new one. You of course have to store the timeout somwhere - you could, for example, create a variable inside a closure.
This code only calls your callback when there was no new input for 1 second:
$("#search").on("keyup", (function () {
var timeout;
return function (e) {
window.clearTimeout(timeout);
timeout = window.setTimeout(getLocationFromDb, 1000);
}
})());
Fiddle: http://jsfiddle.net/uq4x9/
You can use boolean variable to detect if ajax call is finished already before send new
var isAjaxSent = false;
$("#search").on("keyup", function() {
isAjaxSent = true;
setTimeout(getLocationFromDb, 1000);
});
function getLocationFromDb( .... ) {
if (!isAjaxSent) {
//body of function
// in ajax success function set isAjaxSent to false
}
}

jQuery prevent multiple submisssions before response

I would do this in JS fiddle, but I can't get the POST echoer to work, so I'll make an example here. Let's pretend that someApi returns "bar"
JS / jQuery
$(function() {
$('button').click(function(event) {
getSomeData();
});
function getSomeData() {
$("div").text("Foo = ");
$.get("someApi", function(i) {
$("div").append(i);
});
};
});
HTML
<div></div>
<button>Click Me</button>
There maybe some typos here, but please ignore them as I've written an example on-the-fly. What happens is when <button> is clicked once, all works well. The AJAX function is called and the <div> is appended when the response comes. If I wait for the response and click again, the <div> is overwritten with Foo = and then appended. The issue comes when the user becomes inpatient and clicks <button> multiple times, spawning multiple AJAX requests. This ends up with "bar" being appended multiple times. Is there a feature within JS / jQuery to avoid sending multiple requests to the same URL? I DON'T mean I want a async = false scenario; I know this would work, but would also slow the application down. I also know I could add an if loop that checks if bar has already been appended. What I'm asking for is a proper JS / jQuery .blockMultipleRequest(); kind of thing.
I don't think that there's a plugin for that. You could use .one() in this way:
function bindButton() {
$('button').one('click', function(event) {
getSomeData();
});
}
function getSomeData()
$("div").text("Foo = ");
$.get("someApi", function(i) {
$("div").append(i);
bindButton();
});
}
$(function() {
bindButton();
});
In function bindButton() you define your event handler with one(). Once button has been clicked event is removed until response of AJAX call, then function bindButton() is called again and event handler gets bound again.
You could use the global AJAX event handlers that jQuery provides and then do stuff depending on the request.
.ajaxSend() when any request starts (the event, jqXHR, and settings properties are sent to the handler, so you can then do URL-specific actions by evaluating settings.url
.ajaxComplete() when any request completes.
You could then use an object that keeps track of AJAX calls per URL, which can consult before sending off another request (e.g. only if it not currently in an active/pending state).

$.post() callback method suggestion

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.

Categories