jQuery UI autocomplete - matched option not getting selected on focusOut - javascript

I have made a jQuery UI - autocomplete in which I have added the following conditions:
Sorted option list
mandatory select an option
Here I made a mistake that, when I type the exact option, it is not getting selected.
For example:
var json = ["JOAQUIN", "BERNARDINO", "MODOC", "ALASKA", "MADERA", "ANDERSON",
"Kingston", "Kala", "Aka"];
I type aka, it is not accepting.
I also tried Aka, still it is not accepting.
How to reproduce:
After typing the above text in textbox, just focusout - The value
will be emptied. I want to handle aka = Aka = AKA (with/without selecting from option list)
PS: I'm not asking for code, I have made a small blunder which I need it to be correct.
Here is my fiddle, where you can easily reproduce the exception.
How can I change my code to work like this. Please share your suggestions and point me in a right direction on placing the conditions in order.

What if you change the event to close instead of the change event. Then check if the value is in the array and if not clear the container. This will require you match capitalization when you enter the value. If you wanted to ignore capitalization you could do a convert the json array to lower case and the value to lowercase before search the array.
fiddle
var json = ["JOAQUIN", "BERNARDINO", "MODOC", "ALASKA", "MADERA", "ANDERSON", "Kingston", "Kala", "Aka"];
$('input').autocomplete({
source: function (request, response) {
var matches = $.map(json, function (json) {
if (json.toUpperCase().indexOf(request.term.toUpperCase()) === 0) {
return json;
}
});
response(matches);
},
close: function(event, ui) {
var index = jQuery.inArray($('input').val(), json);
if (index == -1) {
$('input').val("");
}
}
});

As far as I can see from the documentation the UI property only gets set when a dropdown item is selected (clicked on) so you will have to add to your validation in the change function to manually check against the values.

Related

Allow Only Selected Values From Dropdown List in Auto Complete Field

