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.
Related
I am working on a form with multiple drop down options.
I stripped everything down to three questions in the form below, to illustrate my issue.
I have tried this code (and a few variations):
function update_variables(el, standard_val, var_list) {
standard_val.value = var_list[el.getElementsByTagName('option')[el.selectedIndex].value];
}
var select2 = document.getElementById('think_question'),
hidden_answer = document.getElementsByName('thought_goes_here')[0];
var why_options = {
'yes': '',
'no': 'Well, you tried.'
};
select2.addEventListener('change', function() {
update_variables(select2, hidden_answer, why_options);
});
var sel_group_control = document.getElementById('follower_question');
var sel_free_will = document.getElementById('think_question');
sel_group_control.addEventListener('change', answer_bypass);
function answer_bypass() {
var user_choice = sel_group_control.value;
if (user_choice === 'no') {
sel_free_will.selectedIndex = 2;
sel_free_will.style.backgroundColor = "#D3D3D3";
sel_free_will.disabled = true;
}
}
<h2>Life Decisions</h2>
<form>
Be exactly like everone else?
<select id='follower_question'>
<option disabled selected value> -- select an option -- </option>
<option>yes</option>
<option>no</option>
</select>
<br> Think for yourself?
<select id='think_question'>
<option disabled selected value> -- select an option -- </option>
<option>yes</option>
<option>no</option>
</select>
<br> Original thought:<input name="thought_goes_here" size="50" value="" id="your_thoughts">
</form>
If question 2 is set to 'no' then the answer to question 3 is known and filled in with a response. If question 1 is 'no' then question 2 should be set to 'no' and read-only. I expected question 3 to be updated automatically too when choosing 'no' in response to question 1 but that function seems to be ignored.
You need to register an event to trigger the second select change.
Like so:
function answer_bypass() {
var user_choice = sel_group_control.value;
if (user_choice === 'no') {
sel_free_will.selectedIndex = 2;
sel_free_will.style.backgroundColor = "#D3D3D3";
sel_free_will.disabled = true;
// Create a new 'change' event
var event = new Event('change');
// Dispatch it.
select2.dispatchEvent(event);
}
}
I am trying to make an HTML Select control display a different set of strings when clicked (or opened) than what is displayed after an item is selected. For example, when opened I want to see "one", two", "three" displayed as choices. But if the user selects two, I want "2" to be displayed as the selected item. My onclick handler reloads the Select options list with the long version of the strings and the onchange handler repopulates the control with the short strings and then re-selects the selected item. This works in Firefox, but not in IE, Safari nor Chrome. It's been almost 10 years since I last had the pleasure of coding in JavaScript. Any help would be appreciated. Here's my code:
var selectedIndex = -1;
function onChanged() {
//once selected, replace verbose with terse forms
var myList = document.getElementById("myList");
selectedIndex = myList.selectedIndex;
var optionArray = ["One|1", "Two|2", "Three|3"];
myList.options.length = 0;
for (var option in optionArray) {
var pair = optionArray[option].split("|");
var newOption = document.createElement("option");
newOption.value = pair[1];
newOption.innerHTML = pair[1];
myList.options.add(newOption);
}
myList.selectedIndex = selectedIndex;
}
function onClicked() {
var myList = document.getElementById("myList");
var optionArray = ["1|One", "2|Two", "3|Three"];
myList.options.length = 0;
for (var option in optionArray) {
var pair = optionArray[option].split("|");
var newOption = document.createElement("option");
newOption.value = pair[1];
newOption.innerHTML = pair[1];
myList.options.add(newOption);
}
if (selectedIndex > -1)
myList.selectedIndex = selectedIndex;
}
<select id="myList" onchange="onChanged()" onclick="onClicked()">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
An alternate approach would be to have each option store the full version of the text, and only change the displayed text of the selected item to the abbreviated version upon selection.
(function() {
var valueMap = {
"1": "One (1)",
"2": "Two (2)",
"11": "Eleven (11)",
"ITAR": "International Traffic in Arms Regulation (ITAR)",
"ACA": "Affordable Care Act (ACA)",
"FUBAR": "Fluffed Up Beyond All Recognition (FUBAR)"
};
var myList = document.getElementById("myList");
for (var prop in valueMap) { // populate the dropdown from our object
var opt = document.createElement("option");
opt.value = prop;
opt.text = valueMap[prop];
myList.add(opt);
}
myList.selectedIndex = -1; // nothing selected by default
myList.addEventListener("change", function() {
this.options[this.selectedIndex].text = this.options[this.selectedIndex].value;
this.blur();
});
myList.addEventListener("mousedown", function() {
if (this.selectedIndex > -1) {
var newValue = valueMap[this.options[this.selectedIndex].value];
if (this.options[this.selectedIndex].text !== newValue) {
this.options[this.selectedIndex].text = newValue;
}
}
});
})();
<select id="myList" style="width:6em"></select>
This gets you most of the way there, but still has the annoying problem that #hopkins-matt alluded to; namely that if the user opens the drop-down list and either selects the already selected item or moves off of the list without selecting anything, the selection will retain the long version of the text.
The other downside to this approach is that you need to specify the select element's width to keep it from expanding to the maximum length of its hidden option elements.
It's a timing issue.
Change:
<select id="myList" onchange="onChanged()" onclick="onClicked()">
to:
<select id="myList" onchange="onChanged()" onmousedown="onClicked()">
If the user opens the list and moves off the list without clicking, the list will not revert to original unless you call onChanged() on onmouseout as well.
<select id="myList" onchange="onChanged()" onmousedown="onClicked()" onmouseout="onChanged()">
Update: To achieve the best cross browser (onfocus is required for FF, but breaks IE) support without browser sniffing us this combination:
<select id="myList" onchange="onChanged()" onblur="onChanged()" onfocus="onClicked()" onmousedown="onClicked()">
This will also correct the second selection of the same event, but only after the user clicks away from the element.
Update:
Solved... I think.
I rewrote the function you were using to change the options. IE was not firing onchange due to you removing all the option elements and adding new option elements. Which was causing IE to not be able to reference if the user had changed the selection index. The function now just modifies the value and innerHTML of the current option elements. I am using browser sniffing to eliminate the onmouseout call for FF. FireFox was calling onmouseout if you moved the cursor to the dropdown menu. This does cause a side effect in FF. If the user selects the same option in FF, the options do not return to the original state until the onblur is fired.
Demo: http://jsfiddle.net/hopkins_matt/3m1syk6c/
JS:
function changeOptions() {
var selectedIndex = -1;
var click = 0;
var myList = document.getElementById("myList");
var optionArray = ["One|1", "Two|2", "Three|3"];
var fireFox = /Firefox/i.test(navigator.userAgent);
console.log(fireFox);
if (fireFox == false) {
myList.onmouseout=function(){changeList(false)};
}
myList.onblur=function(){changeList(false)};
myList.onchange=function(){changeList(false)};
myList.onmousedown=function(){changeList(true)};
function changeList(listOpen) {
var isListOpen = listOpen;
if (isListOpen == true) {
for (i = 0; i < myList.options.length; i++) {
var pair = optionArray[i].split("|");
myList.options[i].value = pair[0];
myList.options[i].innerHTML = pair[0];
}
}
if (isListOpen == false) {
for (i = 0; i < myList.options.length; i++) {
var pair = optionArray[i].split("|");
myList.options[i].value = pair[1];
myList.options[i].innerHTML = pair[1];
}
}
}
}
changeOptions();
HTML:
<select id="myList">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
How can I cancel a select menu choice using a confirm dialog?
The idea is that when a user changes a select menu, they're prompted to confirm their choice. if they choose "cancel", the select menu goes back to its previously selected value. If they choose "OK", the select menu behaves as expected.
Here is the code I'm working on:
HTML:
<select class="selector" name="selector1">
<option value="yes">Yes</option>
<option value="no" selected="">No</option>
<option value="maybe">Maybe</option>
</select>
<select class="selector" name="selector2" >
<option value="yes">Yes</option>
<option value="no" selected="">No</option>
<option value="maybe">Maybe</option>
</select>
JavaScript
var selects = document.querySelectorAll('.selector');
var lastSelected = {};
for (i = 0; i < selects.length; i++) {
var select = selects[i];
lastSelected[select.name] = select.options[select.selectedIndex];
select.addEventListener('change', function (e) {
lastSelected = select.options[select.selectedIndex];
if (confirm("Are you want to choose this?") == true) {
return;
} else {
select.value = lastSelected[select.name];
}
});
}
I'm not entirely sure why this isn't working, any help is much appreciated.
Here is the fiddle I'm working on http://jsfiddle.net/je36eu78/2/
n.b I'd like to do this in native JavaScript (no jquery)
you overwrite lastSelected here:
lastSelected = select.options[select.selectedIndex];
you must also store the new value when the user hits OK
Another approach(stores the previous value as a property of the select)
var selects = document.querySelectorAll('.selector');
for (i = 0; i < selects.length; i++) {
var select = selects[i];
select.defaultValue=select.value;
select.addEventListener('change', function (e) {
if (confirm("Are you want to choose this?") == true) {
this.defaultValue=this.value;
} else {
this.value=this.defaultValue;
}
});
}
Try the snippet below
var select = document.querySelectorAll('.selector');
for (i = 0; i < select.length; i++) {
select[i]['last'] = select[i].options[select[i].selectedIndex].value;
(function(i){
select[i].onchange = function(){
if(confirm("Sure?")){
select[i]['last'] = this.value;
}else{
this.value = select[i]['last'];
}
};
})(i);
}
Working jsBin
Javascript and I will never be best friends.
I try to set a checkbox to checked by changing the elements from a dropdownlist.
This is the snippet
<select name="change_status_to" id="sel">
<option>....</option>
<option>....</option>
</select>
<input type="checkbox" name="do_status_change" value="on">
How could I use Javascript with the onchange-event to set the checkbox to the state checked?
You can set its .checked property:
document.getElementById("do_status_change").checked = true;
document.getElementById("do_status_change").checked = false;
However, you need to set an id; the name attribute will not suffice.
> Example <
Here is an example of why you should be friends with javascript.
JSFiddle - http://jsfiddle.net/juc7Q/1/
HTML
You're feeling toward Javascript
<select name="friends" id="friends">
<option value="love">I love JS</option>
<option value="weird">JS is weird</option>
<option value="misunderstood">Are we speaking the same language?</option>
</select>
<button id="selectWeird">Keep JS Weird</button>
<div>
<input type="checkbox" id="we_friends"> - Friends?
</div>
JavaScript
//Select this using js
var list = document.getElementById('friends');
var cb = document.getElementById('we_friends');
//Add event when change happens
list.onchange = function () {
var value = list.value;
if(value == 'love') {
alert('I knew you would come around');
cb.setAttribute('checked', '');
} else if(value == 'weird') {
alert('And I like being weird :-)!!');
cb.removeAttribute('checked');
} else {
alert('And you think I understand you?');
cb.removeAttribute('checked');
}
}
//Change to 'Weird' when I click on it
var btn = document.getElementById('selectWeird');
btn.onclick = function () {
list.options.selectedIndex = 1;
}
I have an HTML page in which I have 2 selects.
<select id="field" name="field" onchange="checkValidOption();">
<option />
<option value="Plugin ID">Plugin ID</option>
<option value="Name">Name</option>
</select>
<select id="operator" name="operator" onchange="checkValidOption();">
<option />
<option value="EQUALS">EQUALS</option>
<option value="CONTAINS">CONTAINS</option>
<option value="NOT CONTAINS">NOT CONTAINS</option>
<option value="REGEX">REGEX</option>
</select>
What I'd like to happen is that checkValidOption() could make it so that if "Plugin ID" is selected in field that the only option is EQUALS (and it's selected) and otherwise all the other options are available. Any idea on how to approach this?
I tried changing the innerHTML of the operator select in JS:
document.getElementById("operator").innerHTML =
"<option value='EQUALS'>EQUALS</option>";
However this results in an empty select (this would also include manually setting the many options for going back to having all the ones listed above).
I can't think of another solution, any help would be greatly appreciated.
Try this:
Demo here
var field = document.getElementById('field');
var operator = document.getElementById('operator');
field.onchange = function () { fieldcheck(); }
operator.onchange = function () { fieldcheck(); }
fieldcheck();
function fieldcheck() {
if (field.value == 'Plugin ID') {
for (i = 0; i < operator.options.length; ++i) {
if (operator.options[i].value != 'EQUALS') {
operator.options[i].disabled = true;
}
};
operator.value = 'EQUALS';
} else {
for (i = 0; i < operator.options.length; ++i) {
operator.options[i].disabled = false;
};
}
}
To manipulate options when Plugin ID was selected:
function checkValidOption(){
var x=document.getElementById("field");
var y=document.getElementById("operator");
if (x.options[1].selected === true){
document.getElementById("operator").options[1].selected = true;
for(var i=0; i<y.length; i++){
if (i !== 1){
//disabling the other options
document.getElementById("operator").options[i].disabled = true;
}
}
}
else{
for(var i=0; i<y.length; i++){
//enabling the other options
document.getElementById("operator").options[i].disabled = false;
}
}
}
Here's a link to fiddle
A select field doesn't use the innerHTML method, you need to use value.
document.getElementById("operator").value = "...";
heres a jquery solution.
every time the first select changes, it produces new options from an array for the 2nd select. issue here is i had to change the option values of the first select to 0 and 1 to select which value in the array, you can manipulate those later if you are storing this info somewhere
http://jsfiddle.net/2TZJh/
$(document).ready(function() {
$("#field").change(function() {
var val = $(this).val();
$("#operator").html(options[val]);
});
var options = [
'<option value="EQUALS">EQUALS</option>',
'<option></option><option value="EQUALS">EQUALS</option><option value="CONTAINS">CONTAINS</option> <option value="NOT CONTAINS">NOT CONTAINS</option> <option value="REGEX">REGEX</option>'
];
});