How do I add a preview text to my custom built autocomplete?
I was building an autocomplete based on this example and the solution provided by Alexander Poshtaruk
helped me. But there was some minor glitch that happened when the user typed in a small case.
The labels didn't align. To fix this I added a line
labelElem.html(text ? $(this).val() + text.slice(inLength) : '')
This will trim the characters already typed by the user and show the remaining.
This might help anyone else who might be wondering how to do it if facing the same scenario.
I also added code to remove the placeholder on string deletion.
var data = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Carolina',
'North Dakota',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming',
]
var searchData = []
function searchMe(string) {
searchData = []
data.forEach(item => {
if (item.match(new RegExp(string, 'i')))
searchData.push(highlight(item, string))
})
$('.list-group').html("<div class='loader'></div>")
$('.list-group').html('')
if (searchData.length)
searchData.forEach(item =>
$('.list-group').append(
' <a href="#" class="list-group-item list-group-item-action ">' +
item +
'</a>'
)
)
else {
$('.list-group').html('<h4>No results found</h4>')
}
}
function highlight(text, str) {
return text.replace(
new RegExp('(^|)(' + str + ')(|$)', 'ig'),
'$1<b>$2</b>$3'
)
}
$(function() {
let input
//enable or disable the autocomplete
$('div')
.on('mouseenter', '.list-group a', function() {
console.log('mousein')
$('.list-group a.active').removeClass('active')
$(this).addClass('active')
})
.on('mouseleave', '.list-group', function() {
$('.list-group a.active').removeClass('active')
})
$(document).on('click', '.list-group a', function() {
if (input) {
input.val($(this).text())
}
$('.list-group').remove()
})
//autocomplete scroll and action on key up
$('input.my').on('keyup', function(e) {
console.log('From key up')
console.log(e.which)
let inLength = $(this).val().length
console.log(e.key)
let labelElem = $('.label-text')
console.log('labelElem:', labelElem)
if (
(e.key <= 'z' && e.key >= 'a') ||
(e.key >= 'A' && e.key <= 'Z' && e.key.length === 1) ||
e.which === 229
) {
if (!$('.list-group').length) {
$(this).after('<div class="list-group"> </div>')
}
setTimeout(searchMe, 0, $(this).val())
labelElem.html('')
// $('.list-group').html(
//
// //' <div ><h6 class="text-center text-secondary">Loading...</h6><div class="loader"></div></div> '
// ' <a href="#" class="list-group-item list-group-item-action ">Hari </a> <a href="#" class="list-group-item list-group-item-action">Dapibus ac facilisis in</a> <a href="#" class="list-group-item list-group-item-action">Subesh</a> <a href="#" class="list-group-item list-group-item-action">Porta ac consectetur</a> <a href="#" class="list-group-item list-group-item-action disabled">Vestibulum at eros</a> <a href="#" class="list-group-item list-group-item-action ">Vestibulum at eros</a>'
// );
let width = parseInt($(this).css('width'))
if (width > parseInt($('.list-group').css('width'))) {
console.log('width Changed: ' + width)
$('.list-group').css('width', width)
} else {
console.log(width)
console.log($('.list-group').css('width'))
}
console.log('Added list')
} else if ($('.list-group').length) {
//down press
if (e.which === 40) {
if (!$('.list-group a.active').length)
$('.list-group')
.find('a')
.eq(0)
.addClass('active')
else if ($('.list-group a.active').next().length) {
$('.list-group a.active')
.removeClass('active')
.next()
.addClass('active')
} else {
console.log('Enter')
$('.list-group a.active')
.removeClass('active')
.parent()
.children('a')
.eq(0)
.addClass('active')
$('.list-group').scrollTop($('.list-group a.active').get(0).offsetTop)
}
let text = $('.list-group a.active').text()
labelElem.html(text ? $(this).val() + text.slice(inLength) : '')
}
//up press
else if (e.which === 38) {
if (!$('.list-group a.active').length) {
$('.list-group')
.find('a')
.eq(0)
.addClass('active')
let text = $('.list-group a.active').text()
$('label').html(text)
labelElem.html(text ? $(this).val() + text.slice(inLength) : '')
} else if ($('.list-group a.active').prev().length) {
$('.list-group a.active')
.removeClass('active')
.prev()
.addClass('active')
let text = $('.list-group a.active').text()
$('label').html(text)
labelElem.html(text ? $(this).val() + text.slice(inLength) : '')
} else {
$('.list-group').scrollTop($('.list-group a.active').get(0).offsetTop)
$('.list-group a.active').removeClass('active')
}
}
//enter or right keyPressed()
else if (e.which === 39 || e.which === 13) {
if ($('.list-group a.active').length)
var value = $('.list-group a.active').text()
$(this).val(value)
console.log('value for aoutocomplete:', value)
$('.list-group ').remove()
}
//bacspace
else if (e.which === 8) {
//alert('Subesh');
if ($(this).val() === '') {
labelElem.html("")
$('.list-group ').remove()
}
} else if (e.which === 27) {
$('.list-group').remove()
}
//set the scroll pos
if ($('.list-group a.active').prev().length)
$('.list-group').scrollTop(
$('.list-group a.active')
.prev()
.get(0).offsetTop
)
} else {
console.log('No list')
}
})
$('input.my').on('focusin', function(e) {
if ($(this) != input) {
$('.list-group ').remove()
input = $(this)
}
})
$(':not(input)').on('click', function() {
$('.list-group ').remove()
})
//keydown operation
$('input.my').on('keydown', function(e) {
console.log('From Down')
console.log(e.which)
let lastChar = $(this).val().length
//tab
if (e.which === 9) {
if ($('.list-group').length) {
if ($('.list-group a.active').length) {
$(this).val($('.list-group a.active').text())
$('.list-group ').remove()
} else {
$('.list-group')
.find('a')
.eq(0)
.addClass('active')
e.preventDefault()
e.stopPropagation()
}
}
}
//up
else if (e.which === 38) {
e.preventDefault()
e.stopPropagation()
}
})
})
.list-group {
margin-top: 10px;
position: fixed;
border-radius: 2px;
max-height: 200px;
background: rgb(255, 255, 255);
overflow-y: auto;
overflow-x: hidden;
border: 1px solid rgb(238, 238, 238);
box-shadow: 0px 10px 10px 2px #cbc8c8;
padding: 4px 2px;
z-index: 10;
}
.list-group::-webkit-scrollbar-track {
border-radius: 2px;
background-color: #bbbbbb;
}
.list-group::-webkit-scrollbar {
width: 5px;
}
.list-group::-webkit-scrollbar-thumb {
border-radius: 10px;
background-color: #6aa1ff;
}
.label-text {
position: absolute;
top:0;
left: 0;
right: 0;
bottom: 0;
border: 1px solid red;
margin: 0 15px;
padding: .375rem .75rem;
color: silver;
z-index: -1;
}
.my {
z-index: 2;
background-color: transparent !important;
}
/*
* STYLE 2
*/
.loader {
border: 2px solid #f3f3f3;
border-radius: 50%;
border-top: 2px solid #3498db;
width: 30px;
height: 30px;
margin: 20px auto;
-webkit-animation: spin 2s linear infinite;
/* Safari */
animation: spin 2s linear infinite;
}
/* Safari */
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
rgb(215, 215, 215) 100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!-- Popper JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.6/umd/popper.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js"></script>
</head>
<body>
<div style="height:20px;"></div>
<div class="container">
<div class="form-group col-6" style="margin: 0px auto">
<span>
<input type="text" name="" value="" placeholder="Search" class="form-control my">
<div class="label-text"></div>
</span>
</div>
<!-- <div class="list-group"> <a href="#" class="list-group-item list-group-item-action active"> Cras justo odio </a> <a href="#" class="list-group-item list-group-item-action">Dapibus ac facilisis in</a> <a href="#" class="list-group-item list-group-item-action">Morbi leo risus</a> <a href="#" class="list-group-item list-group-item-action">Porta ac consectetur</a> <a href="#" class="list-group-item list-group-item-action disabled">Vestibulum at eros</a> <a href="#" class="list-group-itemlist-group-item-action ">Vestibulum at eros</a></div> -->
</body>
</html>
Tried to absolutely position label element, and seems good for me. Of cause I didn't put code for mouse events and for words in list which start with another letter but contain sequence, but i guess the idea is clear enough (tested in Chrome):
var data = [
'Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California',
'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii',
'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana',
'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota',
'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire',
'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota',
'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island',
'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont',
'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'
];
var searchData = [];
function searchMe(string) {
console.log("why");
searchData = [];
data.forEach((item) => {
if (item.match(new RegExp(string, 'i')))
searchData.push(highlight(item, string));
});
$('.list-group').html("<div class='loader'></div>");
$('.list-group').html("");
if(searchData.length)
searchData.forEach(
(item) => $('.list-group').append(' <a href="#" class="list-group-item list-group-item-action ">'+item+'</a>')
);
else {
$('.list-group').html("<h4>No results found</h4>");
}
}
function highlight(text, str) {
return text.replace(new RegExp('(^|)(' + str + ')(|$)','ig'), '$1<b>$2</b>$3');
}
$(function() {
let input;
//enable or disable the autocomplete
$('div').on('mouseenter', '.list-group a', function() {
console.log('mousein');
$('.list-group a.active').removeClass('active');
$(this).addClass('active');
}).on('mouseleave', '.list-group', function() {
$('.list-group a.active').removeClass('active');
});
$(document).on('click', '.list-group a', function() {
if (input) {
input.val($(this).text());
}
$('.list-group').remove();
});
//autocomplete scroll and action on key up
$('input.my').on('keyup', function(e) {
console.log('From key up');
console.log(e.which);
let inLength = $(this).val().length;
console.log(e.key);
let labelElem = $('.label-text');
console.log('labelElem:', labelElem);
if (((e.key <= 'z' && e.key >= 'a') || (e.key >= 'A' && e.key <= 'Z') && e.key.length === 1) || e.which === 229) {
if(!$('.list-group').length) {
$(this).after(
'<div class="list-group"> </div>'
);
}
setTimeout(searchMe, 0,$(this).val());
// $('.list-group').html(
//
// //' <div ><h6 class="text-center text-secondary">Loading...</h6><div class="loader"></div></div> '
// ' <a href="#" class="list-group-item list-group-item-action ">Hari </a> <a href="#" class="list-group-item list-group-item-action">Dapibus ac facilisis in</a> <a href="#" class="list-group-item list-group-item-action">Subesh</a> <a href="#" class="list-group-item list-group-item-action">Porta ac consectetur</a> <a href="#" class="list-group-item list-group-item-action disabled">Vestibulum at eros</a> <a href="#" class="list-group-item list-group-item-action ">Vestibulum at eros</a>'
// );
let width = parseInt($(this).css('width'));
if (width > parseInt($('.list-group').css('width'))) {
console.log('width Changed: ' + width);
$('.list-group').css('width', width);
} else {
console.log(width);
console.log($('.list-group').css('width'));
}
console.log("Added list");
} else if ($('.list-group').length) {
//down press
if (e.which === 40) {
if (!$('.list-group a.active').length)
$('.list-group').find('a').eq(0).addClass('active');
else if ($('.list-group a.active').next().length) {
$('.list-group a.active').removeClass('active').next().addClass('active');
} else {
console.log("Enter");
$('.list-group a.active').removeClass('active').parent().children('a').eq(0).addClass('active');
$('.list-group').scrollTop($('.list-group a.active').get(0).offsetTop);
}
let text = $('.list-group a.active').text();
labelElem.html(text);
}
//up press
else if (e.which === 38) {
if (!$('.list-group a.active').length) {
$('.list-group').find('a').eq(0).addClass('active');
let text = $('.list-group a.active').text();
$('label').html(text);
labelElem.html(text);
} else if ($('.list-group a.active').prev().length) {
$('.list-group a.active').removeClass('active').prev().addClass('active');
let text = $('.list-group a.active').text();
$('label').html(text);
labelElem.html(text);
} else {
$('.list-group').scrollTop($('.list-group a.active').get(0).offsetTop);
$('.list-group a.active').removeClass('active');
}
}
//enter or right keyPressed()
else if (e.which === 39 || e.which === 13) {
if ($('.list-group a.active').length)
var value = $('.list-group a.active').text();
$(this).val(value);
console.log('value for aoutocomplete:', value)
$('.list-group ').remove();
}
//bacspace
else if (e.which === 8) {
//alert('Subesh');
if ($(this).val() === "")
$('.list-group ').remove();
} else if (e.which === 27) {
$('.list-group').remove();
}
//set the scroll pos
if ($('.list-group a.active').prev().length)
$('.list-group').scrollTop($('.list-group a.active').prev().get(0).offsetTop);
} else {
console.log('No list');
}
});
$('input.my').on('focusin', function(e) {
if ($(this) != input) {
$('.list-group ').remove();
input = $(this);
}
});
$(':not(input)').on('click', function() {
$('.list-group ').remove();
});
//keydown operation
$('input.my').on('keydown', function(e) {
console.log('From Down');
console.log(e.which);
let lastChar = $(this).val().length;
//tab
if (e.which === 9) {
if ($('.list-group').length) {
if ($('.list-group a.active').length) {
$(this).val($('.list-group a.active').text());
$('.list-group ').remove();
} else {
$('.list-group').find('a').eq(0).addClass('active');
e.preventDefault();
e.stopPropagation();
}
}
}
//up
else if (e.which === 38) {
e.preventDefault();
e.stopPropagation();
}
});
});
.list-group {
margin-top: 10px;
position: fixed;
border-radius: 2px;
max-height: 200px;
background: rgb(255, 255, 255);
overflow-y: auto;
overflow-x: hidden;
border: 1px solid rgb(238, 238, 238);
box-shadow: 0px 10px 10px 2px #cbc8c8;
padding: 4px 2px;
z-index: 10;
}
.list-group::-webkit-scrollbar-track {
border-radius: 2px;
background-color: #bbbbbb;
}
.list-group::-webkit-scrollbar {
width: 5px;
}
.list-group::-webkit-scrollbar-thumb {
border-radius: 10px;
background-color: #6aa1ff;
}
.label-text {
position: absolute;
top:0;
left: 0;
right: 0;
bottom: 0;
border: 1px solid red;
margin: 0 15px;
padding: .375rem .75rem;
color: silver;
z-index: -1;
}
.my {
z-index: 2;
background-color: transparent !important;
}
/*
* STYLE 2
*/
.loader {
border: 2px solid #f3f3f3;
border-radius: 50%;
border-top: 2px solid #3498db;
width: 30px;
height: 30px;
margin: 20px auto;
-webkit-animation: spin 2s linear infinite;
/* Safari */
animation: spin 2s linear infinite;
}
/* Safari */
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
rgb(215, 215, 215) 100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!-- Popper JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.6/umd/popper.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js"></script>
</head>
<body>
<div style="height:20px;"></div>
<div class="container">
<div class="form-group col-6" style="margin: 0px auto">
<span>
<input type="text" name="" value="" placeholder="Search" class="form-control my">
<div class="label-text">Alabama</div>
</span>
</div>
<!-- <div class="list-group"> <a href="#" class="list-group-item list-group-item-action active"> Cras justo odio </a> <a href="#" class="list-group-item list-group-item-action">Dapibus ac facilisis in</a> <a href="#" class="list-group-item list-group-item-action">Morbi leo risus</a> <a href="#" class="list-group-item list-group-item-action">Porta ac consectetur</a> <a href="#" class="list-group-item list-group-item-action disabled">Vestibulum at eros</a> <a href="#" class="list-group-itemlist-group-item-action ">Vestibulum at eros</a></div> -->
</body>
</html>
Instead of a label, use another input text box.
With css, position the helper input box behind the main one. With extra css, make the main box (the one in front) have a transparent background.
Then you can set the value (or placeholder text) of the helper box (the one behind) with the full value of the top selection in the list as required.
<input type="text" id="searchhelper" name="searchhelper" value="" placeholder="Search" class="form-control my" style="color:#dddddd;margin-bottom:-38px;">
** NB: it would also be necessary to restructure the helper text based on what the user types since they may change the case (upper/lower) of the result text. So basically, letters of what user types + leftover letters of helper word(s)
Or, to simplify, make all text uppercase with css text-transform: uppercase;
(if lower case is not absolutely required)