I need an autocomplete feature that allows a user to only type/choose from the list of values from a DB table. I do not want them to be able to enter free text after the reason is selected.
I have referred to a similar posting jQuery UI AutoComplete: Only allow selected valued from suggested list1 but I have a different issue.
The challenge is the list of acceptable options have special characters, specifically "(" and ")". When I type an option with ( I can see it in the drop down but when I use the arrow keys to select it will go to the previous option.
The array of acceptable options from PHP are accessed in javascript using
var validOptions = <?php echo json_encode($validOptionsArray); ?>;
The javascript is
previousValue = "";
$('.getValidOptions').autocomplete({
source: validOptions,
}).keyup(function() {
var isValid = false;
for (i in validOptions) {
if (validOptions[i].toLowerCase().match(this.value.toLowerCase())) {
isValid = true;
}
}
if (!isValid) {
this.value = previousValue;
} else {
previousValue = this.value;
}
});
I understand how this is happening. If I remove the line
this.value = previousValue;
it will allow me to select the option with ( but then the user can continue typing.
However, I want to keep this line as it prevents the user from adding text to the dropdown.
I can use the mouse to select the option but I get an error in the console
Uncaught SyntaxError: Invalid regular expression: /(/
So it seems the issue is related to keyup function
I have tried (1) charset="utf-8" and (2) regular expression such as
(this.value).replace(/([.*+?^=!${}()|[\]\/\\])/g, '\\$1');
Any thoughts on how to fix this?
Thanks.
The data in the json_encode($validOptionsArray) is
Array
(
[0] => NI: New Member
[1] => NI: New Member (referred from current member)
[2] => RI: Returning Member
[3] => RI: Returning Member (with conditions)
[4] => XI: Exclusive member (full/all access)
)
I decided to go with jQuery inputToken http://loopj.com/jquery-tokeninput/
It is easy to implement and allows the user to only select the available options and not edit them. Maybe others will find this useful.

Select2 Custom Matcher to keep options open if group title matches

I hope my question makes sense - wasn't sure on the best way to describe this. I have a grouped Select2 select form input something like this:
Vegetables
Lettuce
Tomatoes
Onions
Fruit
Apples
Oranges
Bananas
Spreads
Vegemite
Nutella
Peanut Butter
So you start typing App and of course you get Apples from the Select2 dropdown. If you type veg you get Vegemite and the Vegetables group heading but all the options are hidden. I would like to keep all the group options visible if a search term matches the group heading.
I did some digging in the select2 source code and I think it's actually easy but I could be wrong and if I am right I am stuck on how to make it work. Here is the source code:
https://github.com/select2/select2/blob/81a4a68b113e0d3e0fb1d0f8b1c33ae1b48ba04f/src/js/select2/defaults.js:
and a Gist I created vs. trying to paste it in here:
https://gist.github.com/jasper502/40b810e55b2195476342
I switched the order of the code and made some slight variable name changes to reflect this. I think this would keep the option group open. I tried to make a custom matcher based on this (see my Gist) but I was stuck at the point where it calls DIACRITICS:
https://github.com/select2/select2/blob/8ad8f200ba8c9429d636453b8ee3bcf593e8c87a/src/js/select2/diacritics.js
After some Googling I realized that this is replacing accented characters which I know I don't have so I removed that portion.
Now my matcher fails with TypeError: data.indexOf is not a function. (In 'data.indexOf(term)', 'data.indexOf' is undefined)
errors in my browser.
I am sure I am very close here but I am a bit over my head and beyond my experience and/or skill level to finish this off. Any pointers or ideas would be appreciated.
UPDATE
Here is a JSfiddle with what I am working with:
https://jsfiddle.net/jasper502/xfw4tmbx/9/
What I gather from your question is you want to be able to show options for selection when there's a match in either the option text OR the option's parent optgroup value attribute.
This is relatively straightforward: Mainly, look at both of the values and if either matches, return true using Select2's matcher option:
(Note: Using Select2 v3.5.4.)
(function() {
function matcher(term, text, opt) {
var $option = $(opt),
$optgroup = $option.parent('optgroup'),
label = $optgroup.attr('label');
term = term.toUpperCase();
text = text.toUpperCase();
if (text.indexOf(term) > -1
|| (label !== undefined
&& label.toUpperCase().indexOf(term) > -1)) {
return true;
}
return false;
}
$(".select2").select2({
matcher: matcher
});
})();
https://jsfiddle.net/xfw4tmbx/2/
v4.* and above changed the term and text to a more complex object, so it'll be slightly different, but the main concept is the same. As you can see, all I'm doing is using jQuery to select up to the option's parent if it's an optgroup element and including that in the matcher check.
Also, an optgroup will display if any of it's children are shown, so you only have to worry about displaying one or more of the option's, and not actually "show" the optgroup by manually showing it.
If you have a different requirement, please provide a (working/non-working?) demonstration fiddle showing what you have where we can actually run it.
EDIT
Select2 custom matching changed significantly with the 4.0 release. Here is a custom matcher that was posted to this GitHub issue. It is reproduced as-is below for completeness.
Notice that it's calling itself for child elements (the option elements within the optgroup elements), so modelMatcher() is running against both the optgroup and the option elements, but the combined set is returned after removing the optgroup and option elements that don't match. In the version above, you got every option element and simply returned true/false if you wanted it (and the parent) displayed. Not that much more complicated, but you do have to think about it a little bit more.
(function() {
function modelMatcher(params, data) {
data.parentText = data.parentText || "";
// Always return the object if there is nothing to compare
if ($.trim(params.term) === '') {
return data;
}
// Do a recursive check for options with children
if (data.children && data.children.length > 0) {
// Clone the data object if there are children
// This is required as we modify the object to remove any non-matches
var match = $.extend(true, {}, data);
// Check each child of the option
for (var c = data.children.length - 1; c >= 0; c--) {
var child = data.children[c];
child.parentText += data.parentText + " " + data.text;
var matches = modelMatcher(params, child);
// If there wasn't a match, remove the object in the array
if (matches == null) {
match.children.splice(c, 1);
}
}
// If any children matched, return the new object
if (match.children.length > 0) {
return match;
}
// If there were no matching children, check just the plain object
return modelMatcher(params, match);
}
// If the typed-in term matches the text of this term, or the text from any
// parent term, then it's a match.
var original = (data.parentText + ' ' + data.text).toUpperCase();
var term = params.term.toUpperCase();
// Check if the text contains the term
if (original.indexOf(term) > -1) {
return data;
}
// If it doesn't contain the term, don't return anything
return null;
}
$(".select2").select2({
matcher: modelMatcher
});
})();
https://jsfiddle.net/xfw4tmbx/16/

Twitter Typeahead differentiate between events

I'm designing a system that allows users to annotate elements with either strings or terms from a vocabulary. I use Twitter typeahead for autocompletion and want to distinguish between an entered string and a term.
I am not able to figure out how to differentiate between the following situations:
a user pressing enter after coming up with its own value
a user pressing a down (or up) arrow (thereby selecting an autocompletion option) and pressing enter
The event listeners I wrote:
$("#itemInp").on('typeahead:select', function(event, term) {
console.log("save: term", term);
});
$("#itemInp").on('keyup', function(event) {
if(event.which == 13) {
var string = $("#itemInp").val();
console.log("save: string", string);
}
});
With the following HTML:
<input id="itemInp"><input>
The first listener catches all selected typeahead terms, allowing for proper saving of the term. Problem is, the second listener is also triggered in case a user presses down and enter, selecting a typeahead term, which is now also saved as a plain string. Is there a way to not trigger the second listener in case a typeahead suggestion is selected?
A fiddle with the code loaded:
https://jsfiddle.net/zban3vs6/1/
Edit:
I considered a number of hacks, but all come with their own problems:
Add a special character to the string shown in input at the moment it is selected, by adding custom Typeahead display: https://jsfiddle.net/2t9rzhwf/
This causes an additional character to be introduced, which is troublesome if a user presses the down arrow without pressing enter.
Attempt at sequencing the listeners, have the term listener trigger first, set a boolean to true, and filter on this boolean in the second listener. Don't like this way of filtering, since it introduces a delay.
The solution provided by #peter-clause, checking if the selected value is in the list of available items. But that way I can not keep track of whether the user intended to use the autocomplete option to select a term or explicitly is adding a plain string.
I had a similar problem. This is how I solved it.
var wasSelected = false;
$("#itemInp").on('typeahead:select', function(event, term) {
wasSelected = true;
console.log("save: term", term);
});
$("#itemInp").on('change', function(event) {
if(!wasSelected) {
var string = $("#itemInp").val();
console.log("save: string", string);
}
wasSelected = false;
});
A rather hacky solution, but you could check if the selected value is in the list of available items.
if (states.indexOf(string) == -1) {
//...
}
See https://jsfiddle.net/zban3vs6/2/

Generic way to get the value of an input regardless of type

I'm trying to write a generic function I can use with the jquery validation plugin that will make a field required based on the value of another field. Here's what I want to happen:
If Field 1's value is in a specified array of values (currently testing with "No", "n/a", and "0"), or is empty, do nothing. Otherwise, make Field 2 required.
Getting the value of Field 1 is the issue. I had no problem figuring this out with a text-type or <select> input, but I'm trying to get it to work with radios and having difficulty. Here is an excerpt from my code:
var value = $('[name="option"]').val(),
empty = ['no', '', 'n/a', '0'];
// If the input is not "empty", make the additional field required
if ($.inArray(value.toLowerCase(), empty) < 0) { // returns -1 if not found
required = true;
} else {
required = false;
}
This works for everything I need it to except radios, because it seems to read the value of the first radio, regardless of if it was checked or not.
The field that will trigger the "required" will either be one of the various text inputs (text, url, email, etc.), a <select>, or a single choice set of radios. No multiple choice. I'll be passing this function as a parameter to required in my jquery validation config for each field I want it to apply to. The name attribute of the "other" field that gets evaluated will be available to it.
Here's my demo so far, kind of ugly but there are notes: http://jsfiddle.net/uUdX2/3/
I've tried a bunch of different ways using is(':checked') and the :checked selector, but they have all failed. I removed them from the demo because they didn't work.
What do I need to get this working with radios and text-type or select inputs, without knowing which kind of input it will be?
Try this
var value = $('[name="option"]');
var type = value.attr("type");
if(type && type.toLowerCase() == 'radio')
value = value.filter(":checked").val();
else
value = value.val();
Working demo
Something like this:
var value = $('[type="radio"][name="option"]:checked, [type!="radio"][name="option"]', form).val() || '0'
Quite similar to Shankar's but does it all in the selector.
http://jsfiddle.net/infernalbadger/uUdX2/8/
It's not working when nothing is selected. Not sure what you want it to do when that happens?

Check all values in an array are same

I have a webpage that has a textbox.
When the user enters information into it, it makes a AJAX call to see if the entry is valid, if not it disables a button.
They can also add up to 10 textboxes which is done via jQuery Templates. At the moment each textbox has a class of serial and when a serial textbox is blurred it does this check.
If they enter a invalid serial it will disable the button but if they add a new textbox and that is valid the button is now enabled which is wrong as there is one still invalid.
The only way I can think to do this is to add a 1 or 0 to an array for each textbox and once all elements in the array are 1 then enable the button. Is that a good approach, if not please explain a better one. If it is a good approach how do I check all values in a javascript array are the same?
Thanks
This sounds like a good approach. You can check for equal elements in a javascript array using this simple javascript function. You may paste this to a firebug console to check its functionality.
// check if all elements of my_array are equal, my_array needs to be an array
function check_for_equal_array_elements(my_array){
if (my_array.length == 1 || my_array.length == 0) {
return true;
}
for (i=0;i<my_array.length;i++){
if (i > 0 && my_array[i] != my_array[i-1]) {
return false;
}
}
return true;
}
//Example:
var my_array = [];
my_array.push(5);
my_array.push(5);
// will alert "true"
alert("all elements equal? "+check_for_equal_array_elements(my_array));
my_array.push(6);
// will alert "false"
alert("all elements equal? "+check_for_equal_array_elements(my_array));
I will assume you have a isValid(str) function that returns a boolean.
Since you're using jQuery, you can take advantage of jQuery's filter() function to easily check if any inputs are invalid whenever an input blurs:
$('.serial').live('blur', function () {
// Get an array of all invalid inputs
var invalids = $('.serial').filter(function () {
return !isValid($(this).val());
});
// Does the array contain anything?
$('#button').prop('disabled', invalids.length);
});
Demo: http://jsfiddle.net/3RNV6/
Similar concept, but for use with AJAX:
$('.serial').live('blur', function () {
var me = this;
$.ajax({
// ajax config
success: function (data) {
if (data === 'Y') $(me).addClass('valid');
// Get an array of all invalid inputs
var invalids = $('.serial').filter(function () {
return !$(this).hasClass('valid');
});
// Enable if none invalid
if (invalids.length === 0) $('#button').prop('disabled', false);
}
});
});
$('.serial').live('keypress', function () {
$('#button').prop('disabled', true);
$(this).removeClass('valid');
});
First of if you dynamically create n textboxes you should use live() or delegate() methods of jQuery to inform of new DOM elements added.
Secondly your approach is just fine but instead of an array you can set param of inputs with wrong text and then disable button if there are any elements with wrong text. I think it will be faster than looping though all textboxes all over.
I would use validation to achieve this.
http://docs.jquery.com/Plugins/Validation#Demos
If you can validate client-side great - either use one of the existing jQuery validation functions shown in the link above, or write your own.
If you must validate server side via ajax, then you could build this into a custom validation routine.
Then in the call that shows/hides the button - make a call to $('#formid).validate() - returns false if any validation fails.

Categories

Resources