How to select all options within an optgroup? - javascript

I am wondering what the easiest way to select all options within a single optgroup is?
My understanding is that optgroup labels themselves cannot be selected, so my current implementation has an option element immediately following each optgroup label that says something like "All Options in this optgroup". I'm able to detect the event when this "All" option is selected, but I'm lost after that point. Is this the right path to head down, or am I making this harder than it needs to be?

Use the attribute selector:
'[attribute name = "value"]'
const groupA = document.querySelector('[label="A"]');
/*
Using .querySelectorAll() to collect all of optgroup A options into a NodeList
*/
const groupA = document.querySelector('[label="A"]');
const allOptA = groupA.querySelectorAll('option');
allOptA.forEach(opt => opt.style.color = 'tomato')
/*
Using .children and spead operator [...x] to get all options from optgroup B
into an Array
*/
const groupB = document.querySelector('[label="B"]');
const allOptB = groupB.children;
[...allOptB].forEach(opt => opt.style.color = 'lime');
// Gather all optgroups into an Array
const allGroups = [...document.querySelectorAll('optgroup')];
// Gather each optgroups' options into a sub-array
const allOpts = allGroups.flatMap(grp => [grp.querySelectorAll('option')]);
// Get the value of the first optgroup third option
console.log(allOpts[0][2].value);
<select>
<optgroup label='A'>
<option value='0'>0</option>
<option value='2'>2</option>
<option value='4'>4</option>
</optgroup>
<optgroup label='B'>
<option value='1'>1</option>
<option value='3'>3</option>
<option value='5'>5</option>
</optgroup>
</select>

Related

How to create HTML dynamic select option from JSON hash?

I have a simple $test value code:
$test = A,bb,100|A,ff,200|A,ee,100|D,ee,3|D,gg,10|R,ii,7
I have a select tag in my HTML:
<select name="datas" id="datas"></select>
I need a simple way to create HTML select box from this JSON, like this:
<select>
<option value="">A</option>
<option value="100">bb</option>
<option value="200">ff</option>
<option value="100">ee</option>
</select>
<select>
<option value="">D</option>
<option value="3">ee</option>
<option value="10">gg</option>
</select>
<select>
<option value="">R</option>
<option value="7">ii</option>
</select>
You mention JSON, a pre-existing select and the "php" tag although I don't see how any of those things are relevant to your question.
Please see the code below to produce a select with its content, per your requirements:
// This isn't JSON. It's just a string
let $test = "A,bb,100|A,ff,200|A,ee,100|D,ee,3|D,gg,10|R,ii,7";
// Split the initial string where there are | characters and then loop
// over the resulting array of strings
$test.split("|").forEach(function(values){
// Create a new `select` element
const sel = document.createElement("select");
// Split the part of the string that contains the next level
// values and loop over them
values.split(",").forEach(function(value){
// Create an `option` element
const opt = document.createElement("option");
opt.textContent = value; // Set the value to the individual string
sel.appendChild(opt); // Append the option to the select
});
document.body.appendChild(sel); // Add the select to the page.
});

move option from one select to another select with JS

