I am using bootstrap-multiselect to give the user great controller over two key menus. My first menu is called groups and other called queues. Each option in the queues has an HTML5 data attribute (i.e. data-group="X", where X is a group-value)
When the user selects a option/group from the groups menu, I want to find and hide every queue/option in the queues menu where data-group is not equal to a selected group in the groups menu.
After identifying the queues/items that needs to be hidden/showing, I tried to use .show() and .hide() extensions. Then I tried to use .addClass('hidden') and .removeClass('hidden') methods, but nothing is working for me.
How can I show/hide items on the fly with bootstrap-multiselect?
Here is my code
$(function () {
var queueGroupIds = $('#QueueGroupIds');
var queueIds = $('#QueueIds');
queueGroupIds.multiselect({
nonSelectedText: 'Select group(s)',
onChange: function (option, checked, select) {
var groups = queueGroupIds.find('option:selected');
if (groups.length == 0) {
//When none of the groups are selected, show all queues!
queueIds.find('option').each(function (i, q) {
$(q).show();
});
}
var queueToDeselect = [];
//loop over every select option "if any are selected"
groups.each(function (index, grp) {
var group = $(grp);
// loop over every queue option
queueIds.find('option').each(function (i, q) {
var queue = $(q);
//id the data-group value == selected group show the item
if (queue.data('group') == group.val()) {
queue.show();
//this prints the value which should be showing
console.log('showing', queue.val());
} else {
queueToDeselect.push(queue.val());
queue.hide();
//this prints the value which should be hidden
console.log('hidding', queue.val());
}
});
});
//Delected all hidden queues
queueIds.multiselect('deselect', queueToDeselect);
queueIds.multiselect('refresh');
}
});
queueIds.multiselect({
nonSelectedText: 'Select queue(s)'
});
});
Edited for default none selected:
Example shown in the following fiddler (I've trimmed it down to a base example for clarity): https://jsfiddle.net/m6uuL53w/3/
No need for any manual messy DOM ADD/REMOVE manipulation. Multiselect will carry over the disabled class you put on your raw list so you just need target it with css after you set the disabled values and refresh your list. Let multiselect worry about dom manipulation.
Example HTML:
<select id="one" multiple="multiple">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<select id="two" multiple="multiple">
<option data-group="1" value="OneA" disabled >One A</option>
<option data-group="1" value="OneB" disabled>One B</option>
<option data-group="2" value="TwoA" disabled>Two A</option>
<option data-group="2" value="TwoB" disabled>Two B</option>
<option data-group="3" value="ThreeA" disabled >Three A</option>
</select>
Jquery:
$(document).ready(function() {
$('#one').multiselect({
onChange: function(element, checked) {
var opts = $('*[data-group="'+ element.val() +'"]');
if (checked === true) {
opts.prop('disabled', false).prop('selected', false);
}
else if (checked === false) {
opts.prop('disabled', true).prop('selected', false);
}
$("#two").multiselect('refresh');
}
});
$('#two').multiselect();
});
Just a pinch of CSS:
.multiselect-container > li.disabled { display:none;}
https://jsfiddle.net/ta1wvs3j/1/
//Because we alter the DOM, we need to save an instance of the original list
//There are many ways to do this, I chose an easy one
var globalClone = $('#queues option');
//Init the queues
$('#queues').multiselect({
nonSelectedText: 'Select queue(s)',
onChange: function (option, checked, select) {
}
});
//Init the groups
$('#groups').multiselect({
nonSelectedText: 'Select group(s)',
onChange: function (option, checked, select) {
//Store the list of selections in an array
var selections = $('#groups').val();
//Reset the queue to it's starting list so we can loop
$('#queues').html(globalClone);
//Look at each option
$('#queues option').each(function(){
//"includes" may not be widly suppoerted but there are other ways to see if a value is in an array
//Check to see if the data-group value is in the selections array, if it's not
if( selections.includes(String($(this).data('group'))) == false ){
//Remove the option from the DOM
$(this).remove();
}
});
//Rebuild, per the multiselect docs basically reinit the select. Nice we don't have to destroy and recreate.
$('#queues').multiselect('rebuild');
}
});
This isn't how I'd leave production code exactly but it shows you how it can work.
Basically, loop, check, alter DOM, and rebuild multiselect. I think this is what you want.
Related
so, i want to hide "Overtime AC" Value when i choose Balikpapan Office.
This is my code, still not working
var ddlLoc = $("[id$='_ddl_OfficeLocationosf']");
var ddlReqFor = $("[id$='_ddl_RequestForosf']");
ddlLoc.change(function() {
if(ddlLoc.find("option[value='Balikpapan Office']") == true) {
ddlReqFor.find("option[value='Overtime AC']").parent().parent().hide();
} else {
ddlReqFor.find("option[value='Overtime AC']").parent().parent().show();
}
});
Why use parent().parent() of the option you want to hide?
Do they have a common parent?
$("[id$='_ddl_OfficeLocationosf']").on("change",function() {
$(this)
.closest(".commonContainerClass")
.find("[id$='_ddl_RequestForosf'] option[value='Overtime AC']")
.toggle(this.value === 'Balikpapan Office')
})
Native JavaScript Solution
This is a native JavaScript solution to compliment #mplungjan jQuery solution. The solutions work differently, but both solve the problem of creating a cascading select.
Add an event handler to capture the office name and copy it to data-office attribute on the second select:
_ddl_OfficeLocationosf.addEventListener('change', function() {
_ddl_RequestForosf.dataset.office = this.value;
});
Then we add a css rule that selectively hides options that match the selected offices. This is done by adding a data-exclude attribute with a list of office names where the options should be hidden. Note that we're using a *= match so that we don't need to use full names. In this case we are only hiding one option, but it would be easy to create custom lists for each office.
#_ddl_RequestForosf[data-office*=Balik] option[data-exclude*=Balik] {
display: none;
}
And the markup
<option data-exclude="Balikpapan">Overtime AC</option>
Snippet
The snippet includes some additional code to handle loading and switching offices. See comments for details.
_ddl_OfficeLocationosf.addEventListener('change', function() {
_ddl_RequestForosf.dataset.office = this.value;
// optional -clear previous selection when not an option for current office
let option = _ddl_RequestForosf.querySelector('option:checked');
if (option && getComputedStyle(option).display === 'none') {
_ddl_RequestForosf.value = "";
}
});
// optional - update the control on page load
document.addEventListener('DOMContentLoaded', function() {
_ddl_OfficeLocationosf.dispatchEvent(new Event('change'));
});
body {
font-family: sans-serif;
}
#_ddl_RequestForosf[data-office*=Balik] option[data-exclude*=Balik] {
display: none;
}
<label>Office
<select id="_ddl_OfficeLocationosf">
<option>Balikpapan Office</option>
<option>Brunei Office</option>
<option>Makassar Office</option>
</select>
</label>
<label>Request:
<select id="_ddl_RequestForosf">
<option>Stationary</option>
<option>Business Card</option>
<option>Stamp</option>
<option data-exclude="Balikpapan">Overtime AC</option>
<option>Condolence Bouquet</option>
<option>Fogging for Employee House</option>
<option>Foldable Box</option>
<option>Others</option>
</select>
</label>
In a bespoke shopping cart, I pull from my database a list of options that a particular product has. The number of these options can vary by product.
I then turn that list of options into a JavaScript array.
An example with 3 options:
{"Small":{"Super":{"Pack":"parta","Case":"parte"},"Maxi":{"Pack":"partb","Case":"partf"}},"Large":{"Super":{"Pack":"partc","Case":"partg"}},"X Large":{"Maxi":{"Pack":"partd"}}}
Using the above, I would now like to generate an HTML select field, listing all the "first" options:
<select>
<option value="Small">Small</option>
<option value="Large">Large</option>
<option value="X Large">X Large</option>
</select>
Once the user has selected the first option, I then need another <select> box to then load with options that are available for their selection. So, if they selected "Small" from above, the new select box would be:
<select>
<option value="Super">Super</option>
<option value="Maxi">Maxi</option>
</select>
Finally, when they select from this list, a 3rd select box loads in the final options, along with the part numbers as values:
<select>
<option value="parta">Pack</option>
<option value="parte">Case</option>
</select>
The number of options can vary, from zero to 4. But, each time when options are available, I need to pull the part number based on the users selection. The part number doesn't necessarily need to be the value of the last select, it can be pushed to a new hidden variable.
I can achieve this using ajax, by making an ajax call every time a selection is chosen, but can it be done via JavaScript / jQuery, without having to make ajax calls, given that the array is on the page and available to use?
When you dynamically create the select element, also determine which "node" in your tree structure goes with that element, and use it to:
add a default "Please select..." option to the select element
populate the select element further with the real options
determine the deeper node when an option is selected (also when the initial selection is made when the element is created), and use it to create the next select element with the same function
This cascade stops when the deeper node does not exist (when "Please select..." is selected) or it happens to be a string and not an object.
Here is some code for inspiration:
let optionTree = {"Small":{"Super":{"Pack":"parta","Case":"parte"},"Maxi":{"Pack":"partb","Case":"partf"}},"Large":{"Super":{"Pack":"partc","Case":"partg"}},"X Large":{"Maxi":{"Pack":"partd"}}};
let container = document.querySelector("#container");
addSelector(optionTree);
function addSelector(node) {
let select = document.createElement("select");
// Start with the default option:
let option = document.createElement("option");
option.text = "Please select...";
select.add(option);
for (let key in node) { // Populate the select element
let option = document.createElement("option");
option.value = key;
option.text = key;
select.add(option);
}
container.appendChild(select); // Add it to the page
function change() {
// Remove select elements that come after the selection
while (select.nextElementSibling) {
select.nextElementSibling.remove();
}
let key = select.value;
if (node[key] && typeof node[key] !== "string") {
addSelector(node[key]); // Create the next select element(s)
}
}
// Call the above function whenever a selection is made
select.addEventListener("change", change);
change(); // ... and also call it now
}
<div id="container"></div>
Assuming you already have the three select elements in the DOM, you can populate the first select, and add a change event listener to both the first and second to achieve this. Try this
let $s1 = document.querySelector('#select-1');
let $s2 = document.querySelector('#select-2');
let $s3 = document.querySelector('#select-3');
let object = {"Small":{"Super":{"Pack":"parta","Case":"parte"},"Maxi":{"Pack":"partb","Case":"partf"}},"Large":{"Super":{"Pack":"partc","Case":"partg"}},"X Large":{"Maxi":{"Pack":"partd"}}};
// add options to first select
$s1.innerHTML = '<option></option>'; // empty select
Object.keys(object).forEach(val => $s1.append(new Option(val, val))); // append children
$s1.dispatchEvent(new Event('change')); // trigger change event
// listen to change event on first select/get options for second select
$s1.addEventListener('change', function(e){
$s2.innerHTML = '<option></option>'; // empty select
// append children
Object.keys(object[e.target.value] ?? []).forEach(val => {
$s2.appendChild(new Option(val, val));
});
// trigger change event
$s2.dispatchEvent(new Event('change'));
});
// listen to change event on second select/get options for third select
$s2.addEventListener('change', function(e){
$s3.innerHTML = '<option></option>'; // empty select
// append children
Object.entries(object[$s1.value]?.[e.target.value] ?? []).forEach(([key, val]) => {
$s3.appendChild(new Option(key, val));
});
});
select {min-width:80px}
<select id="select-1"></select>
<select id="select-2"></select>
<select id="select-3"></select>
I have drop down menu with some random values. When I select the value onchange event triggers and I want to add new drop down under it, but the new one should have all values except selected one in first drop down. Now when I change value of second one, I need third one that has only non selected values from previous two drop downs and so on until there is no more option to select.
What I have for now is mechanism for adding new drop downs, but it is not working correctly. If I for example select first value it will do the job, new drop down is shown and it has all options except selected one. But then if I go back and try to change option for first drop down third drop down is created. This should not be a case, if drop down already created another drop down it should not do it again. Is it even possible to avoid triggering of new onchange event for drop down that already triggered one? Or maybe some other kind of event is more suitable for this case?
This is my function that does described job:
createNewDropDonws : function() {
var selectWrapper = $('#select-boxes');
$(document).on('change', '.dynamic-select', function() {
var element = $(this);
var optionsLength = (element.find('option').length) - 1;
if(optionsLength === 1) {
return true;
}
var newSelect = $(this).clone();
newSelect.find("option[value='" + element.val() + "']").remove();
newSelect.appendTo(selectWrapper)
});
}
This is my HTML for drop down:
<div id="select-boxes">
<select class="dynamic-select" id="ddlTest">
<option value=""></option>
<option value="Belbin">Belbin</option>
<option value="Raven">Raven</option>
<option value="PPA">PPA</option>
<option value="PPA+">PPA+</option>
<option value="Basic Knowledge">Basic Knowledge</option>
<option value="PCT">PCT</option>
</select>
</div>
And this is simple CSS class:
.dynamic-select{
display: block;
margin: 10px;
}
Does anybody has any idea how this can be implemented correctly by using jQuery?
We can do it in following way:
We are just adding some class like executed once it add another dropdown. And in event handler we are checking if current dropdwon has that class or not. If it is already there then it means we are already done with that dropdown as follows:
$(document).ready(function() {
var selectWrapper = $('#select-boxes');
$(document).on('change', '.dynamic-select', function() {
var element = $(this);
if(element.hasClass("executed"))
return;
var optionsLength = (element.find('option').length) - 1;
if (optionsLength === 1) {
return true;
}
var newSelect = $(this).clone();
newSelect.find("option[value='" + element.val() + "']").remove();
newSelect.appendTo(selectWrapper)
$(this).addClass("executed");
});
});
.dynamic-select {
display: block;
margin: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="select-boxes">
<select class="dynamic-select" id="ddlTest">
<option value=""></option>
<option value="Belbin">Belbin</option>
<option value="Raven">Raven</option>
<option value="PPA">PPA</option>
<option value="PPA+">PPA+</option>
<option value="Basic Knowledge">Basic Knowledge</option>
<option value="PCT">PCT</option>
</select>
</div>
I started studying javascripting and was wondering if anyone know how to hide values in dropdown list for html?
For example: a dropdwon list with values
Select One
Item1
Item2
Item3
Item4
Item5
I wanna hide the Item 4 and 5, like this and show it when "Show... " is clicked.
Select One
Item1
Item2
Item3
Show 2 more items (Item 4 and 5 hidden)
Is that possible? Below is a piece of code i already started.
var css = select;
var markers = cluster.getMarkers();
var markersLength = markers.length;
var nextOption = new Option("Select One");
css.add(nextOption, 0);
for(var i = 0; i < markersLength; i++) {
nextOption = new Option(markers[i].title);
try {
css.add(nextOption, -1);
} catch (e) {
css.add(nextOption, null);
}
}
You want a generic solution, so tag the more option and the hidden items with classes.
It turns out you cannot consistently style-out options in a select across browsers, so you need to dynamically alter the list options: Refer to this question: How to hide a <option> in a <select> menu with CSS?
Final solution (append elements from another hidden select):
JSFiddle: http://jsfiddle.net/TrueBlueAussie/93D3h/12/
HTML:
Select One
<select class="hidden">
<option>Item4</option>
<option>Item5</option>
<option>Item6</option>
<option>Item7</option>
<select>
<select>
<option>Item1</option>
<option>Item2</option>
<option>Item3</option>
<option class="more">More</option>
</select>
jQuery:
$('select').change(function(){
var $select = $(this);
if ($select.val() == "More"){
$('.more').remove();
$select.append($('.hidden').children());
}
});
Previous info:
Then on then select change event you hide the more option and show the hidden elements:
JSFiddle: http://jsfiddle.net/TrueBlueAussie/93D3h/2/
$('select').change(function(){
var $select = $(this);
if ($select.val() == "More"){
$('.more').hide().prevAll('.hidden').show();
}
});
There appears to be a weird bug in selects as the last item is always visible (even when styled out!). I added a blank entry to fix this for now. This is also why I did not place the hidden items after the more as the last one always shows (what a strange bug - have asked that as a new question: Why is last select option always shown, even when styled out).
You will also want to clear the selected value of "More" as that will no longer exist.
e.g. http://jsfiddle.net/TrueBlueAussie/93D3h/3/
$('select').change(function () {
var $select = $(this);
if ($select.val() == "More") {
$('.more').hide().prevAll('.hidden').show();
$select.val('');
}
});
Followup:
Based on my related question, I was pointed to this one: How to hide a <option> in a <select> menu with CSS? Apparently you cannot style out select options consistently, so adding the items to the list dynamically would be the ideal solution.
Here's my solution:
Html
<select id="test">
<option value="1">Select One</option>
<option value="2">Item 1</option>
<option value="3">Item 2</option>
<option value="4">Item 3</option>
<option value="5">Select Two</option>
<option value="6">Item 4</option>
<option value="7">Item 5</option>
</select>
Script
var array1 = ["1","6","7"];
var array2 = ["1","2","3","4"];
var arrayAll = ["1","2","3","4","5","6","7"];
function hideOptions(array) {
for (var i = 0; i < array.length; i++) {
$('#test option[value="' + array[i] + '"]').hide();
}
}
function showOptions(array) {
for (var i = 0; i < array.length; i++) {
$('#test option[value="' + array[i] + '"]').show();
}
}
$("#test").change(function(){
if($("#test").val()=="5"){
hideOptions(array2);
showOptions(array1);
}
if($("#test").val()=="1"){
hideOptions(array1);
showOptions(array2);
}
});
hideOptions(array1);
here's the fiddle
What about something like:
<head>
<script type="text/javascript">
function makeDynamicOption(target, threshold, messageMore, messageLess) {
var allOptions = collectOptions();
target.addEventListener("change", updateOptions, false); // Use your own event manager
showOptions(threshold);
addMessage(messageMore);
// ---
function collectOptions() {
var options = [];
for(var ii=0; ii<target.options.length; ii++) {
options.push(target.options[ii]);
}
return options;
}
function updateOptions() {
var selectedText = this.options[this.selectedIndex].text;
if (selectedText == messageMore) {
showOptions(allOptions.length);
addMessage(messageLess);
} else if (selectedText == messageLess) {
showOptions(threshold);
addMessage(messageMore);
}
}
function showOptions(upToIndex) {
removeOptions();
for (var ii=0; ii<upToIndex; ii++) {
target.options[ii] = allOptions[ii];
}
}
function removeOptions() {
while(target.options.length > 0) {
target.removeChild(target.options[0]);
}
}
function addMessage(message) {
target.options[target.options.length] = new Option(message, "");
}
}
</script>
</head>
<body>
<select id="foo">
<option value="value1">item1</option>
<option value="value2">item2</option>
<option value="value3">item3</option>
<option value="value4">item4</option>
<option value="value5">item5</option>
</select>
<script type="text/javascript">
makeDynamicOption(
document.getElementById("foo"),
3,
"More...",
"Less..."
);
</script>
</body>
This design separates the lib part (to be linked in the HEAD as an external script) from the activation part. It also lets you inject localized text while generating the view, and preserve existing options in case you have other scripts interacting with them. Note that you should still use your own event manager, and not addEventListener directly as shown in the script, for better cross-browser support.
EDIT: here's how the scripts works:
You call the makeDynamicOptions() function on the select object you want to augment, passing the number of options you want to display, as well as messages to expand/collapse other options. The messages can be written by the view manager, i.e. it could be easily localized if needed.
The first initialization step sees that all original options be collected, so that they can be added back when the user wants to expand the select. Note that we collect the objects themselves, and not only their value/text property values, as other scripts could reference these objects.
The second initialization step registers a change handler on the select, so as to trigger the update on the options list. The script uses addEventListener, but one should substitute one's own event management mechanism, for better cross-browser support.
The last initialization step collapses the select in the intended start position.
The rest is pretty straightforward. Once the user selects an option, the script decides whether the list of options should be repopulated, by analyzing the text of the selected option, and comparing it to the provided expand/collapse labels. If options are to be redrawn, then the script removes all options, adds the expected ones, then adds the new expand/collapse message.
HTH.
I have a PHP page that creates multiple selects depending on how many the page before it gives it and creates the same number of options that there are selected (it's to choose the priority).
<select name="select1" id="idSelect" onchange="javascript:SelectOnChange();">
<option value="Select one">Select one</option>
<option value="1">1</option>
<option value="2">2</option>
</select>
What I want to do is, whenever the user chooses an option that was already selected in another selection, to show an alert in order to let them know that their choice has already been taken and deny the change.
I want to compare the currently selected option to every previously selected option every time the user selects something from every select.
Basically your looking for a change event to loop through each select and notify duplicates.
$("select").change(function() {
var currentSelection = this.value;
$("select").each(function() {
if (this.value == currentSelection) {
alert("you've already chosen this!");
}
});
});
Something like this should work
Listen for change events
Get the element's seletedIndex
Grab all of the selects with getElementsByTagName()
Loop through and get the selected index
Compare to see if used
This could maybe work :
var $selects = $('select');
// WHen a select is changed.
$selects.onchange(function() {
// Storing it's selected value, assuming there is only one per select.
var value = $(this).selected().first().attr('value');
var $changedSelect = $(this);
// For each select...
$selects.each(function() {
var $otherSelect = $(this);
// ...that is not the one that just changed.
if( ! $otherSelect.is($changedSelect)) {
// If that other select's selected value is the same as the changed one's
if($otherSelect.selected().first().attr('value') === value) {
alert('...');
}
}
});
}
I didn't try it though, you might have to change a few details in it if it doesn't work.