Simplify setOptionByValue(selectElement, value) function for <select> element. How? - javascript

When data is received from a JSON api, one of the data properties is used to set the selected option value of a select html element.
The following code sets the option to be selected based on the select html element and the corresponding select option value passed in.
Is there a shorter version of this nowadays?
Take a look:
// Custom function to set select option by value
const setOptionByValue = (selectElement, value) => {
let options = selectElement.getElementsByTagName('option');
for (let i = 0, optionsLength = options.length; i < optionsLength; i++) {
// console.log(options[i].value);
if (options[i].value == value) {
selectElement.selectedIndex = i;
return true;
}
}
return false;
}
// The select element
const selectStatus = document.getElementById('selectStatus');
// Initially set status to Draft.
setOptionByValue(selectStatus, 'Draft');
<label for="selectStatus">Status:</label>
<select id="selectStatus">
<option value="0" selected>Select...</option>
<option value="Draft">Draft</option>
<option value="Pending">Pending</option>
<option value="Complete">Complete</option>
</select>

Just assign the value to the select element's value
// Custom function to set select option by value
const setOptionByValue = (selectElement, value) => {
selectElement.value = value;
}
// The select element
const selectStatus = document.getElementById('selectStatus');
// Initially set status to Draft.
setOptionByValue(selectStatus, 'Draft');
<label for="selectStatus">Status:</label>
<select id="selectStatus">
<option value="0" selected>Select...</option>
<option value="Draft">Draft</option>
<option value="Pending">Pending</option>
<option value="Complete">Complete</option>
</select>
Should the value of the option differ from its content and you wish to select by content:
// Custom function to set select option by value
const setOptionByValue = (selectElement, value) => {
selectElement.querySelectorAll('option').forEach((e) => {
if (e.innerHTML == value) {
selectElement.value = e.value;
}
})
}
// The select element
const selectStatus = document.getElementById('selectStatus');
// Initially set status to Draft.
setOptionByValue(selectStatus, 'Draft');
<label for="selectStatus">Status:</label>
<select id="selectStatus">
<option value="0" selected>Select...</option>
<option value="Draft">Draft</option>
<option value="Pending">Pending</option>
<option value="Complete">Complete</option>
</select>

Related

Trigger change event on individual option selected in `select` with `multiple` attribute

