Inherited some Javascript code (which is paraphrased in the Fiddle for ease of reproduction), which is filtering Select Options based on a selection in a previous Select.
When the page loads, a clone of the Select is made to hold the original values of the 2nd Select element.
When the user makes a Selection in Select element 1, Select element 2 is restored with the original values (as the user might make a different selection in element 1), and then it filters the list.
When the user makes a selection in Select 2, we remove the "Select..." element from the list... But In IE10 the selected element then becomes the option below it.
This only happens in IE10 (IE8/9/11 are all ok, as are latest versions of Chrome and FF)
It looks like an issue with the .clone() element being restored and the "Select..." option being removed.
Here's a fiddle to check it...
cloneModel = $("#model").clone();
$("#make").change(function(){
restoreModels();
removeNoOption("#make");
$("#make").hide();
$("#make").show();
filterModels($("#make option:selected").val());
$("#model").show();
})
function modelChange(){
removeNoOption("#model");
$("#model").hide();
$("#model").show();
}
function removeNoOption(element){
$(element + " option").each(function(){
if($(this).val() == "-"){
$(this).remove();
}
})
}
function restoreModels(){
$("#model").unbind();
$("#model").replaceWith(cloneModel);
cloneModel = $("#model").clone();
$("#model").change(modelChange);
$("#model").hide();
}
function filterModels(make){
$.each(models, function(key, value) {
if(make != value.make){
$("#model option[value='"+value.model+"']").remove();
}
});
}
Anyone ever seen this before or know what could be happening?
Related
I have 3 select tags on one page, generated from struts2 select tag. I am using a jQuery filter function that filters the select. One textfield for every select, but all of them use the same filter function. I have another js function that is called on onChange event.
The problem is that before adding this jQuery function i was filtering the lists with form submit and reload the page and now the filtration happens instant, but when i write a filtration criteria the select somehow the select loses focus and when i click an element no select happens, or better lets say is a kind of select: the element is circled with a dotted line, not selected with a blue filled square. The js is called, the form submitted, but with the old value. However, if i first click in the select where are no elements (empty zone) and then i select an element everything is ok. How can i jump over the firs click?
And now my code:
I. The jQuery filter function and the binding to the selects and textfields.
jQuery.fn.filterByText = function(textbox) {
return this.each(function() {
var select = this;
var options = [];
$(select).find('option').each(function() {
options.push({value: $(this).val(), text: $(this).text()});
});
$(select).data('options', options);
$(textbox).bind('change keyup', function() {
var options = $(select).empty().scrollTop(0).data('options');
var search = $.trim($(this).val());
var regex = new RegExp(search,'gi');
$.each(options, function(i) {
var option = options[i];
if(option.text.match(regex) !== null) {
$(select).append($('<option>').text(option.text).val(option.value));
}
});
});
});
};
$(function() {
$('#selectedClientId').filterByText($('#filterClient'));
$('#selectedLocationId').filterByText($('#filterLocation'));
$('#selectedViewPointId').filterByText($('#filterViewpoint'));
});
II. One of the selects:
<s:select size="10" cssStyle="width:220px;"
label="Select a client"
listKey="id" listValue="name"
list="clientSelectList"
name="selectedClientId" id="selectedClientId"
headerKey="-1" headerValue="Client List"
onchange="onChangeSelect()"
/>
III. The select's textfield:
Filter:<s:textfield name="filterClient" id="filterClient" size="15" autocomplete="off"/>
IV. The onChangeSelect():
function onChangeSelect() {
document.getElementById('deviceListForm').action = '<s:url value="/admin/displayDeviceListPage.action"/>';
document.getElementById('deviceListForm').submit();
}
In the image: in the first select is how looks the selected option after jquery filter and in the other 2 selects are "the good" selected options.
http://i.stack.imgur.com/TAJeY.png
EDIT: So, after the first response to this post (Thanks Amit1992) I started digging further. In Mozilla after the first click (after the frame or dotted line appears) a request is made to the server with the old selected item (or null if none selected), the page refreshes as nothing happened.
In Chrome, on the other hand, the first click does not make any request. It just selects (let's say) the select tag. And the second select makes a good request.
Short story:
mozilla: click 1 -> request with old selected value ->refresh -> no changes
chrome: click 1 -> selects the select tag and no request is made -> click 2 -> request as it should happen
IE - works ok. 1 click-> select and load page as it should . OK this really surprises me. at first I thought is useless to look in IE what happens.
EDIT2
After some digging i concluded that the problem is that when typing in the textfield the select loses focus. If I put $('#selectedClientId').focus();
in the filterByText function at the end of the $(textbox).bind('change keyup', function() the fist select is focused after every char written. But this gives me another problem. I can't write more than 1 char at a time. I have to click the textfield, write a char, click again, write a char etc.
May be this will help you. i did some modification in your code.
$(function() {
$('#selectedClientId').filterByText($('#textbox'), false);
$("#selectedClientId").change(function(){
alert("you have selected ++ " + $(this).val());
});
});
I used change() event of jquery instead of javascript onChange().
you can refer this link http://jsfiddle.net/amitv1093/q55k97yc/ and I recommend you to use jquery fully if you are using it.
kindly let me know if it will work.
I solved the problem by changing the whole filter function. The function with problem was taken from a stack overflow question response (How to dynamic filter options of <select > with jQuery?). The problem is, from what I concluded, that the $(textbox).bind('change keyup', function() line was changing the focus on the textfield, so the first click was changing the focus to the select tag. The new function, the one that works was taken from the same post, John Magnolia's answer:
$(document).ready(function() {
filterClient();
filterLocation(); //same as filterClient
filterViewpoint(); //same as filterClient
});
function filterClient(){
var $this, filter,
$input = $('#filterClient'),
$options = $('#selectedClientId').find('option');
$input.keyup(function(){
filter = $(this).val();
$options.each(function(){
$this = $(this);
$this.removeAttr('selected');
if ($this.text().toLowerCase().indexOf(filter.toLowerCase()) != -1) {
$this.show();
} else {
$this.hide();
}
});
});
}
I'm using the code from the accepted answer here
How do you limit options selected in a html select box?
to count the selected options in a 'select multiple' menu:
var last_valid_selection = null;
$("#select_options").change(function(event) {
if ($(this).val().length > 10) {
$(this).val(last_valid_selection);
} else {
last_valid_selection = $(this).val();
$("#select_options_text").text("Please select at least one, and up to ten options. You have currently selected "+$(this).val().length);
}
});
The menu is divided into six optgroups. When I hit 10 selections I can no longer make selections, as expected. But I can also no longer use CTRL+click on selected options to deselect them.
If I remove all optgroups, the menu functions correctly. It also functions correctly with one and two optgroups. It only seems to be when a third optgroup is added that the problem described above appears.
I have tested in Chrome and Firefox and the problem occurs in both.
Problem
You have duplicate options, so when try to restore the last selection by calling $(this).val(last_valid_selection), you could be selecting more than one value than you actually want (i.e. you end up selecting more than 10).
For example, you have more than one Biochemistry, so when last_valid_selection contains one instance of Biochemistry, all the duplicate Biochemistry options will be selected.
Solution
Use a different way of remembering the last valid selections.
Here I present a solution using data attribute and individually store whether or not an option has been previously selected.
function save_selected(select){
$(select).find("option").each(function(){
var t = $(this);
t.data("last-selected", t.is(":selected"));
});
};
function load_selected(select){
$(select).find("option").each(function(){
var t = $(this);
t.attr("selected", t.data("last-selected"));
});
};
$("#select_options").change(function(event) {
if ($(this).val().length > 10) {
load_selected(this);
} else {
save_selected(this);
}
});
Using this method, each individual option element has its own "last selected" state stored in its own data attribute. There would be no duplicate conflicts.
Demo: https://jsfiddle.net/alan0xd7/gzdrL5wu/12/
I have a table with a button that adds a row to the bottom by cloning the last row. Each row contains a <select> with a different name (0-9), all <select> objects have the .variable_priority class. When pressing the button, the row gets copied successfully and I manage to change the name correctly (10, 11, 12, etc.), and the row gets the same class since it is a perfect copy.
However, when I execute the following function, I encounter problems with cloned table rows:
$('.variable_priority').change(function() {
var selections = new Array();
$(".variable_priority").each(function() {
selections.push($(this).find('option:selected').val());
});
// Iterating on Options of Select
$('.variable_priority').children('option').each(function() {
$(this).removeAttr('disabled');
if($.inArray($(this).val(), selections) !== -1){
if($(this).val() != 0){
if($(this).is(':selected')){
// nothing
} else {
$(this).attr('disabled', true);
}
}
}
});
});
The function should run each time I change the selected option of a <select>, though it only runs with the 10 original ones. This function is not executed when I change the option in a cloned row. However, if I change the value in one of the originals, the function runs and it also adds the values from the cloned rows to the selections array.
So for some reason, the code recognizes that the clones have the same class, but it doesn't run when changing their values.
EDIT: short description of the code above; if I change the selected value of a <select>,the function will add all selected options to an array and disable every selected option in the selects, except in the box where the value itself is selected (if I select '2' in the first box, '2' gets disabled in all other boxes).
Because your select element is dynamic (It is added to the DOM after the initial page load, or to be specific; after the initial event handler attachment) you will need to delegate your change event handler:
$(document).on('change', '.variable_priority', function() {
});
Rather than use document though, you'll want to attach the event handler to the closest static element to .variable_priority.
JSFiddle
I think the code isn't executed on the new clones.
This script is loaded on start. It has to do it over for each new children.. Maybe after adding a new, run this script specific for that new one.
I am trying to load options into a select element when the user focuses.
Here is the jquery code I'm using:
$('select').focus(function() {
var $this = $(this);
if (!$this.data('hasitems')) {
var selectedValue = $this.val();
$this.empty()
.append($('<option></option')
.attr('value','')
.text('Loading...'));
// This would usually be the result of an AJAX call
$this.empty()
.data('hasitems','true');
$.each(['a','b','c','d'], function(index,item) {
$this.append($('<option></option>')
.attr('value',item)
.text(item)
.prop('selected', selectedValue == item));
});
}
});
Here is the fiddle:
http://jsfiddle.net/agnnC/
The solution almost works... except in Firefox (and sometimes not in Chrome either, although the fiddle I put together does appear to work).
The problem in Firefox is that when the user clicks on the select element, the currently selected value is not remembered and is changed to one of the new values in the drop down.
Can anyone think of a workaround?
I think the issue is that the selected attribute is not really a property the way you are using it. Per the MDN doc:
selected
If present, this Boolean attribute indicates that the option is
initially selected. If the element is the descendant of
a element whose multiple attribute is not set, only one single
of this element may have the selected attribute.
Note, it talks about "initially selected" only, not real time changes. It also refers to selected only as an attribute, not a property.
The W3C spec for the option element also talks only about pre-selecting an option, not about making live changes using the selected attribute.
Once the select and options are live, the .selectedIndex property on the <select> object controls which option is selected in real time for single selection select elements.
In support of this theory, if you change to setting the saved item with .selectedIndex as shown below, the problem goes away:
$('select').focus(function() {
var $this = $(this);
if (!$this.data('hasitems')) {
var selectedValue = $this.val();
$this.empty()
.append($('<option></option')
.attr('value','')
.text('Loading...'));
// This would usually be the result of an AJAX call
$this.empty()
.data('hasitems','true');
$.each(['a','b','c','d'], function(index,item) {
$this.append($('<option></option>')
.val(item)
.text(item));
if (selectedValue == item) {
$this.prop("selectedIndex", index);
}
});
}
});
Working demo: http://jsfiddle.net/jfriend00/4333d/
Firefox can't change the focus option when select is opening, but you can change before it open, change the event to mousedown it working Fiddle
$('select').mousedown(function() {
// ...
});
I have the following list that is present to the user
<select id="regions" multiple="multiple">
<option value='us-west' selected="selected">US West Coast</option>
<option value='us-east' selected="selected">US East Coast</option>
<option value='eur' selected="selected">Europe</option>
<option value='asia' selected="selected">Asia</option>
</select>
As shown above, all items are selected.
I was trying to implement a jquery function where if the user clicks an option on the list only the clicked item (let's say Europe) will be removed, while the rest of the options are selected. I know that this is possible by holding down "CTRL" and clicking, but is there a way where all the user has to do is to click?
I tried this function:
<script type="text/javascript">
function(){
$("#regions options:selected").removeAttr("selected");
};
</script>
So it took me a while, but I think I have the functionality you're looking for.
Check this fiddle
The idea is, create an array to keep track of what's selected. The natural behaviour of a multiple select box if you only click, is to deselect all other selections and select what you clicked on.
Therefore, instead of being concerned with what's in the selectbox, we see which option is clicked on (on mousedown) and add it to our personal list of what's selected, then update the select box to reflect whats in our list (on mouseup).
This prevents the user from dragging to select multiple items in the selectbox, but being able to do that would probably just mess it up.
var selection = [];
$('#the_box > option').mousedown(function(){
if(selection.length < 1){
selection.push($(this).val());
} else {
var found = 0;
for(i = 0; i < selection.length; i++) {
if(selection[i] == $(this).val()){
selection.splice(i, 1);
found = 1;
break;
}
}
if(found === 0){
selection.push($(this).val());
}
}
});
$('#the_box').bind('mouseup mouseout', function(){
$(this).children('option').each(function(){
$(this).removeAttr('selected');
});
$(this).children('option').each(function(){
for(i = 0; i < selection.length; i++){
if($(this).val() == selection[i]){
$(this).attr('selected','selected');
}
}
});
});
You would however, be able to do this exact thing with just a list or set of divs, instead of using the native <select> tags, which would actually be easier if you're using this method (due to not having to work around the default behavior of the selectbox) :P
You can use:
$('option').click(function(){
$(this).removeAttr('selected', 'selected')
.siblings().attr('selected', 'selected');
});
What this function does, is to remove current selection state of the clicked item, but re-selecting the entire siblings set.
See this fiddle.
Update: Since by using keyboard, we can send another signal (another flag) to the control, we still need to simulate it somehow using mouse. We can for example assume that right-click is like Ctrl+click and left click is simply a click. However, right-click is not a good pattern in web and not that common.
Another option could be to redefine our business to make the control behave differently (for example, on mouse click, if the object is already selected, then deselect it, and if it's not selected, add it to the whole collection of selected options). But the disadvantage of this approach is that users get lost when using your control, because it gets far from normal HTML controls with which they have interaction on a daily basis. In other words, changing the behavior of a control is not a good solution regarding UX.
This is by no means the perfect solution but you are welcome to try it.
To attach the function (note that this allows a callback function to be fired after selecting items .. for example if you want to populate another list based on the selection)
$('#mySelect').click(function () {
singleClickList(this, myCallbackFunction);
});
The function:
function singleClickList(mythis, callback) {
var selected = $(mythis).data('selected');
if (selected == undefined) selected = [];
var scrollTop = mythis.scrollTop;
var current = $(mythis).val();
var index;
if (current != null) {
if (current.length > 0) {
index = $.inArray(current[0], selected);
if (index >= 0)
selected.splice(index, 1);
else
selected.push(current[0]);
}
$(mythis).val(selected);
$(mythis).data('selected', selected);
mythis.scrollTop = scrollTop;
if (callback) callback();
}
}
Note that this requires jquery, and makes use of the jquery .data method to store the array of selected items.