I'm trying to add categories to a drop down list using jQuery Ajax. When an option is selected I would like to load the subcategories.
The problem that I'm facing is that the addition of options to the drop down list by the Ajax function seems to trigger the change event as well. How can I avoid this or rewrite my code better in order to avoid this behavior?
Here's my code:
categoryHelper.loadLevel1 = function () {
// The control that needs to be filled with categories
var $control = $("#select1");
// This function runs $.getJSON() and fills the drop down list
// with option elements
fillDropDown(url, null, $control);
// When I select a value I want to run the function loadLevel2 which
// takes the currently selected value from level 1 as parameter
$control.change(categoryHelper.loadLevel2($control.val()));
};
Code for the fillDropDown:
function fillDropDown(url, parameters, dropdown) {
/// all data has been loaded</param>
$.getJSON(url, parameters, function (data) {
$(dropdown).empty();
$(dropdown).append(createOption("", ""));
$(data).each(function () {
$(dropdown).append(createOption(this.value, this.text));
});
});
}
All help is appreciated!
This line:
$control.change(categoryHelper.loadLevel2($control.val()));
will pass the result of calling categoryHelper.loadLevel2($control.val()) to .change(); if that isn't a function, then you're not going to be adding an event handler to the element but instead triggering any event handlers that are already bound. Change it to:
$control.change(function() {categoryHelper.loadLevel2($control.val())});
so that you're actually passing a function to .change(), and it should work.
Related
I have a few rows of products a customer can choose to select. These rows have the class "product-line". On the click of a row, I want to show the user that it is (un)checked by toggling the "product-checked" class, and then send a POST message with the IDs all checked rows. To do this, I wrote this code:
<script>
$(".product-line").click(onProductLineClick);
function onProductLineClick(e) {
$(this).toggleClass("product-checked");
updatePrice();
}
function updatePrice() {
var ids = $(".product-checked")
.map(function() {
return $(this).data("id");
});
$.post("/Controller/Action", { 'productIds[]': ids });
alert("Hi!");
}
</script>
This piece of code causes the onProductLineClick to trigger indefinitely. The alert("Hi!") is never triggered. The POST is never sent.
When I remove the $.post, the click event is triggered only once and everything works fine. The toggleClass does its job, showing that the row was selected. When I do add the $.post, it all goes nuts and the moment the $.post is reached, it just fires the click event again, causing an infinite recursion.
The parameter 'e' in onProductLineClicked is never defined other than the very first time. Using e.preventDefault() causes an error on the second time it passes through there.
I'm stumped. I can't understand how or why a POST would cause the click to fire again, and remove the POST stops that from happening.
The error is with your map function. There are two map functions in jQuery.
$('element').map() versus jQuery.map()
As stated in the documentation, the first produces a new jQuery object containing the return values while the second translates all items in an array or object to new array of items.
Serialising the newly created object and passing it in an ajax request doesn't work because it has recursive references within it. [Thanks #ADyson]
FIDDLE
$(".product-line").click(onProductLineClick);
function onProductLineClick(e) {
$(this).toggleClass("product-checked");
updatePrice();
}
function updatePrice() {
var ids = jQuery.map($(".product-checked"), function(element, index) {
return $(element).data("id");
});
console.log(ids);
$.post("/Controller/Action", {
'productIds': ids
});
alert("Hi!");
}
I am using select2 jquery plugin
I am using open event
var select2 = $('select').select2();
select2.on("select2:open", () => {
// I want to do some code here with $('.select2-results__option')
//$('.select2-results__option').size() is not equal to actual elements
});
This event triggers when we click the dropdown. but I am not getting the dropdown elements in this event.
I have 2000 dropdown elements. but in open event I am not getting that. Is there any event to detect the dropdown list is filled or not.
I had a similar problem when I tried to remove an item after open.
You could use a async function to check if the list is complete:
select2.on("open", function (e) {
asyncCheck();
});
async asyncCheck() {
// Waiting for 0 milliseconds was enough for me, maybe you need to increase the value
await new Promise(resolve => window.setTimeout(resolve, 0));
// Here comes your check, if list is completly populated ...
}
But be aware, that this will not work with IE 11 as async and Promise are not supported in IE 11 (see Promises and async function from Can I Use
My workaround for IE 11 looks like this:
$(document).ready(checkContinuously());
function checkContinuously() {
// Do your check here
setTimeout(checkContinuouslyForNullOption, 100);
}
But is has the obvious drawback of always executing the check and theoretically could cause a stack overflow as it uses recursion.
Try to catch 'results:all' event of select2 itself (not DOM).
Here from Select2.prototype._registerEvents:
this.on('query', function (params) {
if (!self.isOpen()) {
self.trigger('open', {});
}
this.dataAdapter.query(params, function (data) {
self.trigger('results:all', {
data: data,
query: params
});
});
});
as we can see open is triggered before querying dataAdapter, that's because you don't have options in its handler I guest.
select2.on("results:all", function (args) { console.log(args.data);});
One way of finding if there are options or not is
$('#select').find("option").length which provides the number of options there are.
Fiddle Here
I've read up on declaring variables globally and then being able to modify them in functions, but things aren't working out for me.
Here is my code:
var selectee = "JK";
// get state selected
$('select.form-control.bfh-states').change(function () {
selectee = $('select option:selected').val();
// works correctly, but i need to access it outside the function
console.log(selectee);
});
// output JK, should change based on a select box on the form
console.log(selectee);
It is because the the change() handler will get executed only when the change event is fired from the select element. You are using the console.log() statement in a sequential executio which will get executed before the change handler is fired
//this is correct
var selectee = "JK";
//this registers a change handler for the select element
$('select.form-control.bfh-states').change(function () {
//but this will not execute now!!! it will get executed only when the select elements change event is fired
selectee = $(this).val();
console.log(selectee); // works correctly, but i need to access it outside the function
});
//this will get executed as soon as the change handler is registered not after the handler is executed
console.log(selectee);
If you want selectee to have the value selected in the select element then you can either do something like
var selectee = $('select.form-control.bfh-states').val() || "JK";
or manually fire the select change handler once the handler is attached on dom ready like
var selectee = "JK";
$('select.form-control.bfh-states').change(function () {
selectee = $(this).val();
console.log(selectee); // works correctly, but i need to access it outside the function
}).change();
The way to fix this problem is to execute the code that needs the value of selectee from within the change handler. You shouldn't be storing it in a global variable in the first place.
// get state selected
$('select.form-control.bfh-states').change(function () {
var selectee = $('select option:selected').val();
console.log(selectee); // works correctly, but i need to access it outside the function
// call your other code from here and pass it the current value of selectee
myOtherFunction(selectee);
});
To explain, the .change() callback function is ONLY executed when the value of the select actually changes. It will be called sometime LATER. So, to use the value of selectee sometime later, you need to execute the code that needs that value at the same time that the new value has been changed.
You code doesn't work procedural as you think. selectee will reflect the new value only after the change event of your select control is fired. The codes inside event handlers don't execute until they are called/triggered/fired. But those outside, like your console.log(selectee) will execute the first time the code is loaded (which in your case, the change event hasn't been called).
It is because change handler is a callbackļ¼ it will fired after the events happen, not executed the code order
An alternative method would be is to pass the selected value into a new function and hence access it within that function(not globally). Try this:
selectee = "JK";
// get state selected
$('select.form-control.bfh-states').change(function () {
selectee = $('select option:selected').val();
// works correctly, but i need to access it outside the function
mynewfunc(selectee);
});
function mynewfunc(){
alert(selectee);
}
Note: The variable selectee is not accessible outside new function mynewfunc once the change is triggered.
Demo
I am a rookie in JS, have a problem understanding JQUERY semantics.
I have a function written for checking the content of a cell.
Problem: the function just starts when the cell loses focus, if I click Submit, the error shows first, then it will run the function.
I want the function to run even when I am inside the cell. How to do it?
Initiated by this:
$(".user_id").blur(function(){ validateUserId($('.user_id')); });
The function:
function validateUserId(reference) {
if ( 5 == $(reference).val().length ) {
$.get('index.php?user=' + $(reference).val(), function(data) {
if ( "error" == data ) {
$(reference).parent().parent().addClass('error');
alert('error');
} else {
$(reference).parent().parent().removeClass('error');
$(reference).addClass('valid');
$(reference).parent().parent().addClass('success');
}
});
} else {
alert('short');
$(reference).parent().parent().addClass('error');
}
}
$(".user_id").on('keyup', function(){
validateUserId($(this));
});
i would use the keyup event.
So everytime somebody types a key in your input cell, the function will be executed.
$(".user_id").on("keyup", function(){ validateUserId($(this)); });
I changed the $(".user_id"), you take the value from ,to $(this). Since you want the value of the field you did the keyup event on. (And not an other field, if there would be 2 fields with the same .user_id class.)
Try to bind function on focus event
$("input[submit]").click(function(e){
e.preventDefault();
var valid = validateUserId($('.user_id')); // maybe the function could return a boolean value too instead of just adding classes to the HTML part
if (valid) {
$("#your_form_id").submit(); // only if the input is valid, submit it
}
});
sidenote on your problem: if you click the submit button it will first trigger the submit action which may give you an error and then execute the blur() block of code
Here is a working demo http://jsfiddle.net/pomeh/GwswM/. I've rewritten a little the code to:
cache DOM selections into variables,
use jQuery methods chaining,
improve if conditions with tripe equal signs,
separated AJAX calls and application logic,
cache identical AJAX calls,
use jQuery deferred to hide the asynchronous aspect of the verification (linked to AJAX)
Hope that'll help :)
I am using a jQuery method $.getJSON to update data in some cascading drop down lists, in particular, a default value if there is nothing returned for the drop down, e.g. "NONE".
I just want some clarification to how my logic should go.
var hasItems = false;
$.getJSON('ajax/test.json', function(data) {
hasItems = true;
//Remove all items
//Fill drop down with data from JSON
});
if (!hasItems)
{
//Remove all items
//Fill drop down with default value
}
But I don't think this is right. So do I enter into the function whether or not I receive data? I guess I really want to check the data object contains something - to set my boolean hasItems.
You should handle the check right inside the callback function, check the example here.
var hasItems = false;
$.getJSON('ajax/test.json', function(data) {
hasItems = true;
//Remove all items
//Fill drop down with data from JSON
if (!hasItems)
{
//Remove all items
//Fill drop down with default value
}
});
You want to do all checking of returned data inside the callback, otherwise that condition will be called before the callback has been called, resulting in it always being the initial value assigned.
You're dealing with asynchrony, so you need to think of the code you're writing as a timeline:
+ Some code
+ Fire getJSON call
|
| server working
|
+ getJSON call returns and function runs
The code inside the function happens later than the code outside it.
Generally:
// Setup any data you need before the call
$.getJSON(..., function(r) { //or $.ajax() etc
// Handle the response from the server
});
// Code here happens before the getJSON call returns - technically you could also
// put your setup code here, although it would be weird, and probably upset other
// coders.