Given a standard select as per below, is is possible by jQuery (or otherwise) to trigger a change event on individual options - As multiple may be selected, I don't want to trigger on the select but on the option?
<select multiple="">
<option>Test 2</option>
<option>Testing 3</option>
</select>
Something like:
$(`option`).trigger(`change`);
One way is to keep a reference of the last state of the multi-select, then compare to find the most recently clicked option.
let sels = [];
$(`select`).on(`change`, function() {
$(this).val().forEach(v => {
if (sels.indexOf(v) === -1) {
console.log(`The option most recently selected is ${v}`);
}
})
sels = $(this).val();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select multiple="">
<option value='2'>Test 2</option>
<option value='3'>Testing 3</option>
</select>
There is no change on an option. If you want it to act like a user is selecting option by option in the select, you would need to select the option and trigger the change event on the select.
const mySelect = document.querySelector("#mySelect");
mySelect.addEventListener("change", function () {
const selected = Array.from(mySelect.querySelectorAll("option:checked")).map(x => x.value);
console.log(selected);
});
function triggerEvent(elem, event){
const evt = document.createEvent("HTMLEvents");
evt.initEvent(event, true, true );
elem.dispatchEvent(evt);
}
const values = ['1','2','4'];
values.forEach(val => {
const opt = mySelect.querySelector(`[value="${val}"]`);
if (opt) {
opt.selected = true;
triggerEvent(mySelect, 'change');
}
});
/*
const values = ['1','2','4'];
const options = mySelect.querySelectorAll("option");
options.forEach(opt => {
const initial = opt.selected;
opt.selected = values.includes(opt.value);
if (opt.selected !== initial) {
triggerEvent(mySelect, 'change');
}
});
*/
<select id="mySelect" multiple>
<option value="1">one</option>
<option value="2">two</option>
<option value="3">three</option>
<option value="4">four</option>
</select>
The real solution is why is your page not set up to handle default values? Seems like you should just be able to call a method with the values and be done. Seems odd to rely on events.

Update selected value in dropdown list depending on other lists selected values

i'm trying to figure out how can i make dynamic selection of values in few dropdown lists using Vanilla JS. I have 3 dropdown lists with same values. Each of them should have different value selected at the same time (duplicates not allowed). So once user changes another value in one list, value of another list should be updated accordingly. Basically, i need to swap their numbers in such case. But i can't complete the task as I'm quite new to JS and overall in programming. I tried following:
// defining initial values
var startPoint = document.querySelectorAll("select");
// function to recalculate value of each list on change
function calculateNewValues() {
var changes = new Array();
// getting updated values from lists as an array
var updatedValues = document.querySelectorAll("select");
// looping through array
for (let i = 0; i < updatedValues.length; i++) {
// if in updated array value of current index isn't equal to value
// of index in initial array
if (updatedValues[i].value != startPoint[i].value) {
// creating variable changes for tracking
changes[i] = updatedValues[i].value;
}
// if var changes has been defined (i.e. value of any list was updated)
if (changes.length > 0) {
// finding index of initial array with same value
var key = startPoint.findIndex(changes[i]);
// setting value of found index to previously stored value in updated list
updatedValues[key].value = startPoint[i].value;
}
}
updatedValues.forEach(element => {
console.log(element.value);
});
}
// event listeners for change of every dropdown list
const lists = document.querySelectorAll("select");
for (k = 0; k < lists.length; k++) {
lists[k].addEventListener("change", calculateNewValues, false);
}
<p>
<select name="list1" id="list1">
<option value="1" id="list1option1" selected>1</option>
<option value="2" id="list1option2">2</option>
<option value="3" id="list1option3">3</option>
</select>
</p>
<p>
<select name="list2" id="list2">
<option value="1" id="list2option1">1</option>
<option value="2" id="list2option2" selected>2</option>
<option value="3" id="list2option3">3</option>
</select>
</p>
<p>
<select name="list3" id="list3">
<option value="1" id="list3option1">1</option>
<option value="2" id="list3option2">2</option>
<option value="3" id="list3option3" selected>3</option>
</select>
</p>
i am probably doing some mistake when it comes to if (changes.length > 0) {
but i can't understand how i can make this part better.
Thanks a lot in advance.
You have assigned startPoint to querySelectorAll. Which will hold exact same objects whenever you write same querySelectorAll. So in your case startPoint and updatedValues will always same.
I have definen startPoint as an array of values and set its values in setStartingPointValues().
Added target as current list which is being updated.
Updated if (target.id == updatedValues[i].id && updatedValues[i].value != startPoint[i]) and retrieved target's old value.
var key = startPoint.findIndex(x => x == target.value); will find select with value similar to new value. If we found key != -1, then update that select with oldValue.
Call setStartingPointValues() at end of change event.
// defining initial values
var startPoint = [];
// store values for all select.
function setStartingPointValues() {
startPoint = [];
document.querySelectorAll("select").forEach(x => startPoint.push(x.value));
}
setStartingPointValues();
// function to recalculate value of each list on change
function calculateNewValues() {
let target = this;
let oldValue = 0;
// getting updated values from lists as an array
var updatedValues = document.querySelectorAll("select");
// looping through array
for (let i = 0; i < updatedValues.length; i++) {
// if in updated array value of current index isn't equal to value
// of index in initial array
if (target.id == updatedValues[i].id && updatedValues[i].value != startPoint[i]) {
// creating variable changes for tracking
// changes[i] = updatedValues[i].value;
oldValue = startPoint[i];
}
// if var changes has been defined (i.e. value of any list was updated)
// finding index of initial array with same value
var key = startPoint.findIndex(x => x == target.value);
// setting value of found index to previously stored value in updated list
if (key !== -1)
updatedValues[key].value = oldValue;
}
setStartingPointValues();
}
// event listeners for change of every dropdown list
const lists = document.querySelectorAll("select");
for (k = 0; k < lists.length; k++) {
lists[k].addEventListener("change", calculateNewValues, false);
}
<p>
<select name="list1" id="list1">
<option value="1" id="list1option1" selected>1</option>
<option value="2" id="list1option2">2</option>
<option value="3" id="list1option3">3</option>
</select>
</p>
<p>
<select name="list2" id="list2">
<option value="1" id="list2option1">1</option>
<option value="2" id="list2option2" selected>2</option>
<option value="3" id="list2option3">3</option>
</select>
</p>
<p>
<select name="list3" id="list3">
<option value="1" id="list3option1">1</option>
<option value="2" id="list3option2">2</option>
<option value="3" id="list3option3" selected>3</option>
</select>
</p>
Define good structure (store selected value somewhere) and rest should be easy
const lists = Array.from(document.querySelectorAll("#list")).map(element => {
const options = Array.from(element.children);
const selected = options.findIndex(child => child.selected) + 1;
return {
element,
options,
selected
}
});
const calculateNewValues = ({target}) => {
const value = Number(target.value);
const currentList = lists.find(pr => pr.element === target);
const list = lists.find(pr => pr.selected === value)
const oldValue = currentList.selected;
currentList.selected = value;
list.options[value - 1].selected = false;
list.options[oldValue - 1].selected = true;
list.selected = oldValue;
}
for(let {element} of lists) {
element.addEventListener("change", calculateNewValues, false);
}
<p>
<select name="list1" id="list">
<option value="1" selected>1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</p>
<p>
<select name="list2" id="list">
<option value="1">1</option>
<option value="2" selected>2</option>
<option value="3">3</option>
</select>
</p>
<p>
<select name="list3" id="list">
<option value="1">1</option>
<option value="2">2</option>
<option value="3" selected>3</option>
</select>
</p>

Slicing elements from three select menus into and out of array

var select1 = document.getElementById('select1');
var select2 = document.getElementById('select2');
var array = [];
let sel1 = false;
function myFunct1() {
var one = select1.options[select1.selectedIndex].value;
if(array.length === 1 && !sel1) array.unshift(one);
else array.splice(0,1,one);
console.log(array);
sel1 = true;
}
function myFunct2() {
var two = select2.options[select2.selectedIndex].value;
array.splice(sel1, 1, two);
console.log(array);
}
function myFunct3() {
var three = select3.options[select3.selectedIndex].value;
}
<select id = 'select1' onchange = 'myFunct1()'>
<option disabled selected value> -- select an option -- </option>
<option value = 'Dog1'>Dog</option>
<option value = 'Cat1'>Cat</option>
<option value = 'Bear1'>Bear</option>
</select>
<select id = 'select2' onchange = 'myFunct2()'>
<option disabled selected value> -- select an option -- </option>
<option value = 'Dog2'>Dog</option>
<option value = 'Cat2'>Cat</option>
<option value = 'Bear2'>Bear</option>
</select>
<select id = 'select3' onchange = 'myFunct3()'>
<option disabled selected value> -- select an option -- </option>
<option value = 'Dog3'>Dog</option>
<option value = 'Cat3'>Cat</option>
<option value = 'Bear3'>Bear</option>
</select>
I have this method that works exactly how I want it with two select menus. So if you select twice in a row from the second select the array's length is never more than one until you select from the first. Now I want to incorporate a third select menu. Please help me make this work. I'm aware I could combine them all into one function and not have to deal with these issues but for my use, I can't do that. The main condition is that there is never multiple selections within the array from the same select menu and never any empty positions within the array that still count towards its length. so an array of [undefined, Cat2] does not occur.
The simple way is:
Create two arrays i.e realArr(to keep strings at original indexes). For example value from select1 will always we set to realArr[0] and from select2 to realArr[1]...
Second array showArr is array from which you will remove undefined using filter()
var select1 = document.getElementById('select1');
var select2 = document.getElementById('select2');
var select3= document.getElementById('select3');
var realArr = [];
var showArr = [];
function myFunct1() {
var one = select1.options[select1.selectedIndex].value;
realArr[0] = one;
showArr = realArr.filter(x => x !== undefined);
console.log(showArr);
}
function myFunct2() {
var two = select2.options[select2.selectedIndex].value;
realArr[1] = two
showArr = realArr.filter(x => x !== undefined);
console.log(showArr);
}
function myFunct3() {
var three = select3.options[select3.selectedIndex].value;
realArr[2] = three;
showArr = realArr.filter(x => x !== undefined);
console.log(showArr);
}
<select id = 'select1' onchange = 'myFunct1()'>
<option disabled selected value> -- select an option -- </option>
<option value = 'Dog1'>Dog</option>
<option value = 'Cat1'>Cat</option>
<option value = 'Bear1'>Bear</option>
</select>
<select id = 'select2' onchange = 'myFunct2()'>
<option disabled selected value> -- select an option -- </option>
<option value = 'Dog2'>Dog</option>
<option value = 'Cat2'>Cat</option>
<option value = 'Bear2'>Bear</option>
</select>
<select id = 'select3' onchange = 'myFunct3()'>
<option disabled selected value> -- select an option -- </option>
<option value = 'Dog3'>Dog</option>
<option value = 'Cat3'>Cat</option>
<option value = 'Bear3'>Bear</option>
</select>

Remove the duplicate values and combinations in html select option

I have a dynamically generated <select> field with <option>.
<select>
<option value=""></option>
<option value=""></option>
<option value=""> False</option>
<option value=""> True</option>
<option value="">False False</option>
<option value="">False True</option>
<option value="">True</option>
<option value="">True True</option>
</select>
I would like to remove the duplicate occurrences and combinations. The final <select> field with <option> should look like :
<select>
<option value=""></option>
<option value="">False</option>
<option value="">True</option>
</select>
Here is how my fiddle looks like. Been trying to solve this for hours.
var values = [];
$("select").children().each(function() {
if (values.length > 0) {
var notExists = false;
for (var x = 0; x < values.length; x++) {
var _text = this.text.replace(/\s/g, "");
var value = values[x].replace(/\s/g, "");
if (values[x].length > _text.length) {
//console.log('>>+', value, ' || ', _text, value.indexOf(_text))
notExists = value.indexOf(_text) > -1 ? true : false;
} else {
//console.log('>>*', value, ' || ', _text, _text.indexOf(value))
notExists = _text.indexOf(value) > -1 ? true : false;
}
}
if (notExists) {
//this.remove();
values.push(this.text);
}
} else {
values.push(this.text);
}
});
Any help to solve this is appreciated.
You can use map() to return all options text and use split() on white-space. Then to remove duplicates you can use reduce() to return object. Then you can empty select and use Object.keys() to loop each property and append to select.
var opt = $("select option").map(function() {
return $(this).text().split(' ')
}).get();
opt = opt.reduce(function(o, e) {return o[e] = true, o}, {});
$('select').empty();
Object.keys(opt).forEach(function(key) {
$('select').append(' <option value="">'+key+'</option>');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select>
<option value=""></option>
<option value=""></option>
<option value="">False</option>
<option value="">True</option>
<option value="">False False</option>
<option value="">False True</option>
<option value="">True</option>
<option value="">True True</option>
</select>
You can loop through each of this children text , then use substring to get the first text & put it in an array.
Once done empty the select element and append the newly created options
var _textHolder=[]; // NA empty array to hold unique text
var _options="";
$("select").children().each(function(item,value) {
var _textVal = $(this).text().trim(); // Remove white space
//get the first text content
var _getText = _textVal.substr(0, _textVal.indexOf(" "));
// if this text is not present in array then push it
if(_textHolder.indexOf(_getText) ==-1){
_textHolder.push(_getText)
}
});
// Create new options with items from _textHolder
_textHolder.forEach(function(item){
_options+='<option value="">'+item+'</option>'
})
// Empty current select element and append new options
$('select').empty().append(_options);
JSFIDDLE
I would do with pure JS ES6 style. This is producing a words array from the whitespace separated options element's innerText value regardless the words are in the front, middle or the end; and it will create a unique options list from that. Basically we are concatenating these arrays and getting it unified by utilizing the new Set object. The code is as follows;
var opts = document.querySelector("select").children,
list = Array.prototype.reduce.call(opts, function(s,c){
text = c.innerText.trim().split(" ");
return new Set([...s].concat(text)) // adding multiple elements to a set
},new Set());
list = [...list]; // turn set to array
for (var i = opts.length-1; i >= 0; i--){ //reverse iteration not to effect indices when an element is deleted
i in list ? opts[i].innerText = list[i]
: opts[i].parentNode.removeChild(opts[i]);
}
<select>
<option value=""></option>
<option value=""></option>
<option value=""> False</option>
<option value=""> True</option>
<option value="">False False</option>
<option value="">False True</option>
<option value="">True</option>
<option value="">True True</option>
</select>

How to synchronize two SELECT elements

I was wondering how to synchronize the values and text of two elements. For instance,
<select id="box1" onchange="sync();">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<select id="box2">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
and then sync(); would look something like....
function sync()
{
box2.selected = box1.selected;
}
Any idea how I would do this?
Thanks,
Matthew
One possible approach:
function sync(el1, el2) {
// if there is no el1 argument we quit here:
if (!el1) {
return false;
}
else {
// caching the value of el1:
var val = el1.value;
// caching a reference to the element with
// with which we should be synchronising values:
var syncWith = document.getElementById(el2);
// caching the <option> elements of that <select>:
var options = syncWith.getElementsByTagName('option');
// iterating over those <option> elements:
for (var i = 0, len = options.length; i < len; i++) {
// if the value of the current <option> is equal
// to the value of the changed <select> element's
// selected value:
if (options[i].value == val) {
// we set the current <option> as
// as selected:
options[i].selected = true;
}
}
}
}
// caching the <select> element whose change event should
// be reacted-to:
var selectToSync = document.getElementById('box1');
// binding the onchange event using an anonymous function:
selectToSync.onchange = function(){
// calling the function:
sync(this,'box2');
};
function sync(el1, el2) {
if (!el1) {
return false;
} else {
var val = el1.value;
var syncWith = document.getElementById(el2);
var options = syncWith.getElementsByTagName('option');
for (var i = 0, len = options.length; i < len; i++) {
if (options[i].value == val) {
options[i].selected = true;
}
}
}
}
var selectToSync = document.getElementById('box1');
selectToSync.onchange = function() {
sync(this, 'box2');
};
<select id="box1">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<select id="box2">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
JS Fiddle demo.
Or, revised and updated somewhat:
function sync() {
// caching the changed element:
let el = this;
// retrieving the id of the element we should synchronise with
// from the changed-element's data-syncwith custom attribute,
// using document.getElementById() to retrieve that that element.
document.getElementById( el.dataset.syncwith )
// retrieving the <options of that element
// and finding the <option> at the same index
// as changed-element's selectedIndex (the index
// of the selected <option> amongst the options
// collection) and setting that <option> element's
// selected property to true:
.options[ el.selectedIndex ].selected = true;
}
// retrieving the element whose changes should be
// synchronised with another element:
var selectToSync = document.getElementById('box1');
// binding the snyc() function as the change event-handler:
selectToSync.addEventListener('change', sync);
function sync() {
let el = this;
document.getElementById(el.dataset.syncwith).options[el.selectedIndex].selected = true;
}
var selectToSync = document.getElementById('box1');
selectToSync.addEventListener('change', sync);
<select id="box1" data-syncwith="box2">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<select id="box2">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
JS Fiddle demo.
Note that this approach does assume – and requires – that the <option> elements are in the same order.
To update the original approach, where the order is irrelevant, using ES6 approaches (and the same data-syncwith custom attribute approach):
function sync() {
// caching the changed element (since
// we're using it twice):
let el = this;
// retrieving the id of the element to synchronise 'to' from
// the 'data-syncwith' custom attribute of the changed element,
// and retrieving its <option> elements. Converting that
// Array-like collection into an Array using Array.from():
Array.from(document.getElementById(el.dataset.syncwith).options)
// Iterating over the array of options using
// Array.prototype.forEach(), and using an Arrow function to
// pass the current <otpion> (as 'opt') setting that current
// <option> element's selected property according to Boolean
// returned by assessing whether the current option's value
// is (exactly) equal to the value of the changed element:
.forEach(opt => opt.selected = opt.value === el.value);
}
var selectToSync = document.getElementById('box1');
selectToSync.addEventListener('change', sync);
function sync() {
let el = this;
Array.from(document.getElementById(el.dataset.syncwith).options).forEach(opt => opt.selected = opt.value === el.value);
}
let selectToSync = document.getElementById('box1');
selectToSync.addEventListener('change', sync);
<select id="box1" data-syncwith="box2">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<select id="box2">
<option value="1">One</option>
<option value="3">Three</option>
<option value="2">Two</option>
</select>
JS Fiddle demo.
If you look at the HTML in the Snippet you'll see that I switched the positions of <option> elements in the second <select> element to demonstrate that the <option> position doesn't matter in this latter approach.
References:
Array.from().
Array.prototype.forEach().
Arrow functions.
document.getElementById().
EventTarget.addEventListener().
for loop.
HTMLElement.dataset.
HTMLSelectElement.
let statement.
var.
In the Actual browsers you dont have to do to much...
<select id="box1" onchange="box2.value=this.value;">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<select id="box2">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
Jsfiddle DEMO
Without jQuery:
for (var i=0; i<document.getElementById('box1').options.length; i++)
if (document.getElementById('box1').options[i].selected)
for (var j=0; j<document.getElementById('box2').options.length; j++)
if (document.getElementById('box1').options[i].value == document.getElementById('box2').options[j].value)
document.getElementById('box2').options[j].selected = true;
With jQuery:
Note: on method requires jQuery > 1.7
jQuery(function($) {
$('#first').on('change', function() {
var sel = $('option:selected', this).val();
$('#second option').filter(function(index, el) {
return el.value == sel;
}).prop('selected', true);
});
});
<select name="first" id="first" autocomplete="off">
<option value="0">-- Select one option --</option>
<option value="1">First</option>
<option value="2">Second</option>
<option value="3">Third</option>
<option value="4">Fourth</option>
<option value="5">Fifth</option>
<option value="6">Sixth</option>
</select>
<select name="second" id="second" autocomplete="off">
<option value="0">-- Select one option --</option>
<option value="1">First</option>
<option value="2">Second</option>
<option value="3">Third</option>
<option value="4">Fourth</option>
<option value="5">Fifth</option>
<option value="6">Sixth</option>
</select>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Categories