In this code I have 2 dependent dropdown lists and a button to duplicate/clone the form. The color selection changes based on what is selected in item. When I duplicate the dropdown list the function didn't work. I tried changing the id of the duplicated dropdown list but still can't manage to match the id of 2 dropdown list. Is there any solution? Thanks.
var count = 1;
var duplicate_div = document.getElementById('duplicate_1');
function addRecord() {
var clone = duplicate_div.cloneNode(true);
clone.id = "duplicate_" + ++count;
duplicate_div.parentNode.append(clone);
var cloneNode = document.getElementById(clone.id).children[0];
$(clone).find("*[id]").each(function() {
$(this).val('');
var tID = $(this).attr("id");
var idArray = tID.split("_");
var idArrayLength = idArray.length;
var newId = tID.replace(idArray[idArrayLength - 1], count);
$(this).attr('id', newId);
});
}
$(document).ready(function() {
$("#item_" + count).change(function() {
var val = $(this).val();
if (val == "shirt") {
$("#color_" + count).html("<option>Black</option> <option>Gray</option>");
} else if (val == "pants") {
$("#color_" + count).html("<option>Blue</option> <option>Brown</option>");
} else if (val == "shoe") {
$("#color_" + count).html("<option>White</option> <option>Red</option>");
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form class="select-form">
<div class="duplicate" id="duplicate_1">
<br>
<label>item</label>
<select id="item_1">
<option value="template" disabled selected></option>
<option value="shirt">Shirt</option>
<option value="pants">Pants</option>
<option value="shoe">Shoe</option>
</select>
<label>color</label>
<select id="color_1">
<option disabled selected>Select item first</option>
</select>
</div>
</form>
<br><br>
<button type="button" id="add-button" onclick="addRecord()">add</button>
Since you've imported jQuery into the project, I suggest you fully use it.
It's recommended to use jQuery's .on method instead of onclick attribute.
The change event will not work on the dynamically created elements.
You should instead use "event delegation".
Last but not least, you can remove the ids if they serve as selectors. You can use jQuery to easily transverse the DOM
Try this
$(document).ready(function() {
var $cloned = $('.duplicate').first().clone(true);
var $container = $('.select-form');
$('#add-button').click(function() {
$container.append($cloned.clone());
})
$('.select-form').on('change', '.item', function() {
var val = $(this).val();
var $color = $(this).closest('.duplicate').find('.color');
if (val == "shirt") {
$color.html("<option>Black</option> <option>Gray</option>");
} else if (val == "pants") {
$color.html("<option>Blue</option> <option>Brown</option>");
} else if (val == "shoe") {
$color.html("<option>White</option> <option>Red</option>");
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form class="select-form">
<div class="duplicate">
<br>
<label>item</label>
<select class="item">
<option value="template" disabled selected></option>
<option value="shirt">Shirt</option>
<option value="pants">Pants</option>
<option value="shoe">Shoe</option>
</select>
<label>color</label>
<select class="color">
<option disabled selected>Select item first</option>
</select>
</div>
</form>
<br><br>
<button type="button" id="add-button">add</button>
I made a form that has a chained dropdown lists. Each subsequent dropdown list will be repopulated with ajax request based on the first dropdown list selection. In each dropdown list the first selected field is "---------" which has the attribution selected. When user selects another option the previous selected option still has the attribute of "selected". What I want to achieve is that the new selected option will be the one which has the selected attribute.
I have tried the .removeProp('selected', false)
and .removeAttr('selected') but the problem is persisting.
Here is the HTML Code:
<div id="div_id_CommonForm-country" class="form-group">
<label for="id_CommonForm-country" class=" requiredField">
country :
<span class="asteriskField">*</span>
</label>
<div class="">
<select name="CommonForm-country" class="select form-control" required="" id="id_CommonForm-country">
<option value="" selected="">---------</option>
<option value="1">USA </option>
<option value="5">Canada</option>
</select>
</div>
Here is the ajax request:
<script type="text/javascript">
$("#id_CommonForm-country").change(function(){
var url_province = $("#ads_main_post_form").attr("data-provinces-url"); // get the url of the `load_provinces` view
var countryId = $(this).val(); // get the selected country ID from the HTML input
$("#id_CommonForm-country option:selected").each(function () {
$(this).attr('selected', '');
});
var value = $(this).val();
$(this).find('option[value="'+value+'"]').attr("selected", "selected");
$.ajax({ // initialize an AJAX request
url: url_province, // set the url of the request (= localhost:8000/twons/ajax/load-rovinces/)
data: {
'user_country': countryId, // add the country id to the GET parameters
csrfmiddlewaretoken: '{{csrf_token}}',
},
success: function (data) { // `data` is the return of the `load_provinces` view function
var json = JSON.parse(data);
var provinces_list = $("#id_CommonForm-province").html('');
var city_list = $("#id_CommonForm-city").html('');
city_list.append('<option selected disabled>'+'---------'+ '</option>');
var first_item =
'<option selected disabled>'+'---------'+ '</option>';
var list=""
for(var j = 0; j < json.province_id.length; j++) {
list+=
'<option value="'+json.province_id[j]+'">'+json.province_name[j]+'</option>';
// replace the contents of the province input with the data that came from the server
}
complete_list = first_item.concat(list);
provinces_list.append(complete_list);
}
});
});
Any help or suggestion is highly appreciated:
Note: I have tried many suggested solutions posted on stackover flow but nothing helpt.
You can simply use removeAttr('selected'); to remove selected and then use attr("selected", "selected"); to add option selected to only to choosen option.
Demo code :
$("#id_CommonForm-country").change(function() {
var url_province = $("#ads_main_post_form").attr("data-provinces-url");
var countryId = $(this).val();
$("#id_CommonForm-country option").removeAttr('selected'); //remove attr selected
$(this).find('option[value="' + countryId + '"]').attr("selected", "selected"); //add selected to option actual selected
console.log(countryId)
//your ajax call
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="div_id_CommonForm-country" class="form-group">
<label for="id_CommonForm-country" class=" requiredField">
country :
<span class="asteriskField">*</span>
</label>
<div class="">
<select name="CommonForm-country" class="select form-control" required="" id="id_CommonForm-country">
<option value="" selected>---------</option>
<option value="1">USA </option>
<option value="5">Canada</option>
</select>
</div>
Only the hospital dropdown code is displaying "HOSPITAL 1" in the dropdown display but the ward dropdown code is not displaying "WARD 1" like it should when I refresh the page.
Everytime I refresh the page it will call ng-change from hospital dropdown and retrieve the respective wards according to the hospital selected which is "HOSP1" but the display for the ward dropdown will be set as "Please Select Ward" instead of "WARD1".
I tried to retain the old wardCode == 'WARD1' like I did for hosp in the controller but to no avail as the value changes after the ng-change is fired.
Does anyone know if I can use angular copy in this scenario?
HTML
<div class="form-group">
<label class="control-label">Hosp</label>
<select ng-model='search.hosp'select-options=" {{hosp}}" select-data="
{{search.hospCode}}" ng-change="wardDropdown()" id="hospCode"
name="hospCode">
<option value="">Please Select Hosp</option>
<option ng-repeat="obj in hosp" value="{{obj.VALUE}}">{{obj.LABEL}}
</option>
</select>
</div>
<div class="form-group">
<label class="control-label">Ward</label>
<select ng-model='search.wardCode' select-options="{{wards}}"
select-data="{{search.wards}}" id="wardCode" name="wardCode">
<option value="">Please Select Ward</option>
<option ng-repeat="obj in wards" value="{{obj.VALUE}}">
{{obj.LABEL}}
</option>
</select>
</div>
JS
$scope.search = {}
$scope.search.hosp = 'HOSP1';
$scope.search.wardCode = 'WARD1';
//Start, hosp dropdown list
$scope.hosp = function () {
var url = /searchHosp',
method = methodType.POST,
functionName = 'searchHosp',
params = {};
if (data.status == messageStatus.success) {
$scope.hospitals = data.result;
} else {
$scope.errorMessage = promptMessage.searchRecordFailure;
}
},
function (error) {
$scope.errorMessage = promptMessage.exception;
)
};
$scope.hospitalList();
//End, hosp dropdown
//Start, ward dropdown
$scope.wardDropdown = function () {
var url = searchWard,
method = methodType.POST,
functionName = 'searchWard',
params = {};
params.hosp = $scope.search.hosp;
if (data.status == messageStatus.success) {
$scope.wards = data.result;
}
}
else {
$scope.errorMessage = promptMessage.searchRecordFailure;
}
$scope.wardDropdown();
//End, ward dropdown list
I'm using a <datalist>
<datalist id="items"></datalist>
And using AJAX to populate the list
function callServer (input) {
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
//return the JSON object
console.log(xmlhttp.responseText);
var arr = JSON.parse(xmlhttp.responseText);
var parentDiv = document.getElementById('items');
parentDiv.innerHTML = "";
//fill the options in the document
for(var x = 0; x < arr.length; x++) {
var option = document.createElement('option');
option.value = arr[x][0];
option.innerHTML = arr[x][1];
//add each autocomplete option to the 'list'
option.addEventListener("click", function() {
console.log("Test");
});
parentDiv.appendChild(option);
};
}
}
xmlhttp.open("GET", "incl/search.php?value="+input.value, true);
xmlhttp.send();
}
However I can't get it to perform an action when I click on a selection in the datalist, for example if I type in "Ref F" and the item "Ref flowers" comes up, if I click on it I need to execute an event.
How can I do this?
option.addEventListener("click", function() {
option.addEventListener("onclick", function() {
option.addEventListener("change", function() {
Sorry for digging up this question, but I've had a similar problem and have a solution, that should work for you, too.
function onInput() {
var val = document.getElementById("input").value;
var opts = document.getElementById('dlist').childNodes;
for (var i = 0; i < opts.length; i++) {
if (opts[i].value === val) {
// An item was selected from the list!
// yourCallbackHere()
alert(opts[i].value);
break;
}
}
}
<input type='text' oninput='onInput()' id='input' list='dlist' />
<datalist id='dlist'>
<option value='Value1'>Text1</option>
<option value='Value2'>Text2</option>
</datalist>
This solution is derived from Stephan Mullers solution. It should work with a dynamically populated datalist as well.
Unfortunaltely there is no way to tell whether the user clicked on an item from the datalist or selected it by pressing the tab-key or typed the whole string by hand.
Due to the lack of events available for <datalist> elements, there is no way to a selection from the suggestions other than watching the input's events (change, input, etc). Also see my answer here: Determine if an element was selected from HTML 5 datalist by pressing enter key
To check if a selection was picked from the list, you should compare each change to the available options. This means the event will also fire when a user enters an exact value manually, there is no way to stop this.
document.querySelector('input[list="items"]').addEventListener('input', onInput);
function onInput(e) {
var input = e.target,
val = input.value;
list = input.getAttribute('list'),
options = document.getElementById(list).childNodes;
for(var i = 0; i < options.length; i++) {
if(options[i].innerText === val) {
// An item was selected from the list!
// yourCallbackHere()
alert('item selected: ' + val);
break;
}
}
}
<input list="items" type="text" />
<datalist id="items">
<option>item 1</option>
<option>item 2</option>
</datalist>
Use keydown
Contrary to the other answers, it is possible to detect whether an option was typed or selected from the list.
Both typing and <datalist> clicks trigger the input's keydown listener, but only keyboard events have a key property. So if a keydown is triggered having no key property, you know it was a click from the list
Demo:
const opts = document.getElementById('dlist').childNodes;
const dinput = document.getElementById('dinput');
let eventSource = null;
let value = '';
dinput.addEventListener('keydown', (e) => {
eventSource = e.key ? 'input' : 'list';
});
dinput.addEventListener('input', (e) => {
value = e.target.value;
if (eventSource === 'list') {
alert('CLICKED! ' + value);
}
});
<input type="text" id="dinput" list="dlist" />
<datalist id="dlist">
<option value="Value1">Text1</option>
<option value="Value2">Text2</option>
</datalist>
Notice it doesn't alert if the value being clicked is already in the box, but that's probably desirable. (This could also be added by using an extra tracking variable that will be toggled in the keydown listener.)
Datalist actually don't have an event (not all browsers), but you can detect if a datalist option is selected in this way:
<input type="text" list="datalist" />
<datalist id="datalist">
<option value="item 1" />
<option value="item 2" />
</datalist>
window.addEventListener('input', function (e) {
let event = e.inputType ? 'input' : 'option selected'
console.log(event);
}, false);
demo
Shob's answer is the only one which can detect when an option gets clicked as well as not trigger if an intermediary written text matches an option (e.g.: if someone types "Text1" to see the options "Text11", "Text12", etc. it would not trigger even if "Text1" is inside the datalist).
The original answer however did not seem to work on newer versions of Firefox as the keydown event does not trigger on clicks so I adapted it.
let keypress = false;
document.getElementById("dinput").addEventListener("keydown", (e) => {
if(e.key) {
keypress = true;
}
});
document.getElementById("dinput").addEventListener('input', (e) => {
let value = e.target.value;
if (keypress === false) {
// Clicked on option!
console.debug("Value: " + value);
}
keypress = false;
});
<input type="text" id="dinput" list="dlist" />
<datalist id="dlist">
<option value="Value1">Text1</option>
<option value="Value2">Text2</option>
</datalist>
Datalist don't support click listener and OnInput is very costly, checking everytime all the list if anything change.
What I did was using:
document.querySelector('#inputName').addEventListener("focusout", onInput);
FocusOut will be triggered everytime a client click the input text and than click anywhere else. If they clicked the text, than clicked somewhere else I assume they put the value they wanted.
To check if the value is valid you do the same as the input:
function onInput(e) {
var val = document.querySelector('#inputName').value;
options = document.getElementById('datalist').childNodes;
for(var i = 0; i < options.length; i++) {
if(options[i].innerText === val) {
console.log(val);
break;
}
}
}
<input type="text" id="buscar" list="lalista"/>
<datalist id="lalista">
<option value="valor1">texto1</option>
<option value="valor2">texto2</option>
<option value="valor3">texto3</option>
</datalist>
//0 if event raised from datalist; 1 from keyboard
let idTimeFuekey = 0;
buscar.oninput = function(){
if(buscar.value && idTimeFuekey==0) {
alert('Chévere! vino desde la lista')
}
};
buscar.onkeydown = function(event){
if(event.key){ //<-- for modern & non IE browser, more direct solution
window.clearInterval(idTimeFuekey);
idTimeFuekey = window.setInterval(function(){ //onkeydown --> idTimeFuekey++ (non 0)
window.clearInterval(idTimeFuekey);
idTimeFuekey = 0 //after 500ms onkeydown --> 0 (could work 500, 50, .. 1)
}, 500)
}
}
Well, at least in Firefox the onselect event works on the input tag
<input type="text" id="dinput" list="dlist" onselect="alert(this.value)"/>
<datalist id="dlist">
<option value="Value1">Text1</option>
<option value="Value2">Text2</option>
</datalist>
After having this problem and not finding a suitable solution, I gave it a shot.
What I did was look at the "inputType" of the given input event on top of the event toggle variable from above, like so:
eventSource = false;
const selector = document.getElementById("yourElementID");
selector.addEventListener('input', function(evt) {
if(!eventSource) {
if(evt.inputType === "insertReplacementText") {
console.log(selector.value);
}
}
});
selector.addEventListener('keydown', function(evt) {
eventSource = !evt.key;
});
This works if you want to allow the user to search a field but only hit a specific function/event on selection from the datalist itself. Hope it helps!
Edit: Forgot to mention this was done through Firefox and has not been tested on other browsers.
I'm creating a standard html select dropdown with a hundred or so entries. My users would like to be able to type in the value to get to the proper selection faster. While this is supported natively, the keystroke timeout is very quick, so if you don't type the string quickly, you end up with the wrong selection. Is there a way to increase the timeout? Or has anyone written code to do this manually?
Here's a jsFiddle to illustrate the issues. JsFiddle
label for="title">Choose your poison</label>
<select id="title" name="title">
<option value="Cider" selected>Apple Cider</option>
<option value="Juice">Apple Juice</option>
<option value="Curacao">Curacao</option>
<option value="Jack">Jack's Hard Cider</option>
<option value="Jake">Jake's Hard Cider</option>
<option value="James">James' Hard Cider</option>
<option value="Jamison">Jamison Irish Whiskey</option>
<option value="Kool">Kool Ade</option>
<option value="Lemonade">Lemonade</option>
<option value="Prune">Prune Juice</option>
</select>
Try selecting the Jack's or Jake's by slowly typing and see if you end up selecting Curacao or Kool Ade.
You could use a <datalist> instead. It's supported in IE10 and higher. MDN Page
DEMO
<label for="poison">Choose your poison</label>
<input id="poison" name="poison" list="poisons" />
<datalist id="poisons">
<option value="Cider" selected>Apple Cider</option>
<option value="Juice">Apple Juice</option>
<option value="Curacao">Curacao</option>
<option value="Jack">Jack's Hard Cider</option>
<option value="Jake">Jake's Hard Cider</option>
<option value="James">James' Hard Cider</option>
<option value="Jamison">Jamison Irish Whiskey</option>
<option value="Kool">Kool Ade</option>
<option value="Lemonade">Lemonade</option>
<option value="Prune">Prune Juice</option>
</datalist>
If you have a small number of entries, the answer using datalist is fantastic. However, my users were using lists that had over a hundred entries and the datalist won't scroll. So, I built a utility class for use in a few places.
To use it, create the listFilter and initialize it with your list of choices. Then hook up keyUp and keyPress and use the return values. The class uses a 2-second time out and actually filters the answers. Using the delete key, you can clear the filtering. Be sure to check for a null return value in the keyUp, since that only handles the delete key. Note that the dropdown must be unexpanded for you to get the key events to work.
var listFilter = {
originalListToHold: [],
time1: 1,
search: "",
initialize: function(originalListToCopy) {
this.originalListToHold = [];
for (var i = 0; i < originalListToCopy.length; i++) {
this.originalListToHold[i] = originalListToCopy[i];
}
},
isInOriginalList: function(member) {
return this.originalListToHold.indexOf(member) > 1;
},
keyUpEvent: function(event) {
var keyCode = event.keyCode;
if (keyCode == 46) {
var filtered = this.filterList("");
this.search = "";
event.stopPropagation();
return filtered;
} else {
return null;
}
},
keyPressEvent: function(event) {
//The delete key will reset the list. See the key up event above.
var val = String.fromCharCode(event.which).toUpperCase();
var timenow = event.timeStamp;
var timeDiff = timenow - this.time1;
if (!isNaN(timeDiff)) {
//If the time difference is < 2 seconds (2000 ms), then we
//will search the options.
if (timeDiff > 2000) {
//Reset the search string
this.search = "" + val;
} else {
this.search = this.search + val;
}
} else {
this.search = "" + val;
}
this.time1 = timenow;
//Now, let's filter the options by the search string.
var filtered = this.filterList(this.search);
event.stopPropagation();
return filtered;
},
filterList: function(filter) {
var newList = [];
for (var i = 0; i < this.originalListToHold.length; i++) {
if (this.originalListToHold[i].indexOf(filter) > -1) {
newList.push(this.originalListToHold[i]);
}
}
return newList;
}
}