From two html selects, I would like to move when I click the button to move an option to another select with javascript vanila, and when it has been moved, it is removed from the select from where it was at the beginning. It should also work the other way around.
function move1() {
var x = document.getElementById("select1");
}
function move2() {}
<select id="select1">
<option value="0">1</option>
<option value="0">2</option>
<option value="0">3</option>
</select>
<button type="button" onclick="move1()">>></button>
<button type="button" onclick="move2()"><<</button>
<select id="select2"></select>
You can do it like this:
Fetch both selects with getElementById() and store them in select_1 and select_2 variables.
Check if selected option exists by compering selectedIndex property of the select with -1. It will be equal to -1 only if the user didn't select anything.
If selectedIndex is equal to -1, then do nothing because there is nothing to transfer to second select.
If selectedIndex is different from -1, it means that user selected something and option with that index should be transfered.
Create new option for second select with document.createElement('option') and copy the selected option values to the new option.
Remove the selected option of current select with select.options.remove(select.selectedIndex)
function move1() {
const select_1 = document.getElementById("select1");
const select_2 = document.getElementById("select2");
if(select_1.selectedIndex !== -1) {
const selected_option = select_1.options[select_1.selectedIndex];
let new_option = document.createElement('option');
new_option.value = selected_option.value
new_option.innerHTML = selected_option.innerHTML;
select_2.appendChild(new_option);
select_1.options.remove(select_1.selectedIndex)
}
}
function move2(){
const select_1 = document.getElementById("select1");
const select_2 = document.getElementById("select2");
if(select_2.selectedIndex !== -1) {
const selected_option = select_2.options[select_2.selectedIndex];
let new_option = document.createElement('option');
new_option.value = selected_option.value
new_option.innerHTML = selected_option.innerHTML;
select_1.appendChild(new_option);
select_2.options.remove(select_2.selectedIndex)
}
}
<select id="select1">
<option value="0">1</option>
<option value="0">2</option>
<option value="0">3</option>
</select>
<button type="button" onclick="move1()">>></button>
<button type="button" onclick="move2()"><<</button>
<select id="select2"></select>
Here's a version that can manage more than 2 sets of <select> and <button> pairs. If there are more than 2 pairs, all recieving <select> will add the selcted <option>. In the HTML a data-* attribute is added to each <option>:
<option data-idx="0" value='1'>1</option>
<option data-idx='1' value="2">2</option>
<option data-idx='2' value="3">3</option>
The value of data-idx is the <option>s original index position. In the event handler switchOPt(e) the data-idx value will be used to determine what index it should be placed in:
to.add(copy, +copy.dataset.idx);
All details are commented in the example.
/*
Collect all buttons into a HTMLCollection then convert it
into an Array. Same with all select
*/
const btnArray = [...document.querySelectorAll('button')];
const selArray = [...document.querySelectorAll('select')];
/*
Iterate through the array of buttons. Register each button
to the click event. The event handler is switchOpt(e)
*/
btnArray.forEach(btn => btn.addEventListener('click', switchOpt));
// Event handler always passes the Event Object
function switchOpt(e) {
/*
Determine the select that will send it's option by
matching it's #id vs this.name (the [name] of the button
the user clicked
*/
const from = document.getElementById(this.name);
// if the select doesn't have any options end function
if (from.childElementCount < 1) return;
/*
Determine the select that adds an option by .filter()
under the condition that it is NOT >from<
*/
let to = selArray.filter(sel => sel.id != from.id);
// Rereference >to< to be the select with the array
to = to[0];
// Determine which option has been selected
const opt = from.options[from.selectedIndex];
// Make a copy of the selected option
const copy = opt.cloneNode(true);
/*
Add >copy< to >to< the second parameter is the index
of the element that >copy< will be placed before it so
it'll always be in order
*/
to.add(copy, +copy.dataset.idx);
// Remove the option from >from<
opt.remove();
}
<!-- Assign each option a data attribute wuth the value
of it's index -->
<select id="A">
<option data-idx='0' value="1">1</option>
<option data-idx='1' value="2">2</option>
<option data-idx='2' value="3">3</option>
</select>
<button name='A' type="button">>></button>
<button name='B' type="button"><<</button>
<select id="B"></select>

Two Drop Down Menus

I wish I could use jQuery, but this has to in JavaScript. I'd appreciate the help.
When "empty" (first drop down menu) is selected, I need all values from the second drop down menu (a, b, c).
When "1" is selected, I need just a, b.
When "2" is selected, I need just b, c.
Nothing's wrong with the drop down menu. Just had to change the values. How would I fix this in JavaScript?
First menu
<onchange="first(this);>
<option value="empty"></option>
<option value="1">1</option>
<option value="2">2</option>
Second menu
<id="second">
<option value="a">a</option>
<option value="b">b</option>
<option value="c">c</option>
One solution that I would prefer is to set the style to none via CSS in JS. This way, the element still exists but it is just hidden from the viewer.
You can the get value of an element via [element-here].value and compare the to some value that you want. From there, you would select the second drop down option value you have and run [element-here].style.display = "none"
Another way that is more complicated that I would not recommend is to create and destroy elements entirely. Something like:
var x = document.createElement("option");
x.value = VALUE HERE;
parent.appendChild(document.createTextNode("TEXT HERE"))
This is a bit sloppy, but here's one way to do it. You have an array of data with the valid secondary values per primary value. Then you render them each time the primary list changes.
let dropdownlist = [];
dropdownlist[1] = ['a', 'b', 'c'];
dropdownlist[2] = ['b', 'c'];
let select = document.createElement('select');
document.getElementById('myItemList').appendChild(select);
let x = 1;
document.getElementById('firstddl').addEventListener('change', (e) => {
//console.log(e.target.value);
x = e.target.value;
select.innerHTML = '';
renderDDL(dropdownlist, x)
});
function renderDDL(dropdownlist, x) {
dropdownlist[x].forEach(function(item) {
let option = document.createElement('option');
select.appendChild(option);
option.innerHTML += item;
});
}
renderDDL(dropdownlist, x); // Runs once
<select id="firstddl">
<option value=1>1</option>
<option value=2>2</option>
</select>
<div id="myItemList">
</div>

querySelector on <select> tag with multiple attributes

