I dynamically generate additional forms on a page using Django Model Formsets. The user can generate as many forms as they need. This is done in a Vuejs method.
My issue is changing the options of the second select of the form that the first select value was chosen. I was thinking about trying on focus or on click to get the select that was last changed, but I'm not sure how that would work.
I do track the current count of forms on the page.
In the example below. If the user changed form_select_0 to One, I need form_subselect_0 to only have options C and D, but the selects in form_1 should not be altered.
Example:
<form id = form_0>
<select id="form_select_0">
<option value="one">One</option>
<option value="two">Two</option>
</select>
<select id="form_subselect_0">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
<option value="D">D</option>
</select>
</form>
<form id = form_0>
<select id="form_select_1">
<option value="one">One</option>
<option value="two">Two</option>
</select>
<select id="form_subselect_1">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
<option value="D">D</option>
</select>
</form>
Follow-up:
Added vue method (below) for how I'm currently adding Vuejs models to the Django Model Formsets. Question: Is there a better way?
addForm: function () {
this.count++
let form_count = this.count
form_count++
let formID = 'id_form-' + this.count
incremented_form = this.vue_form.replace(/form-\d/g, 'form-' + this.count)
this.formList.push(incremented_form)
this.$nextTick(() => {
let total_forms = document.getElementsByName('form-TOTAL_FORMS').forEach
(function (ele, idx) {
ele.value = form_count
})
})
},
As stated above this was my final answer due to no response:
addForm: function () {
this.count++
let form_count = this.count
form_count++
let formID = 'id_form-' + this.count
incremented_form = this.vue_form.replace(/form-\d/g, 'form-' + this.count)
this.formList.push(incremented_form)
this.$nextTick(() => {
let total_forms = document.getElementsByName('form-TOTAL_FORMS').forEach
(function (ele, idx) {
ele.value = form_count
})
})},
Related
My JavaScript skills are pretty basic, but I've written this code where you select from the dropdown on the left (Select1) an option which shows only the specified options in the dropdown on the right (Select2), and hides the rest.
I've also put it in a codepen here, in case you want to fiddle.
The code seems to work in both of the above environments in Firefox 90.0.2, but it fails in both and writes nothing to the console in Chrome 92.0.4515.131.
Any ideas why it's working in Firefox but not Chrome (and others) and what I can do so it works on all major browsers.
I'm running Windows 10 and I'd like to avoid iQuery if practical, as I don't want to get into learning or using that yet, as I'm starting with the basics.
Thanks.
Here's the code:
<!DOCTYPE html>
<html>
<head>
<script>
function hide_options(select_id,type)
{
//alert("hide_options(select_id="+select_id+", type="+type+")");
console.log("hide_options(select_id="+select_id+", type="+type+")");
var x = document.getElementById(select_id);
for (i=1; i<x.options.length; i++)
{
x.options[i].style.display = "none";
}
x.options[0].selected = true;
if (type == 'A')
{ unhide_options(select_id,"one","two") }
if (type == 'B')
{ unhide_options(select_id,"two","three") }
if (type == 'C')
{ unhide_options(select_id,"two") }
}
function unhide_options(select_id,...opts)
{
//alert("unhide_options(select_id="+select_id+"opts="+opts+")");
console.log("unhide_options(select_id="+select_id+"opts="+opts+")");
for (i=0; i<opts.length; i++)
{
document.getElementById(select_id+"_"+opts[i]).style.display = "";
}
}
</script>
</head>
<body>
<p>Selecting an option in the "Select1" dropdown should show only those options in the "Select2" dropdown.</p>
<select name=select1>
<option>Select1...</option>
<option onclick="hide_options('field1','A')">Show options 1 + 2 only</option>
<option onclick="hide_options('field1','B')">Show options 2 + 3 only</option>
<option onclick="hide_options('field1','C')">Show option 2 only</option>
</select>
<select name=update_action id=field1>
<option value=''>Select2...</option>
<option value=one id=field1_one>One</option>
<option value=two id=field1_two>Two</option>
<option value=three id=field1_three>Three</option>
</select>
</body>
</html>
Perhaps Chrome doesn't emit an event on clicking an option - however, you can use the change event on the select
I've used data-* attributes for the options, and I also show/hide in the one loop
the showing object is just a nice easy way to configure what should show, so you don't need if/else if/else if etc - I find this easier to maintain then countless if else if`'s
const selects = document.querySelectorAll('.filterer');
const showing = {
A: ["one", "two"],
B: ["two", "three"],
C: ["two"]
};
selects.forEach(select => {
select.addEventListener('change', function(e) {
const {
target,
value
} = this[this.selectedIndex].dataset;
const show = showing[value] || [];
const x = document.getElementById(target) || [];
[...x]
.slice(1)
.forEach(option =>
option.style.display = (show.length === 0 || show.includes(option.value)) ? '' : 'none'
);
if (x && x[0]) {
x[0].selected = true;
}
});
});
<p>Selecting an option in the "Select1" dropdown should show only those options in the "Select2" dropdown.</p>
<select id="select1" name="select1" class="filterer">
<option>Select1...</option>
<option data-target='field1' data-value='A'>Show options 1 + 2 only</option>
<option data-target='field1' data-value='B'>Show options 2 + 3 only</option>
<option data-target='field1' data-value='C'>Show option 2 only</option>
</select>
<select name=update_action id=field1>
<option value=''>Select2...</option>
<option value=one id=field1_one>One</option>
<option value=two id=field1_two>Two</option>
<option value=three id=field1_three>Three</option>
</select>
<br/>
<hr/>
<select id="select2" name="select2" class="filterer">
<option>Select1...</option>
<option data-target='field2' data-value='A'>Show options 1 + 2 only</option>
<option data-target='field2' data-value='B'>Show options 2 + 3 only</option>
<option data-target='field2' data-value='C'>Show option 2 only</option>
</select>
<select name=update_action id=field2>
<option value=''>Select2...</option>
<option value=one id=field2_one>One</option>
<option value=two id=field2_two>Two</option>
<option value=three id=field2_three>Three</option>
</select>
Alternative. You mention in a comment that the Selects and Options are created dynamically on the server code
The following would allow you to do that, and to specify the visible selections given the current choice, all in the option data-values attribute
Nothing needs hard coding in javascript this way
const selects = document.querySelectorAll('.filterer');
selects.forEach(select => {
select.addEventListener('change', function(e) {
const { target, values } = this[this.selectedIndex].dataset;
const show = values.split(',').map(s => s.trim());
const x = document.getElementById(target) || [];
[...x]
.slice(1)
.forEach(option =>
option.style.display = (show.length === 0 || show.includes(option.value)) ? '' : 'none'
);
if (x && x[0]) {
x[0].selected = true;
}
});
});
<p>Selecting an option in the "Select1" dropdown should show only those options in the "Select2" dropdown.</p>
<select id="select1" name="select1" class="filterer">
<option>Select1...</option>
<option data-target='field1' data-values='one, two'>Show options 1 + 2 only</option>
<option data-target='field1' data-values='two, three'>Show options 2 + 3 only</option>
<option data-target='field1' data-values='two'>Show option 2 only</option>
</select>
<select name='update_action' id='field1'>
<option value=''>Select2...</option>
<option value='one' id='field1_one'>One</option>
<option value='two' id='field1_two'>Two</option>
<option value='three' id='field1_three'>Three</option>
</select>
<br/>
<hr/>
<br/>
<select id="select2" name="select2" class="filterer">
<option>Select1...</option>
<option data-target='field2' data-values='apple,banana'>Apple and banana</option>
<option data-target='field2' data-values='banana,pineapple'>Banana and pineapple</option>
<option data-target='field2' data-values='pineapple'>Pineapple</option>
</select>
<select name='update_action' id='field2'>
<option value=''>Select2...</option>
<option value='pineapple' id='field2_one'>Pineapple</option>
<option value='banana' id='field2_two'>Banana</option>
<option value='apple' id='field2_three'>Apple</option>
</select>
Sure, this may require changing server code, but your original code did have hard coded logic for A B and C - this code, the server code can emit anything it likes and the javascript doesn't need to be changed
I have a select with options and values:
<select id="sid">
<option value="sValue1">sText1</option>
...
</select>
I need to create an associated array for all pairs:
var data = {"sText1":"sValue1",...};
Is there ready/simplifies tools to do that?
It's a basic for ... on ... iteration.
Take note that if there are multiple <option> with the same label, the last one will take place in the result object.
const select = document.getElementById('sid');
const objContainer = document.getElementById('sid-obj');
const options = select.getElementsByTagName('option');
const selectObj = {};
for (const opt of options) {
let optObj = {};
optObj[opt.textContent] = opt.value;
Object.assign(selectObj, optObj);
}
// print result in code
objContainer.textContent = JSON.stringify(selectObj);
<select id="sid">
<option value="">Default</option>
<option value="sValue1">sText1</option>
<option value="sValue2">sText2</option>
<option value="sValue3">sText3</option>
<option value="sValue4">sText4</option>
<option value="sValue5">sText4</option>
</select>
<h2>Result:</h2>
<code id="sid-obj" />
Add a function to the select which will be called on change and on change get the value and the text from the selected option
function getValue(elem) {
let obj = {};
obj[elem.options[elem.selectedIndex].text] = elem.value
console.log(obj)
}
<select id="sid" onchange='getValue(this)'>
<option value="sValue1">sText1</option>
<option value="sValue2">sText2</option>
<option value="sValue3">sText3</option>
</select>
Sorry for being late to the party but here is a jQuery solution for the same.
var arr = {};
$("#sid option").each(function() {
let text = $(this).text();
arr[text] = $(this).val();
});
// print result
$('#sid-obj').append(JSON.stringify(arr));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select id="sid">
<option value="">Default</option>
<option value="sValue1">sText1</option>
<option value="sValue2">sText2</option>
<option value="sValue3">sText3</option>
<option value="sValue4">sText4</option>
<option value="sValue5">sText4</option>
</select>
<h2>Result:</h2>
<code id="sid-obj" />
I'm trying to show/hide the cart button depending on 2 separate select field values.
My logic is this:
Display the cart button if selectId #pa_custom-engraving = 'no' OR if selectId #pa-color != 'custom-print'. Otherwise I want to hide the cart.
This is what I have so far which works unless you continue to toggle the select fields they cancel each other out. How can I combine this into proper 'OR' conditional statement?
JS
document.getElementById('pa_custom-engraving').addEventListener('change', function () {
var style = this.value == 'no' ? 'block' : 'none';
document.getElementsByClassName('woocommerce-variation-add-to-cart')[0].style.display = style;
});
document.getElementById('pa_color').addEventListener('change', function () {
var pstyle = this.value !== 'custom-print' ? 'block' : 'none';
document.getElementsByClassName('woocommerce-variation-add-to-cart')[0].style.display = pstyle;
});
HTML
<select id="pa_custom-engraving">
<option value="">Choose an option</option>
<option value="no">No</option>
<option value="yes">Yes</option>
</select>
<select id="pa_color">
<option value="">Choose an option</option>
<option value="black">Black</option>
<option value="white">White</option>
<option value="custom-print">Custom Print</option>
</select>
Add the same listener to both selects. On change, look at both select values, and apply the conditions separated by ||:
const cartButton = document.querySelector('.woocommerce-variation-add-to-cart');
const selects = ['#pa_custom-engraving', '#pa_color'].map(c => document.querySelector(c));
for (const select of selects) {
select.addEventListener('change', () => {
const displayStyle= selects[0].value === 'no' || selects[1].value !== 'custom-print'
? 'block'
: 'none';
cartButton.style.display = displayStyle;
});
}
You can take the all selected value in an array and check if the array includes/not include the value you want to match:
var sel = Array.from(document.querySelectorAll('#pa_custom-engraving, #pa_color'));
sel.forEach(function(el){
el.addEventListener('change', function () {
var val = sel.map(i => i.value);
var style = val.includes('no') || !val.includes('custom-print') ? 'block' : 'none';
document.getElementsByClassName('woocommerce-variation-add-to-cart')[0].style.display = style;
});
});
<select id="pa_custom-engraving">
<option value="">Choose an option</option>
<option value="no">No</option>
<option value="yes">Yes</option>
</select>
<select id="pa_color">
<option value="">Choose an option</option>
<option value="black">Black</option>
<option value="white">White</option>
<option value="custom-print">Custom Print</option>
</select>
<button class="woocommerce-variation-add-to-cart">woocommerce-variation-add-to-cart</button>
I'm trying to get the value of the option which have the attribute "selected" to compare it to the current option selected.
function onChangeOption(opt) {
var update_value = opt.value;
var selectedValue = '???'; // get selected attribute
if (update_value != selectedValue) {
// Do some things
}
}
<select class="form-control" onchange="onChangeOption(this)">
<!-- I wanna got the content of option selected=selected-->
<option selected="selected" value="1">1</option>
<option value="2">2</option>
</select>
// save initial selected value to a variable
var initSelected = $('.form-control option:selected').val();
$('select').on('change', function() {
// check if the selected value is the same as the initial one was
if(this.value == initSelected) {
console.log('same values');
} else {
console.log('not same values');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select class="form-control">
<option selected="selected" value="1">1</option>
<option value="2">2</option>
</select>
Just add change event listener.And get the selected value.You can achieve comparision between selected value and changed value by maintaining an array.Like below.
values = []//creates an array
select = document.querySelector('#myselect');
values.unshift(select.value);
//console.log(values);
select.addEventListener('change',function(){
update_value = this.value;
console.log(this.value);
if (update_value != values[0]) {
// alert('Not matched');
console.log('Not matched');
}
else{
//alert('Matched');
console.log('Matched')
}
});
<select class="form-control" id="myselect">
<option selected="selected" value="1"> 1 </option>
<option value="2"> 2 </option>
</select>
I think alexis actually wants something more like this:
function onChangeOption(opt) {
var update_value = opt.value;
var options = document.getElementsByTagName("option");
if (options[0].getAttribute("selected")=="selected") {
var selectedValue = options[0].value;
} else {
var selectedValue = options[1].value;
}
if (update_value != selectedValue) {
// If the selected option's value is not equal to the value of the option with the attribute "selected", then do... (this way, you can change the attribute to any of the options!)
console.log(selectedValue);
}
}
<select class="form-control" onchange="onChangeOption(this)">
<option selected="selected" value="1">1</option>
<option value="2">2</option>
</select>
Comment the result and if you need anything else. Glad to help.
You can always store previously selected values, if you want to access them somehow later on: working example.
HTML:
<select id="mySelect" class="form-control" onchange="onChangeOption(this)">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
<p>Previous: <span id="prev"></span></p>
<p>Current: <span id="curr"></span></p>
JS:
var selectElem = document.getElementById("mySelect");
var prev = document.getElementById("prev");
var curr = document.getElementById("curr");
var allEverSelected = [ selectElem.value ];
selectElem.addEventListener("change", function(evt){
allEverSelected.push( this.value );
prev.innerHTML = allEverSelected[allEverSelected.length - 2];
curr.innerHTML = allEverSelected[allEverSelected.length - 1];
});
To access default value, just get the <select> value after DOM loads.
selected attribute on <option> tag exist only to make other than first <option> element inside <select> default option, i.e.:
<select>
<option value="1">1</option>
<option selected value="2">2</option>
</select>
Above select's default value is 2.
I think this is the one what you want. Try it.
function onChangeOption(opt) {
var update_value = opt.value;
console.log(update_value);
var selectedValue;// = '???'; // get selected attribute
// I think this is the one you want
//If you want to select the HTML element,
selectedValue=document.querySelector("option[value='"+update_value+"']");
console.log(selectedValue);
//
if (update_value != selectedValue) {
// Do some things
}
}
//onChangeOption(document.querySelector('form'));
function start(){
while(typeof document.querySelector('form')!=typeof {}){}
onChangeOption(document.querySelector('.form-control'));
}
<body onload="start()">
<select class="form-control" onchange="onChangeOption(this)">
<option selected="selected" value="1">1</option>
<!-- I wanna got this -->
<option value="2">2</option>
</select></body>
I am using the SumoSelect dropdown for multiselect options. But i cannot get the selected values array.
Below the sample code :
<script type="text/javascript">
$(document).ready(function () {
window.testSelAll = $('.testSelAll').SumoSelect({okCancelInMulti:true, selectAll:true });
$('.btnOk').on('click', function(){
var obj = [];
$('option:selected').each(function () {
obj.push($(this).index());
alert("Selected Values=="+$(this).val());
});
for (var i = 0; i < obj.length; i++) {
$('.testSelAll')[0].sumo.unSelectItem(obj[i]);
}
});
});
</script>
<select multiple="multiple" placeholder="Share Your Friends" onchange="console.log($(this).children(':selected').length)" class="testSelAll">
<option value="1">Volvo</option>
<option value="2">Saab</option>
<option value="3">Mercedes</option>
<option value="audi">Audi</option>
<option value="bmw">BMW</option>
<option value="porsche">Porche</option>
<option value="ferrari">Ferrari</option>
<option value="mitsubishi">Mitsubishi</option>
</select>
If you want the selected values instead of the text, just change .text() to .val().
If you want to get the array, see below with working example at the bottom.
jQuery
$(document).ready(function() {
$('.testSelAll').SumoSelect({
okCancelInMulti: true,
selectAll: true
});
$('.btnOk').on('click', function() {
var obj = [],
items = '';
$('.testSelAll option:selected').each(function(i) {
obj.push($(this).val());
$('.testSelAll')[0].sumo.unSelectItem(i);
});
for (var i = 0; i < obj.length; i++) {
items += ' ' + obj[i]
};
alert(items);
});
});
HTML
<select multiple="multiple" class="testSelAll">
<option value="car1">Volvo</option>
<option value="car2">Saab</option>
<option value="car3">Mercedes</option>
<option value="car4">Audi</option>
</select>
Working JSFIDDLE
You can get them from underlying hidden select element.
using jquery eg.
$('.select1 option:selected')
I think the cleanest way to do this. Is to take advantage of html5 select element underlying SumoSelect.
HTML
<select multiple="multiple" class="testSelAll" id="multi-select">
<option value="car1">Volvo</option>
<option value="car2">Saab</option>
<option value="car3">Mercedes</option>
<option value="car4">Audi</option>
</select>
Javascript
var values = $('#multi-select').val();
This line will return a string list of the values selected.