I know this question has been asked before, but I wasn't able to find any answers that are up to date or functional (at least for my application).
My JQuery autocomplete box is using a mysql database as its source. I want the user to be able to type to get recommendations, but then is forced to select from the dropdown choices before they can submit the form.
My Javascript:
<script type="text/javascript">
$.widget( 'ui.autocomplete', $.ui.autocomplete, {
_renderMenu: function( ul, items ) {
var that = this;
$.ui.autocomplete.currentItems = items;
$.each( items, function( index, item ) {
that._renderItemData( ul, item );
});
}
});
$.ui.autocomplete.currentItems = [];
$(function() {
$("#college").autocomplete({
source: "search.php",
minLength: 5
});
});
var inputs = {college: false};
$('#college').change(function(){
var id = this.id;
inputs[id] = false;
var length = $.ui.autocomplete.currentItems.length;
for(var i=0; i<length; i++){
if($(this).val() == $.ui.autocomplete.currentItems[i].value){
inputs[id] = true;
}
}
});
$('#submit').click(function(){
for(input in inputs){
if(inputs.hasOwnProperty(input) && inputs[input] == false){
alert('incorrect');
return false;
}
}
alert('correct');
$('#college_select_form').submit();
});
</script>
My form:
<form action="choose.php" method="post" id="college_select_form" name="college_select_form">
<input type="text" id="college" name="college" class="entry_field" value="Type your school" onclick="this.value='';" onfocus="this.select()" onblur="this.value=!this.value?'Type your school':this.value;" /><input type="submit" id="submit" name="submit" class="submitButton" value="Go" title="Click to select school" />
</form>
Search.php:
<?php
try {
$conn = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
}
catch(PDOException $e) {
echo $e->getMessage();
}
$return_arr = array();
if ($conn)
{
$ac_term = "%".$_GET['term']."%";
$query = "SELECT * FROM college_list where name like :term";
$result = $conn->prepare($query);
$result->bindValue(":term",$ac_term);
$result->execute();
/* Retrieve and store in array the results of the query.*/
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
array_push($return_arr, array('label' => $row['name'], 'value' => $row['name']));
}
}
/* Free connection resources. */
//$conn = null;
/* Toss back results as json encoded array. */
echo json_encode($return_arr);
?>
So what would be the best approach to doing this? The only solution I can think of is using PHP to verify that the textbox's value matches a value in the database, but I'm not sure how to implement that with my current code.
You should always check it in "choose.php" (server-side) since the user can disable the JavaScript and post whatever they want in the inputs of your form
$college = mysql_real_escape_string($_GET['college']);
if ($college != "" || $college != null || $college != -1)
{
//DO STUFF
}
NOTE: YOU SHOULD ALWAYS USE "mysql_real_escape_string" to prevent SQL Injection! more info: http://www.tizag.com/mysqlTutorial/mysql-php-sql-injection.php
So accordingly in search.php change the
$ac_term = "%".$_GET['term']."%";
to
$ac_term = "%". mysql_real_escape_string($_GET['term']) ."%";
You can also check the form before the user submit to just make it more user friendly (users don't want to wait couple of seconds for the page to gets refreshed with errors on it!)
so maybe something like this would help: Submit Event Listener for a form
function evtSubmit(e) {
// code
e.preventDefault();
// CHECK IT HERE!
};
var myform = document.myForm;
myform.setAttribute('action', 'javascript:evtSubmit();');
In my project i handled it by checking on focus-out , if the text entered in the autocomplete field actually matches my dropdown options.If not i will simply remove it.
change: function(event, ui) {
if (!ui.item) {
this.value = '';
}
}
See my full example here-Jquery auto comp example
it has an embeded fiddle,you can check the fiddle directly also
Add this code to your JavaScript before you instantiate your autocomplete object:
$.widget( 'ui.autocomplete', $.ui.autocomplete, {
_renderMenu: function( ul, items ) {
var that = this;
$.ui.autocomplete.currentItems = items;
$.each( items, function( index, item ) {
that._renderItemData( ul, item );
});
}
});
$.ui.autocomplete.currentItems = [];
This will make it so whenever the menu appears, you have a list of current items the user can choose from stored in $.ui.autocomplete.currentItems
. You can then use that to check against when you are submitting your form. Of course the way you implement this part is up to you depending on how dynamic your form is, but here is an example that requires hard-coding a list of input fields and making sure they all have ids.
//create an object that contains every input's id with a starting value of false
var inputs = {college: false};
//for each input, you will have a function that updates your 'inputs' object
//as long as all inputs have id's and they all are using autocomplete,
//the first line could be written as: $('input').change(function(){ and the
//function would only need to be written once. It is easier to maintain
//if you use seperate id's though like so:
$('#college').change(function(){
var id = this.id;
inputs[id] = false;
var length = $.ui.autocomplete.currentItems.length;
for(var i=0; i<length; i++){
if($(this).val() == $.ui.autocomplete.currentItems[i].value){
inputs[id] = true;
}
}
});
//when you submit, check that your inputs are all marked as true
$('#submit').click(function(){
for(input in inputs){
if(inputs.hasOwnProperty(input) && inputs[input] == false){
return false; //one or more input does not have correct value
}
}
//all inputs have a value generated from search.php
$('#myform').submit();
});
The only difference between our two examples (one that works and one that doesn't) is that you are binding other events to your input element, onclick
and onblur
. So by changing our listener from change
to blur
as well mostly fixes the problem. But it creates a new problem when the enter/return key is pressed to submit the form. So if we add a listener for that specific event then everything works out ok. Here is what the code looks like now:
var validateInfo = function(elem){
var id = elem.id;
inputs[id] = false;
var length = $.ui.autocomplete.currentItems.length;
for(var i=0; i<length; i++){
if($(elem).val() == $.ui.autocomplete.currentItems[i].value){
inputs[id] = true;
}
}
}
$('#college').on('blur', function(){
validateInfo(this);
}).on('keydown', function(e){
if(e.which == 13){ //Enter key pressed
validateInfo(this);
}
});
As this is javascript, your only concern should be if an item is selected from the autocomplete list. This can simply be done by setting a variable to true on select and false on change. That is enough to prevent regular users from continuing without selecting a school. To prevent abuse you need to check the value server side after posting. All normal user will pass that check.
If I understand the question correctly, this is something I have encountered before. Here is some code pretty much lifted straight out of another project. I have used a local datasource here but the project this is lifted from uses remote data so there won't be a difference:
var valueSelected = '';
$('#college').autocomplete({
source: ['collegeA', 'collegeB', 'collegeC']
}).on('autocompletechange autocompleteselect', function (event, ui) {
if (!ui.item) {
valueSelected = '';
} else {
$('#submit').prop('disabled', false);
valueSelected = ui.item.label;
}
}).on('propertychange input keyup cut paste', function () {
if ($(this).val() != valueSelected) {
valueSelected = '';
}
$('#submit').prop('disabled', !valueSelected);
});
This will programatically enable and disable the submit button depending on whether a value has been selected by the user.
Add a hidden input element to your form:
<input type="hidden" name="selectedvalue" id="selectedvalue" />
Add a select event handler to your autocomplete, that copies the selected value to the hidden input:
$("#college").autocomplete({
source: "search.php",
minLength: 5,
select: function (event, ui) {
$('#selectedvalue').val(ui.item.value);
}
});
Then just ignore the auto-complete form input in posted data.