I want to get an array of values which is selected by <select> tag with multiple attributes by using querySelector, NOT jQuery.
I googled on that, but Google lists only about selecting multiple elements by using querySelector. In my case, I want one select element with multiple attributes.
I gave up searching on that, I just tried to code with this <select> tag.
<select id="bgmSources" className="bgmSelector" multiple="multiple">
<option value="1">test1</option>
<option value="2">test2</option>
<option value="3">test3</option>
</select>
In JavaScript, I tried as below, but nothing worked.
const values = document.querySelector("#bgmSources").value;
console.log(values);
const selectedOptions = document.querySelectorAll("#bgmSources option:selected");
selectedOptions.forEach(option => console.log(option));
Is there a way to get the array of selected options in <select> tag with multiple attributes?
You can select the children options which are :checked, and then get each of those selected options' values:
bgmSources.onchange = () => {
const selectedOptionVals = Array.from(
bgmSources.querySelectorAll(':scope > option:checked'),
({ value }) => value
);
console.log(selectedOptionVals);
};
<select id="bgmSources" className="bgmSelector" multiple="multiple">
<option value="1">test1</option>
<option value="2">test2</option>
<option value="3">test3</option>
</select>
You can't use .value to get a meaningful result for multiple, unfortunately, and you have to use :checked, not :selected.

jquery change() event acting strange

So I am making two dropdowns. One is dependent on the other one, as in when you choose an option in the first dropdown, the values in the other should change. I am rendering my form with Symfony3.4, so I do not have much control over it. (I do not think I can add dynamic class names/value names to it). If it is relevant, I am using Bulma css framework.
Here is what my selectboxes look like:
states:
<select name="state" id="stateSelect">
<option value="1">Lagos</option>
<option value="2">Abuja</option>
<option value="3">Rivers</option>
<option value="4">Ogun</option>
<option value="5">Oyo</option>
<option value="6">Anambra</option>
<option value="7">Enugu</option>
<option value="8">Akwa Ibom</option>
<option value="9">Adamawa</option>
...
<option value="37">Zamfara</option>
</select>
LGA (local government areas):
<select id="lgaSelect" name="areas_registration[lga]">
<optgroup label="Lagos">
<option value="1">Abule Egba</option>
<option value="2">Agege</option>
<option value="3">Ajah</option>
<option value="4">Alimosho</option>
<option value="5">Amuwo Odofin</option>
...
</optgroup>
<optgroup label="Abuja">
<option value="38">Apo</option>
<option value="39">Asokoro</option>
<option value="40">Central Area</option>
<option value="41">Chika</option>
<option value="42">Dakibiyu</option>
...
</optgroup>
....35 more optgroups
</select>
My goal is when a user chooses an option from States dropdown, the LGA dropdown should only have options relevant to the selected state. I am using optgroup label for this. What I tried in my javascript is that when the page loads, I clone the LGA dropdown, hide it, call it lgaSelectSeed and use it for seeding the original LGA dropdown: (#hiddenLgas is just an empty div)
$(function () {
var stateSelect = $("#stateSelect") || null;
var lgaSelect = $("#lgaSelect") || null;
var hiddenLga = $("#hiddenLgas") || null;
$(hiddenLga).html(lgaSelect.clone().prop('id', 'lgaSelectSeed'));
stateSelect.change(function () {
var select_class = $(this).find('option:selected').text();
var options = $(lgaSelectSeed).find('optgroup[label="' + select_class + '"]');
$(lgaSelect).html(options.children());
}
This works but there is a bug in it. If you select random options in the state dropdown, and then go up and select Lagos or Abuja, the LGA dropdown becomes blank. I have been trying to figure out for a few days why this is happening, but still cant. Is there any jquery plugin to handle this instead?
DEMO: https://jsfiddle.net/gafyvbL9/
How to replicate the bug: In the states dropdown (left), choose Lagos. Then choose Anambra. Then choose Lagos again, then choose Anambra. You can see that the LGA dropdown (right) becomes empty. Why is this happening? Thanks in advance
Make a clone of the stored options so you don't remove the originals from $(hiddenLga)
Change:
$(lgaSelect).html(options.children());
To
$(lgaSelect).html(options.children().clone());
Playing with your fiddle, I think the issue is when you perform $(lgaSelect).html(), you're deleting all of the information stored there. Try storing that outside of your handler.
let $options = $('#lgaSelect').clone();
$("#stateSelect").on('change',function() {
let state = $(this).find('option:selected').text();
$("#lgaSelect").html($options.find(`optgroup[label="${state}"]`).html());
});
EDIT:
Notice the double quote in optgroup[label="${state}"]. This prevents issue with states that contain a space, like Akwa Ibom.

Categories