I have 3 CascadingDropDowns and am trying to set their values from the clientside. I have a solution that works, but uses setTimeout which is not always reliable, so I am looking for a more stable solution.
The problem I'm having is that after setting the Parent CascadingDropDown, its child is not immediately populated -- it takes some milliseconds to load the data. Therefore in the javascript code if I assign the parent dropdown a value and then try to loop through the corresponding child values to find the value I want, the child dropdown will not be immediately populated since its data has not come back from the web service.
Here is the code using timeouts that is working for me -- but I want to get away from the timeouts.
for (i = 0; i < ddlPC1.length; i++) {
if (ddlPC1.options[i].value == test1) {
var ajaxcdd_ddlPC2 = $find(document.getElementById('ajaxcdd_ddlPC2ID').value);
ddlPC1.selectedIndex = i;
ajaxcdd_ddlPC2._onParentChange(null, false);
break;
}
}
setTimeout(function () {
var ddlPC2 = document.getElementById(document.getElementById('ddlPC2ID').value);
for (i = 0; i < ddlPC2.length; i++) {
if (ddlPC2.options[i].value == test2) {
ddlPC2.selectedIndex = i;
var ajaxcdd_ddlPC3 = $find(document.getElementById('ajaxcdd_ddlPC3ID').value);
ajaxcdd_ddlPC3._onParentChange(null, false);
break;
}
}
setTimeout(function () {
var ddlPC3 = document.getElementById(document.getElementById('ddlPC3ID').value);
for (i = 0; i < ddlPC3.length; i++) {
if (ddlPC3.options[i].value == test3) {
ddlPC3.selectedIndex = i;
break;
}
}
}, 250);
}, 250);
test1, test2, and test3 are the values that I want to assign the dropdowns to. If I remove the timeouts, the for loops will not iterate since there is not any data immediately populated in the child drop downs (data coming from webservice, and takes about 100 ms).
Is there a way to set the child drop downs (ddlPC2, ddlPC3) without having to use setTimeout?
Thank you.
Related
Let us consider that we have a function, invoked on some event (let's say - scroll event). This function loads more items into some list.
Suppose that the logic of this function is designed as follows:
function() {
oldSize = list.length;
// add new items (prepare them in advance)
for (i = 0; i < PAGE_SIZE; i++) list.push({});
$http.get("/next/page/" + oldSize).then(function() {
// here I want operate with oldSize value which is actual on moment of
// the $http.get invocation:
for (i = 0; i < PAGE_SIZE;i++) {
// do something with
list[oldSize + i] = ... ;
}
}
}
The problem is that the entire function can be invoked almost simultaneously multiple times, what leads to the effect, that .then(function() { operates with improper value of oldSize variable - it becomes the value of last list.length, while I need it be preserved as it was on the moment of invocation.
For instance, if this event listener was invoked almost simultaneously 2 times, it will be:
oldSize == 5, list increased by 10 (for example) elements. But inside $http.get(...).then() I need to work with value oldSize == 5.
Second invocation: oldSize == 15 (because we have increased list by 10 elements in the first invocation). So inside this particular $http.get(...).then() I want to have oldSize == 15.
I hope it is clear. Please, do not suggest me to change my logic. I just want to know how to save variable value for postponed result of the asynchronous function (in my case it is $http.get(...).then(...)). Thanks.
Assuming you are not able to define oldSize inside this function because you need it somewhere else.
function() {
oldSize = list.length;
// add new items (prepare them in advance)
for (i = 0; i < PAGE_SIZE; i++) list.push({});
var currentOldSize = oldSize;
$http.get("/next/page/" + oldSize).then(function() {
// here I want operate with oldSize value which is actual on moment of
// the $http.get invocation:
for (i = 0; i < PAGE_SIZE;i++) {
// do something with
list[currentOldSize + i] = ... ;
}
}
}
Why is oldSize declared outside the scope or globally? Declare the variable in the scope of the function.
let list = [];
function() {
let size = list.length;
$http.get(...)
.then(function() {
// handle size
[...]
});
};
I have a gridview and a dropdownlist in my ASP in the C# i have functionality that will allow the user to select which dropdownlist item will apply to each gridview row.
so in the JavaScript i want slideshow like functionality for each dropdownlist item and count how many gridview rows contain that item.
I'm attempting to get the code to run at a consistent speed (2 seconds per dropdownlist item)
Below is my JavaScript
$(document).ready(function () {
loop();
});
function loop() {
var ddl = document.getElementById("cphMain_ddlInc");
for (var j = 1; j < ddl.options.length; j++) {
setTimeout(shippedEmbryos(j), 2000);
}
}
function shippedEmbryos(j) {
var st = $("input[id*=txtShipped]");
var grid = document.getElementById("cphMain_gvFertEntry");
var ddl = document.getElementById("cphMain_ddlInc");
var embryos = 0;
var ddlValue = ddl.options[j].innerHTML;
for (var i = 1; i < grid.rows.length - 1; i++) {
if (ddlValue == grid.rows[i].cells[2].innerHTML) {
var txtbox = $("input[id*=txtShipped]");
var x = txtbox[i].value;
if (x == "") {
x = 0;
}
}
else {
x = 0;
}
embryos += parseInt(x);
}
var label = document.getElementById("cphMain_lblShippedEmbryos");
label.innerHTML = "Embryos Shipped for " + ddlValue + ": " + embryos;
};
JavaScript doesn't work like that ...
The language is single-threaded, which means that if you sleep for two seconds everything is locked up while you do. That includes your current window -- everything you are trying to draw on screen.
In many browsers all windows freeze.
You need to do event-driven programming here where you fire off an event to draw the next lie.
There are a number of work-arounds depending on if you can use ES7 await/async through a cross compiler or if you can use ES6's Promises. Lastly ES5 gives you setTimeout
What is the JavaScript version of sleep()?
If you want to wait some time in JS, you can't keep looping because it would block thread.
You could use setTimeout method instead or you can use Window.requestAnimationFrame() method, it would fit in your current code better.
Here is draft(modified from MDN sample code)
var start;
function step(timestamp) {
var progress;
if (start === null) start = timestamp;
progress = timestamp - start;
// trigger every 2000ms, equal 2s
if (progress >= 2000) {
//reset start time
start = timestamp;
// do whatever you want ..., such as call shippedEmbryos()
//start another loop
requestAnimationFrame(step);
}
}
MDN:Window.requestAnimationFrame()- This method call every frame and it is browser friendly API(would not affect page rendering performance).
It has one argument : timestamp, browser system would pass current timestamp in automatically.
look like this: 55378.47799999872 55395.189000002574 55411.900000006426....
Then you can keep call the method like recursive.
If you want to stop, just simply not call the method.
I'm trying to add an onclick event handler to a list of checkboxes. Instead of alerting the checkbox ID that I would expect it to (the one I'm clicking on), it's always alerting checkbox number 3 regardless of which on I click. Why is this and how can I get this to behave as desired. When I click on checkbox 2, I want it to alert "2"... etc.
function component (componentId, displayType, parentId) {
this.componentId = componentId;
this.parentId = parentId;
this.displayType = displayType;
}
var components = [];
components.push(new component(0, null, null);
components.push(new component(1, null, null);
components.push(new component(2, null, null);
components.push(new component(3, null, null);
var arrayLength = components.length;
for (var i = 0; i < arrayLength; i++) {
var component = components[i];
jQuery('#questionnaireComponentId'+component.componentId).find('input:checkbox').click(
function(){
selectParentCheckbox(component.componentId);
}
);
}
function selectParentCheckbox(componentId){
alert(componentId);
}
This is happening because of the Javascript closures.
You should pass the parameter each iteration, and not only after finishing the iteration.
for (var i = 0; i < arrayLength; i++) {
var component = components[i];
jQuery('#questionnaireComponentId' + component.componentId)
.find('input:checkbox')
.click(selectParentCheckbox.bind(null, component.componentId));
}
The bind function allows you to name what the function the click event will be calling and what parameters should be passed to it.
Nothing to do with the question, but only for your information: I think you're misunderstanding the power of jQuery and its event delegation, since you could use a much better aproach at all.
function createTextFields(obj) {
for (var i = 0; i < obj.length; i++) {
var dataDump = {};
for (var key in obj[i]) {
var textField = Ti.UI.createTextField(pm.combine($$.labelBrown, {
left: 200,
height:35,
value:obj[i][key],
width:550,
keyboardType:Ti.UI.KEYBOARD_NUMBER_PAD,
layout:'horizontal',
backgroundColor:'transparent',
id:i
}));
dataDump[key] = textField.value;
var callback = function (vbKey) {
return function (e) {
dataDump[vbKey] = e.source.value;
};
}(key);
}
globalData.push(dataDump);
}
}
I am using the simlar code for Adding the data and it works fine. I posted the problem yesterday and it got resolved...
Last Object is always getting updated?
Now when i go to edit page, it shows me four text fields or number of text fields added... now when i edit something and click on save... the value get's updated on the fourth or the last TextFields Object...
Don't define functions inside loops. Computationally expensive and leads to problems, like this one. Here's a fix that should solve it:
function createTextFields(obj) {
var callback = function (vbKey, localDump) {
return function (e) {
localDump[vbKey] = e.source.value;
};
}
var i;
var max = obj.length;
for (i = 0; i < max; i++) {
var dataDump = {};
for (var key in obj[i]) {
dataDump[key] = textField.value;
var callBackInstance = function(keyn, dataDump);
}
globalData.push(dataDump);
}
}
JavaScript does not have block level scope, so your variables dataDump and callback, though "declared" inside for-loops actually belong to the function. As in, you're saving a value to dataDump, then you're overwriting it, each time you go through the loop. Which is why finally only the code that operated on the last value remains.
Take a look at What is the scope of variables in JavaScript? too.
I am creating a simple listbox filter that takes the user input and returns the matching results in a listbox via javascript/jquery (roughly 5000+ items in listbox). Here is the code snippet:
var Listbox1 = $('#Listbox1');
var commands = document.getElementById('DatabaseCommandsHidden'); //using js for speed
$('#CommandsFilter').bind('keyup', function() {
Listbox1.children().remove();
for (var i = 0; i < commands.options.length; i++) {
if (commands.options[i].text.toLowerCase().match($(this).val().toLowerCase())) {
Listbox1.append($('<option></option>').val(i).html(commands.options[i].text));
}
}
});
This works pretty well, but slows down somewhat when the 1st/2nd char's are being typed since there are so many items.
I thought a solution I could use would be to add a delay to the textbox that prevents the 'keyup' event from being called until the user stops typing. The problem is, I'm not sure how to do that, or if its even a good idea or not.
Any suggestions/help is greatly appreciated.
You can do a delay like this:
$('#CommandsFilter').keyup(function() {
clearTimeout($.data(this, 'timer'));
var wait = setTimeout(search, 500);
$(this).data('timer', wait);
});
function search() {
var temp = $("<select />");
for (var i = 0; i < commands.options.length; i++) {
if (commands.options[i].text.toLowerCase().match($(this).val().toLowerCase())) {
$('<option></option>', { val: i, html: commands.options[i].text }).appendTo(temp);
}
}
Listbox1.empty().append(temp.children());
}
This stores a timeout on the element you're typing in, if 500ms (adjust as needed) passes between keystrokes, a search executes. Also this appends the elements in a document fragment then into the DOM (still preserving encoding, etc). Depending on the number of items, this may be a decent performance boost as well.
If the commands drop-down isn't changing, I'd suggest the following (note I've dropped jQuery for better performance and compatibility). There are several improvements:
Timer to delay updating the filtered list once half a second has elapsed since the last keypress
List of command texts is pre-cached
Unnecessary use of match replaced with indexOf
Uses fast native DOM manipulation that works in all scriptable browsers since the 1990s
A quick test suggests that for a drop-down with 5000 options containing short strings, it's between 10 and 30 times faster than the jQuery equivalent in most browsers.
Code:
var commands = document.getElementById("DatabaseCommandsHidden");
var filteredDropDown = document.getElementById("Listbox1");
var filterInput = document.getElementById("CommandsFilter");
var timer;
// Create a cached list of the lower case text of the commands drop-down
var commandTexts = [], commandText;
for (var i = 0, len = commands.options.length; i < len; ++i) {
commandText = commands.options[i].text;
commandTexts.push({original: commandText, lower: commandText.toLowerCase()});
}
function populateFilteredDropDown() {
timer = null;
var val = filterInput.value.toLowerCase(), commandText;
var opts = filteredDropDown.options;
filteredDropDown.length = 0;
for (var i = 0, len = commandTexts.length; i < len; ++i) {
commandText = commandTexts[i];
if (commandText.lower.indexOf(val) > -1) {
opts[opts.length] = new Option(commandText.original);
}
}
}
filterInput.onkeyup = function() {
if (timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(populateFilteredDropDown, 500);
};