Does anyone have any solution to improve the speed when deleting/adding <option> from a <select> when it has 60k+ elements?
I've a form with two <select>, let's call them selectA and selectB. I want the values of selectB to change when selectA is changed. Like (code not functional, just for idea):
if selectA = option1
then selectB.options = [opt1_1, opt1_2, opt1_3, ...]
else if selectA = option2
then selectB.options = [opt2_1, opt2_2, opt2_3, ...]
...
I have a (quite huge) variable that gives me the list of option for selectB associated with selectA value.
I've found many example for that kind of thing and it works on small examples. For now, I've something like :
$(document).ready(function() {
$("#selectB").html("<option value='' slected='selected'>Choose SelectA first</option>");
$("#selectA").change(function() {
var val = $(this).val();
var options = //... string generated with all <option></options> depending on $val
$("#selectB").html(options)
});
});
Here comes the mess, as selectB is a list of IPv4 that can contain a full /16. So that's 60k+ elements to handle (ouch ^^ ). And that's why I don't want to have every option in the select but instead "pre-select" with selectA and then "sub-select" with selectB.
I want to have all possible IPs initially in the HTML form because of the following reason : Even if JS is not enabled, the user must be able to choose one IP (the server checks that the choice from selectA and selectB corresponds). And only if JS is enabled I remove everything from selectB and add only the values I want when selectA is changed. So you might say that anyway a user with no JS can't event choose an option without loading 1GB+ RAM of options and wait 5-10 min for every option to be loaded, but I also want the page to works if there is only a few options to display like 1k.
I've to admit I was amazed my browser (FF) handles the creation of the page quite quickly. But when JS delete all the options, everything freezes on :
$("#selectB").html("<option value='' slected='selected'>Choose SelectA first</option>");
What I've managed to debug is that what takes time is when the browser needs to unload/delete every options. Actually creating them is not that slow.
So I would like to know if there is a faster solution than what I'm doing right now. How can I speed up the deletion of all <option> in selectB ?
Any suggestion is welcome from a simple change in JS to change the whole structure of HTML to not use <select> but something else (I don't know).
Related
I have a checkbox which toggles state - giving a true or false value.
If the value is false, I'd like to hide the select box. If the value is true, I'd like to show the select box. This is all working 'out of the box' with just some template stuff - I simple do:
{{#if fooState }}
<select class="form-control" id="addFoo" value='{{selectedFoo}}'>
<option selected disabled>Select Foo</option>
{{#foo}}
<option value="{{_id}}|{{title}}">{{title}}</option>
{{/foo}}
</select>
{{/if}}
However, if I select a Foo option... this option remains selected when I set the fooState to false. I'd like to reset this - so the first option (select Foo) is selected - emptying the {{selectedFoo}} value.
I've tried doing stuff with observe, and events - but cant quite seem to grok this. When I punch my code into the console - I'm able to change the select option... but cant seem to trigger it from when the state changes.
Ok - so I finally figured it out... to do this, I don't lean on the data binding... I create a new on-change event and add that to the select.
<select class="form-control" id="addFoo" on-change='selectedFoo' >
<option></option>
</select>
ractive.on('selectedFoo' , function (event) {
var resourceArray = event.node.options[event.node.options.selectedIndex].value // Get our selected Foo option
resourceArray = resourceArray.split('|')
var FooData = {
a1: resourceArray[0],
a2: resourceArray[1],
a3: 'foo'
}
data.foo.push(resourceData);
});
So grab the data from the event - and then manually push it onto the array. This is completely self contained - it only affects this single select. Now does everything that I need to it to.
* Scrub this... it appears to break two way data binding. Please see above *
So I was watching a Ractive video - and had an epiphany! I was sort of stabbing around in the right area - but doing it all wrong:
ractive.on('fooStateToggle', function(event) {
console.log(event)
if (data.fooState) {
// Reset our select box! This took me ****ing ages to figure out... but its so, so, simple!
var addFooElem = ractive.nodes.addFoo;
addFooElem.selectedIndex = 0;
} else {
.....
}
});
So I needed to add some code in the event that's fired when the checkbox is clicked (fooStateToggle). Then if fooState is true... we grab the select box from the Ractive node list (not the dom - this is a virtual dom, so doing a direct selection wasn't going to work) and select the the first option by index. One gotcha... when the elem is hidden, its no longer available in the ractive.node list (makes perfect sense as its not being rendered to the page) so you have to catch it as soon as its rendered. You cant act upon it when its hidden - as it doesn't exist. I must say, I'm loving Ractive... I'm finding it so much quicker to pick up than React / Angular or any of the others. I love it! (thats probably going to get edited out by someone - but the guys who made this are awesome. Its made my dev so much easier!!)
There are several questions very similar to this one yet I have been unable to come up with a solution.
I have a select list using angularJS. I need to use the title attribute so I have an ng-repeat to create the options, there is always a blank option even if I use ng-selected to always select the first option.
Even if I make a selection and the blank option goes away, if I then filter out that selected value the blank will reappear.
I have included a select list using ng-option (which does not include my needed tittle attribute) and a default value to show that the blank will appear after filter.
The behavior I desire would be to never have a blank option (always selecting first option would be fine) and to possibly have a directive per option for special handling of click events.
Thanks in Advance!
JS Fiddle: http://jsfiddle.net/32DFM/3/
<select size="3" ng-model="person.current">
<option ng-repeat="p in people | filter:person.SearchTerm"
ng-selected="$first"
value="{{p}}"
title="{{p.name}}">
{{p.name}}
</option>
</select>
I forked your fiddle (if I may be so blunt): http://jsfiddle.net/XsFe8/2/
This fixes it somewhat. Although I haven't gotten it to work properly together with the filter.
Anyway, what I do here, is to use the person.id as the value on each option.
<select ng-model="person.current">
<option ng-repeat="p in people | filter:person.SearchTerm" ng-selected="$first" value="{{p.id}}" title="{{p.name}}">
{{p.name}}
</option>
</select>
And set the initial calue on the person.current model:
$scope.person.current = $scope.people[1].id;
But it's still not 100% though. I'm a bit stumped to why the blank spaces appear when you filter the select....
An alternative that might or might not work, would be to use something like ng-repeat="p in filterPeople() and filter your array in a filterPeople function. But I'm not sure if this will change anything.
UPDATE: I tested out my suggestion above, here: http://jsfiddle.net/XsFe8/2/
If you set the selected object to be the first object in the filtered array, it works. I do this each time a new filtered array is created:
$scope.filterPeople = function () {
var array = filterFilter($scope.people, $scope.person.SearchTerm);
$scope.person.current = array[0].id;
return array;
};
It looks like things get hairy when another object than what is visible in the select is actually selected. This is kind of understandable :)
Your actual problem is the value in ngModel is referencing a value which doesn't exist in the select anymore.
A solution is to whenever you alter the select options, you also check the person.current to ensure that it points to a valid entry.
This also implies that you might want to move your filter into the controller, and set the options in the scope (you can use the $filter service in your code to get same behaviour there, https://docs.angularjs.org/api/ng/filter/filter). This way you can have a function in your controller checking if person.current is valid, and if not, set it to desired options (e.g. the first one).
the hairyness cited above is due to an empty array when all items are filtered out and is fixed by:
if(array.length>0)
$scope.person.current = array[0].id;
http://jsfiddle.net/b0z6vpr8/
Hope this helps
I am wanting to try to change or limit a drop down list using JavaScript, or some other solution. Unfortunately, I have no control over the way the HTML comes out that I am trying to change client side. The drop down list is generated server side, but we would like to give the user additional options to further limit the choices in the drop down list.
We can't edit what is generated, but we can insert HTML.
One suggested solution, which may not be possible, is to use JavaScript to limit the dropdown list. For example, the drop down follows the format of:
<SELECT ID="dropdown_1">
<OPTION VALUE="" >None
<OPTION VALUE="1000">AB-ITEM 1 DESCRIPTION
<OPTION VALUE="2001">AB-ITEM 2 DESCRIPTION
<OPTION VALUE="50" >AB-ITEM 8 DESCRIPTION
<OPTION VALUE="70" >BB-ITEM 3 DESCRIPTION
<OPTION VALUE="100" >BB-ITEM 5 DESCRIPTION
<OPTION VALUE="2" >ABB-ITEM 4 DESCRIPTION
</SELECT>
What I want to limit by the beginning of the text, so AB-, BB-, or ABB- in this case. The value has no rhyme or reason, it's just an index number. I don't think this is possible since this is just text, and not associated with an attribute.
One thought would be to be to:
Store the list into a JavaScript array
Keep only entries like 'TYPE-X%'
Delete original HTML list
Replace with new list stored in the Array
However, I'm not sure if this is possible, and if it is, what would be needed to create such code. Any help or references to functions or examples would be greatly appreciated.
Anything is possible (with jQuery):
$(document).ready(function() {
$("#dropdown_1 option").hide();
$("#dropdown_1 option").filter(":contains(TYPE-X)").show();
});
An advantage with this is that all of the options are still there, you just can't see them. So all it would take to return to the default list would be a call to:
$("#dropdown_1 option").show();
Edit for regex:
$(document).ready(function() {
$("#dropdown_1 option").hide();
$("#dropdown_1 option").filter(function() {
return $(this).text().match(/^AB-/);
}).show();
});
You can filter your list using a regex like seen above.
Edit: A note about jQuery, this in the filter function is the DOM element itself. In order to access the jQuery helper method text(), I first need to wrap that DOM element with the jQuery function, as edited above.
Thanks to BinaryTox1n, I was put on a good path to find an all-browser compatible solution to my question. However, it is a slightly different approach pieced together from other solutions on StackOverFlow.
The difference comes is how one deals with the OPTIONs. Though .hide() works on some browsers, it is not compatible with IE8 (and maybe some others). Alternatives and variations to .hide() also failed. Another problem with .hide() is that you also need to use .disable(). The last problem is that if you have several (20 or so) options and only 2 or 3 are visible, Chrome (and perhaps other browsers) do not render the dropdown box properly.
The best approach found is to .remove() unwanted options. No compatibility issues because the OPTION is simply removed. However, I also want to have the flexibility to add back options if needed. So the following is a version of what I'll be using:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
var regex_str = "^AAB-";
var dd1 = $("#dropdown1 option");
//Clone the 'None', Current, and All options into respective variables.
//All options are stored in order to allow different selection criteria
var all_Opt = dd1.clone(true);
var none_Opt = dd1.filter(":contains(None)").clone(true);
var cur_Opt = dd1.filter(function(){
return $(this).text().match(regex_str);
}).clone(true);
//Remove all options and replace the 'None' and Current options
dd1.remove();
noneOpt.appendTo($("#dropdown1"));
curOpt.appendTo($("#dropdown1"));
});
</script>
The only thing I'd like to add is the ability for this to change using a different drop down box or some other trigger.
I have a problem with my price calculator when I've tried to add options. (jsFiddle here = http://jsfiddle.net/wemdragon/ZG8m)
What I'm trying to do is where it says:
var firstoption=$('select#myselect')
I would like the options' value turned into the price value but I can't seem to do it. It works in my shopping cart but not in the JavaScript.
Maybe I'm trying to add the value in the wrong place, I don't know what's going wrong.
Your select id (at least in the fiddle) was camelCased as mySelect but you were trying to access the id #myselect. This should look like the following unless you change your markup's id:
var firstoption = $('select#mySelect');
I also noticed that you specified a default value of 0 for not selecting an option. For some reason, !firstoption.val(), when the val() was zero, returns false instead of true like your if statement seemed to be expecting. !0 is apparently true in this instance.
I changed the logic to match your mark-up and it seemed to behave like you might have expected or want:
if (firstoption.val() == 0) {
error.html('Please include an option!');
firstoption.focus();
}
Edit Updated fiddle: http://jsfiddle.net/ZG8mv/8/
Now the focus() executions will show in the order that they are wrong. I.e., if there is no width and no option specified, that box will receive focus. Previously, the option message would trump the other two.
I have 50 rows of data and i want users to give them points by 1 to 50. I put dropdown boxes near them with options 1/50. But all i want is when a user selects 15(for example) for a row, 15 will be deleted from all other select tags of other rows. I am not as good as you in JavaScript. How can i accomplish this?
Hi casablanca i couldnt make he script you sent work. I need it to work on just one select tag so i give select tag an ID and an ID for the form too. I edit the scripts getElementsByTagName with getElementsByTagID (select tag's ID) to effect only one select tag. But the function doesnt triggered?
This might not be a very good idea, because it is very difficult for the user to modify choices -- for example, if I want to give 15 to a different option, I need to change the old one to something else and then change the new one to 15. Also, once all points have been assigned, it's impossible to make any changes because all options are gone.
A better idea would be to let the user do whatever he/she wants and then validate the form in the end. You could do that like this:
function validate() {
var used = []; // This array stores already used options
var rows = document.getElementById('myForm').getElementsByTagName('select');
for (var i = 0; i < rows.length; i++) {
var points = rows[i].value;
if (used[points]) {
// This value was already used
alert('Please choose a different value.');
rows[i].focus();
return false;
} else {
// This value was not used before; mark it as used now
used[points] = true;
}
}
return true;
}
And call this function in the onsubmit handler of your form:
<form id="myForm" onsubmit="return validate();">
EDIT1: id -> class
give each option the class of the number it is
<option class="15">15</option>
<option class="16">16</option>
etc.
Then jquery can remove() an item by class
$('.15').remove();
obviously have to do an on change and get the value just set. "remove()" is nice in this instance because I believe it will yank every instance of that class.
EDIT3: upon further consideration the above method would be further complicated by the need to not remove the "selected" option. Not going to figure out the exact method but I think changing the class from "15" to "selected15" with a $(this).append() or something of the sort before calling the remove would get the job done fairly safely.
EDIT2:
As noted by casblanca below this is not an ideal user interface at all for this type of input. You may want to look into this: http://www.utdallas.edu/~jrb048000/ListReorder/
Allows user to drag and drop items in a list to reorder them. Much more natural.