I have a page where i will have keyup autocomplete search functionality. using arrow navigation i need to scroll down till end. how to add scroll tp function in jquery
Please check with autocomplete
https://jsfiddle.net/os5ko0v8/2/
$(document).ready(function(){
var people = ['Peter Bishop', 'Nicholas Brody', 'Gregory House', 'Hank Lawson', 'Tyrion Lannister', 'Nucky Thompson'];
var cache = {};
var drew = false;
$("#search").on("keyup", function(event){
var query = $("#search").val()
if($("#search").val().length > 2){
//Check if we've searched for this term before
if(query in cache){
results = cache[query];
}
else{
//Case insensitive search for our people array
var results = $.grep(people, function(item){
return item.search(RegExp(query, "i")) != -1;
});
//Add results to cache
cache[query] = results;
}
//First search
if(drew == false){
//Create list for results
$("#search").after('<ul id="res"></ul>');
//Prevent redrawing/binding of list
drew = true;
//Bind click event to list elements in results
$("#res").on("click", "li", function(){
$("#search").val($(this).text());
$("#res").empty();
});
}
//Clear old results
else{
$("#res").empty();
}
//Add results to the list
for(term in results){
$("#res").append("<li>" + results[term] + "</li>");
}
}
//Handle backspace/delete so results don't remain
else if(drew){
$("#res").empty();
}
});
});
You can capture the keycode on keyup
$(document).ready(function(){
var people = ['Peter Bishop', 'Nicholas Brody', 'Gregory House', 'Hank Lawson', 'Tyrion Lannister', 'Nucky Thompson'];
var cache = {};
var drew = false;
var currentSelection = 0;
var navigation = false
$("#search").on("keyup", function(event){
switch(event.keyCode) {
// User pressed "up" arrow
case 38:
navigation = true;
navigate('up');
break;
// User pressed "down" arrow
case 40:
navigation = true;
navigate('down');
break;
// User pressed "enter"
case 13:
navigation = true;
$("#search").val($("#res li.selected").text());
$("#res").empty();
break;
default:
navigation = false;
break;
}
if(navigation == false) {
var query = $("#search").val()
if($("#search").val().length > 0){
//Check if we've searched for this term before
if(query in cache){
results = cache[query];
}
else{
//Case insensitive search for our people array
var results = $.grep(people, function(item){
return item.search(RegExp(query, "i")) != -1;
});
//Add results to cache
cache[query] = results;
}
//First search
if(drew == false){
//Create list for results
$("#search").after('<ul id="res"></ul>');
//Prevent redrawing/binding of list
drew = true;
//Bind click event to list elements in results
$("#res").on("click", "li", function(){
$("#search").val($(this).text());
$("#res").empty();
});
}
//Clear old results
else{
$("#res").empty();
}
//Add results to the list
for(term in results){
$("#res").append("<li>" + results[term] + "</li>");
}
}
//Handle backspace/delete so results don't remain
else if(drew){
$("#res").empty();
}
}
});
});
function navigate(direction) {
if($("#res li.selected").size() == 0) {
currentSelection = -1;
}
if(direction == 'up' && currentSelection != -1) {
if(currentSelection != 0) {
currentSelection--;
}
} else if (direction == 'down') {
if(currentSelection != $("#res li").size() -1) {
currentSelection++;
}
}
setSelected(currentSelection);
}
function setSelected(menuitem) {
$("#res li").removeClass("selected");
$("#res li").eq(menuitem).addClass("selected");
}
#res {
margin: 0px;
padding-left: 0px;
width: 150px;
}
#res ul li {
list-style-type: none;
}
#res li:hover {
background: #110D3B;
color: #FFF;
cursor: pointer;
}
#res li.selected {
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="output"><div>
<input id="search" type="text">
Related
Here is my code example im currently working on. (i needed an exmaple of an autocomplete input for only airports) this is forked from someone on codepen, so credit on their behalf.
What it currently does?
The code curently autocompletes the airport code based on the city you input into the field. This is the expected behaviour and is fine.
What i am currently trying to achieve ?
I am now trying to add a second input (id name of autocomplete2) field to behave the exact same. So i can simple add 2 seperate airports, 1 in each input.
I have tried adding adding the following var bc = $('#autocomplete2') and using var bc thorugh the code but i am getting lost when it comes to the bottom of the code
function search(e) {
if (e.which === 38 || e.which === 13 || e.which === 40) {
return;
}
if (ac.val().length > 0) {
results = _.take(fuse.search(ac.val()), 7);
numResults = results.length;
var divs = results.map(function(r, i) {
return '<div class="autocomplete-result" data-index="'+ i +'">'
+ '<div><b>'+ r.iata +'</b> - '+ r.name +'</div>'
+ '<div class="autocomplete-location">'+ r.city +', '+ r.country +'</div>'
+ '</div>';
});
selectedIndex = -1;
list.html(divs.join(''))
.attr('data-highlight', selectedIndex);
} else {
numResults = 0;
list.empty();
}
}
I have reverted my changes and gone back to a single input for the time being.
https://codepen.io/anon/pen/vrvgKQ?editors=0010
EDIT
https://codepen.io/eyecandy91/pen/JZwrqy?editors=0010
Changing to a class allows me to use multiple inputs but now theres a issues with the dropdown when there is double inputs & double inputs on each class. Codepend updated.
Take a look at this pen, I believe it should be working the way you want it: https://codepen.io/anon/pen/vrvZmv?editors=0011
var options = {
shouldSort: true,
threshold: 0.4,
maxPatternLength: 32,
keys: [{
name: 'iata',
weight: 0.5
},
{
name: 'name',
weight: 0.3
},
{
name: 'city',
weight: 0.2
}]
};
var fuse = new Fuse(airports, options);
$('.autocomplete').each(function() {
var ac = $(this);
ac.on('click', function(e) {
e.stopPropagation();
})
.on('focus keyup', search)
.on('keydown', onKeyDown);
var wrap = $('<div>')
.addClass('autocomplete-wrapper')
.insertBefore(ac)
.append(ac);
var list = $('<div>')
.addClass('autocomplete-results')
.on('click', '.autocomplete-result', function(e) {
e.preventDefault();
e.stopPropagation();
selectIndex($(this).data('index'), ac);
})
.appendTo(wrap);
});
$(document)
.on('mouseover', '.autocomplete-result', function(e) {
var index = parseInt($(this).data('index'), 10);
if (!isNaN(index)) {
$(this).attr('data-highlight', index);
}
})
.on('click', clearResults);
function clearResults() {
results = [];
numResults = 0;
$('.autocomplete-results').empty();
}
function selectIndex(index, autoinput) {
if (results.length >= index + 1) {
autoinput.val(results[index].iata);
clearResults();
}
}
var results = [];
var numResults = 0;
var selectedIndex = -1;
function search(e) {
if (e.which === 38 || e.which === 13 || e.which === 40) {
return;
}
var ac = $(e.target);
var list = ac.next();
if (ac.val().length > 0) {
results = _.take(fuse.search(ac.val()), 7);
numResults = results.length;
var divs = results.map(function(r, i) {
return '<div class="autocomplete-result" data-index="'+ i +'">'
+ '<div><b>'+ r.iata +'</b> - '+ r.name +'</div>'
+ '<div class="autocomplete-location">'+ r.city +', '+ r.country +'</div>'
+ '</div>';
});
selectedIndex = -1;
list.html(divs.join(''))
.attr('data-highlight', selectedIndex);
} else {
numResults = 0;
list.empty();
}
}
function onKeyDown(e) {
var ac = $(e.currentTarget);
var list = ac.next();
switch(e.which) {
case 38: // up
selectedIndex--;
if (selectedIndex <= -1) {
selectedIndex = -1;
}
list.attr('data-highlight', selectedIndex);
break;
case 13: // enter
selectIndex(selectedIndex, ac);
break;
case 9: // enter
selectIndex(selectedIndex, ac);
e.stopPropagation();
return;
case 40: // down
selectedIndex++;
if (selectedIndex >= numResults) {
selectedIndex = numResults-1;
}
list.attr('data-highlight', selectedIndex);
break;
default: return; // exit this handler for other keys
}
e.stopPropagation();
e.preventDefault(); // prevent the default action (scroll / move caret)
}
I put the autocomplete inputs through a loop to create the "dropdown"-container for each one and then changed most of the functions so that they would work for multiple inputs.
There's probably room for improvement and cleaning up, but hopefully this will get you in the right direction.
Edit: Found and fixed bug in onKeyDown-function
I'm trying to create an undo/redo function for jQueryUI .draggable at the moment I have a function but doesn't work as expect, here is my funcion:
$(document).ready(function() {
$('.editable').draggable({
stop: stopHandlerDrag,
start: startHandlerDrag
});
});
var historyApp = {
stackStyle: [],
stackId: [],
counter: -1,
add: function(style, id) {
++this.counter;
this.stackStyle[this.counter] = style;
this.stackId[this.counter] = id;
this.doSomethingWith(style, id);
// delete anything forward of the counter
this.stackStyle.splice(this.counter + 1);
},
undo: function() {
--this.counter;
this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]);
},
redo: function() {
++this.counter;
this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]);
},
doSomethingWith: function(style, id) {
//Check if make buttons undo/redo disabled or enabled
if (this.counter <= -1) {
$('#undo').addClass('disabled');
$('#redo').removeClass('disabled');
return;
} else {
$('#undo').removeClass('disabled');
}
if (this.counter == this.stackStyle.length) {
$('#redo').addClass('disabled');
$('#undo').removeClass('disabled');
return;
} else {
$('#redo').removeClass('disabled');
}
console.log(style + ' - ' + id);
//Apply history style
$('#' + id).attr('style', style);
console.log(this.counter + ' - ' + this.stackStyle.length);
}
};
//Stop Handler Drag
function stopHandlerDrag(event, ui) {
console.log('stop drag');
var style = $(ui.helper).attr('style');
var id = $(ui.helper).attr('id');
historyApp.add(style, id);
}
//Star Handler Drag
function startHandlerDrag(event, ui) {
console.log('start drag');
var style = $(ui.helper).attr('style');
var id = $(ui.helper).attr('id');
historyApp.add(style, id);
//Dettach all events
$('#' + id).draggable("option", "revert", false);
//reassign stop events
$('#' + id).draggable({
stop: stopHandlerDrag,
start: ''
});
}
//Click Events For Redo and Undo
$(document).on('click', '#redo', function() {
historyApp.redo();
});
$(document).on('click', '#undo', function() {
historyApp.undo();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script>
<span id="undo" class="btn btn-sm btn-success disable">Undo</span>
<span id="redo" class="btn btn-sm btn-danger disable ">Redo</span>
<p id="P1" class="editable" style="left: auto; top:auto;">Drag #1</<p>
<p id="P2" class="editable" style="left: auto; top:auto;">Drag #2</<p>
<p id="P3" class="editable" style="left: auto; top:auto;">Drag #3</<p>
And here is a Working code demo
Now, the problem is that for example if you run my code and drag the elements in the following order (Drag #1, Drag #2, Drag #3), and then you click on undo sometimes you will have to click twice, and then if you drag in that order and the click on undo to restore all the elements as at te beginning and then drag elements #1 and #2 and then click on undo you will notice that only the #1 element is going to comeback to his possition, so my question is what I'm doing wrong and how can I solve this? I think that maybe my counter is wrong or something is wrong with my var historyApp
You probably need to append a logic to check if the style to undo or redo differs from the prev/next state:
add: function (style, id) {
var preIndex = this.counter;
if (this.counter == this.stackStyle.length - 1)
{
++this.counter;
}
else
{
this.counter = this.stackStyle.length;
}
this.stackStyle[this.counter] = style;
this.stackId[this.counter] = id;
this.doSomethingWith(style, id);
},
undo: function () {
--this.counter;
while ($('#' + this.stackId[this.counter]).attr('style') == this.stackStyle[this.counter])
{
--this.counter;
}
this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]);
},
redo: function () {
++this.counter;
while ($('#' + this.stackId[this.counter]).attr('style') == this.stackStyle[this.counter]) {
++this.counter;
}
this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]);
},
There's something wrong with your logic. Here's a StateMaker I made myself. Don't copy it letter for letter, but it shows you the logic.
function StateMaker(initialState){
var o = initialState;
if(o){
this.initialState = o; this.states = [o];
}
else{
this.states = [];
}
this.savedStates = []; this.canUndo = this.canRedo = false; this.undoneStates = [];
this.addState = function(state){
this.states.push(state); this.undoneStates = []; this.canUndo = true; this.canRedo = false;
return this;
}
this.undo = function(){
var sl = this.states.length;
if(this.initialState){
if(sl > 1){
this.undoneStates.push(this.states.pop()); this.canRedo = true;
if(this.states.length < 2){
this.canUndo = false;
}
}
else{
this.canUndo = false;
}
}
else if(sl > 0){
this.undoneStates.push(this.states.pop()); this.canRedo = true;
}
else{
this.canUndo = false;
}
return this;
}
this.redo = function(){
if(this.undoneStates.length > 0){
this.states.push(this.undoneStates.pop()); this.canUndo = true;
if(this.undoneStates.length < 1){
this.canRedo = false;
}
}
else{
this.canRedo = false;
}
return this;
}
this.save = function(){
this.savedStates = this.states.slice();
return this;
}
this.isSavedState = function(){
var st = this.states, l = st.length; sv = this.savedStates;
if(l !== sv.length){
return false;
}
function rec(o, c){
var x, z;
if(typeof o !== typeof c){
return false;
}
else if(o instanceof Array){
for(var n=0,q=o.length; n<q; n++){
x = o[n]; z = c[n];
if(x && typeof x === 'object'){
return rec(x, z);
}
else if(o !== c){
return false;
}
}
}
else if(o && typeof o === 'object'){
for(var n in o){
x = o[n]; z = c[n];
if(x && typeof x === 'object'){
return rec(x, z);
}
else if(o !== c){
return false;
}
}
}
else if(o !== c){
return false;
}
return true;
}
for(var i=0; i<l; i++){
if(!rec(st[i], sv[i])){
return false;
}
}
return true;
}
}
Implementation with jQuery UI would look something like:
var dragging;
function DragMaster(yourDraggable){
var d = yourDraggable, p = d.offset(), sm = new StateMaker({left:p.left, top:p.top})), states = sm.states, t = this;
function ls(){
if(states.length){
return states.slice(-1)[0];
}
return false;
}
this.update = function(){
var cp = d.offset();
sm.addState({left:cp.left, top:cp.top});
return this;
}
this.undo = function(){
sm.undo(); d.offset(ls());
return this;
}
this.redo = function(){
sm.redo(); d.offset(ls());
return this;
}
this.save = function(){
sm.save();
return this;
}
this.getStates = function(){
return states;
}
this.getSavedStates = function(){
return sm.savedStates;
}
this.init = function(){
return {
start: function(){
dragging = d;
},
stop: function(){
t.update();
}
}
}
}
var yd = $('#yourDraggable'), dm = new DragMaster(yd);
yd.draggable(dm.init());
$('#undo').click(function(){
if(dragging === yd)dm.undo();
});
$('#redo').click(function(){
if(dragging === yd)dm.redo();
});
Now you can create new DragMaster()s and send .init() into draggable.
Note:
If you want to see if the current state is the saved state to change the color of your save button then StateMakerInstance.isSavedState() is what you can use.
I want to make a search demo. My problem is that I have one parent div in which I have two child divs. Both child divs have same class (RLTRightDiv). I want to search on all content. Can you please tell me how I can achieve that. It is currently only searching the first child of the parent div.
example "leave" is present in both div.
http://jsfiddle.net/3MVNj/5/
var searchIndex = -1;
var searchTermOld = '';
$(document).ready(function () {
/*
Search Functionality method.
*/
$('.searchbox').on('change', function () {
if ($(this).val() === '') {
var selector = "#fullContainer .RLTRightDiv";
$(selector + ' span.match').each(function () {
$(this).replaceWith($(this).html());
});
}
searchIndex = -1;
$('.searchNext').attr("disabled", "disabled");
$('.searchPrev').attr("disabled", "disabled");
searchTermOld = $(this).val();
});
$('.searchbox').on('keyup', function () {
var selector = "#fullContainer .RLTRightDiv";
if ($(this).val() === '') {
$(selector + ' span.match').each(function () {
$(this).replaceWith($(this).html());
});
}
if ($(this).val() !== searchTermOld) {
$(selector + ' span.match').each(function () {
$(this).replaceWith($(this).html());
});
searchIndex = -1;
$('.searchNext').attr("disabled", "disabled");
$('.searchPrev').attr("disabled", "disabled");
}
});
//Search Click method
$('.search').on('click', function () {
if (searchIndex == -1) {
var searchTerm = $('.searchbox').val();
if (searchTerm == '') {
PG_alert("Please Insert Text.")
return;
}
if (searchTerm == 'b'||searchTerm == 'br'||searchTerm == 'r') {
return;
}
setTimeout(function(){
searchAndHighlight(searchTerm);
},300);
} else
{
//naveen
setTimeout(function(){
searchNext();
},300);
}
if ($('.match').length > 1) {
$('.searchNext').removeAttr("disabled");
$('.searchPrev').removeAttr("disabled");
}
});
$('.searchNext').on('click', searchNext);
});
/*
Seacrh and highlight text method.
*/
function searchAndHighlight(searchTerm) {
if (searchTerm) {
var searchTermRegEx, matches;
var selector = "#fullContainer .RLTRightDiv";
$(selector + ' span.match').each(function () {
$(this).replaceWith($(this).html());
});
try {
searchTermRegEx = new RegExp('(' + searchTerm + ')', "ig");
} catch (e) {
return false;
}
$('.highlighted').removeClass('highlighted');
matches = $(selector).text().match(searchTermRegEx);
if (matches !== null && matches.length > 0) {
var txt = $(selector).html().replace(searchTermRegEx, '<span class="match">$1</span>');
$(selector).html(txt);
searchIndex++;
$('.match:first').addClass('highlighted');
$('#realTimeContents').animate({
scrollTop: $('.match').eq(searchIndex).get(0).offsetTop
});
return true;
} else {
console.log("yes===========")
searchIndex = -1;
}
return false;
}
return false;
}
function searchNext() {
var searchTerm = $('.searchbox').val();
if (searchTerm == '') {
PG_alert("Please Insert Text.")
return;
}
if (searchTerm == 'b'||searchTerm == 'r'||searchTerm == 'br') {
// PG_alert("Please .")
return;
}
if(searchIndex!=-1){
searchIndex++;
if (searchIndex >= $('.match').length) {
//naveen end
searchIndex = -1;
}
$('.highlighted').removeClass('highlighted');
$('.match').eq(searchIndex).addClass('highlighted');
$('#realTimeContents').animate({
scrollTop: $('.match').eq(searchIndex).get(0).offsetTop
});
}
}
function IsChecked()
{
var rblActive = document.getElementById("<%=rblActive.ClientID %>");
var item = rblActive.getElementsByTagName("input");
var IsItemChecked = false;
for (var i = 0; i < item.Length; i++)
{
if (item[i].checked)
{
IsItemChecked = true;
}
}
if (IsItemChecked == false)
{
alert("Check Yes or No");
rblActive.focus();
return false;
}
return true;
}
This is the code I tried. When control comes in the for loop, it directly comes out without any action even if the item in the radio button list is checked or not checked.
You can do the same with jQuery also..
function ValidateControls() {
var count = 0;
$("input[type=radio]").each(function () {
if ($(this).attr('checked')) {
count++;
}
});
if (count > 0) {
return true;
}
else {
alert("No Row Selected");
return false;
}
}
At the url http://www.candyundies.com/template_non_product.php, I am using an autocomplete script on the search box for suggestions. I have tested and is working in current versions of Chrome, Safari, Opera, Firefox and IE 8. However, I noticed in IE 8, it is throwing an Object expected error after the first letter is typed in the search box but the script continues to work flawlessly. I'm sure it is a syntax error or something small I have overlooked but I cannot seem to find the problem. Any help would be much appreciated.
Contents of autocomplete.js:
// global variables
var acListTotal = 0;
var acListCurrent = -1;
var acDelay = 100;
var acURL = null;
var acSearchId = null;
var acResultsId = null;
var acSearchField = null;
var acResultsDiv = null;
function setAutoComplete(field_id, results_id, get_url) {
// initialize vars
acSearchId = "#" + field_id;
acResultsId = "#" + results_id;
acURL = get_url;
// create the results div
$("#auto").append('<div id="' + results_id + '"></div>');
// register mostly used vars
acSearchField = $(acSearchId);
acResultsDiv = $(acResultsId);
// on blur listener
acSearchField.blur(function(){ setTimeout("clearAutoComplete()", 100) });
// on key up listener
acSearchField.keyup(function (e) {
// get keyCode (window.event is for IE)
var keyCode = e.keyCode || window.event.keyCode;
var lastVal = acSearchField.val();
// check an treat up and down arrows
if(updownArrow(keyCode)){
return;
}
// check for an ENTER or ESC
if(keyCode == 13 || keyCode == 27){
clearAutoComplete();
return;
}
// if is text, call with delay
setTimeout(function () {autoComplete(lastVal)}, acDelay);
});
}
// treat the auto-complete action (delayed function)
function autoComplete(lastValue) {
// get the field value
var part = acSearchField.val();
// if it's empty clear the resuts box and return
if(part == ''){
clearAutoComplete();
return;
}
// if it's equal the value from the time of the call, allow
if(lastValue != part){
return;
}
// get remote data as JSON
$.getJSON(acURL + part, function(json){
// get the total of results
var ansLength = acListTotal = json.length;
// if there are results populate the results div
if(ansLength > 0){
var newData = '';
// create a div for each result
for(i=0; i < ansLength; i++) {
newData += '<div class="unselected">' + json[i] + '</div>';
}
// update the results div
acResultsDiv.html(newData);
acResultsDiv.css("display","block");
// for all divs in results
var divs = $(acResultsId + " > div");
// on mouse over clean previous selected and set a new one
divs.mouseover( function() {
divs.each(function(){ this.className = "unselected"; });
this.className = "selected";
});
// on click copy the result text to the search field and hide
divs.click( function() {
acSearchField.val(this.childNodes[0].nodeValue);
clearAutoComplete();
});
} else {
clearAutoComplete();
}
});
}
// clear auto complete box
function clearAutoComplete() {
acResultsDiv.html('');
acResultsDiv.css("display","none");
}
// treat up and down key strokes defining the next selected element
function updownArrow(keyCode) {
if(keyCode == 40 || keyCode == 38){
if(keyCode == 38){ // keyUp
if(acListCurrent == 0 || acListCurrent == -1){
acListCurrent = acListTotal-1;
}else{
acListCurrent--;
}
} else { // keyDown
if(acListCurrent == acListTotal-1){
acListCurrent = 0;
}else {
acListCurrent++;
}
}
// loop through each result div applying the correct style
acResultsDiv.children().each(function(i){
if(i == acListCurrent){
acSearchField.val(this.childNodes[0].nodeValue);
this.className = "selected";
} else {
this.className = "unselected";
}
});
return true;
} else {
// reset
acListCurrent = -1;
return false;
}
}
Issue resolved. See comment by ocanal.