Autocomplete script getting Object expected error - javascript

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.

Related

Uncaught TypeError: Cannot read property 'indexOf' of undefined in angularjs

I am very new to the world of coding, and need some help from experts. Below is the angularJs error I have noticed in the file:
The error is being pointed to this:
if( searchItemsSmallLetters.indexOf(searchTextSmallLetters) !== -1){
Your assistance will be much appreciated.
I am trying to give user suggestion based on the key which user enters.
var app = angular.module('app',[]);
app.controller('autoCompleteCTRL', function($scope, $rootScope,$http){
$rootScope.searchItems = [];
var arr= getCountries(); // Load all countries with capitals
function getCountries(){
$http.get("ajax/getCountries.php").success(function(data){
for(var i=0;i<data.length;i++)
{
$rootScope.searchItems.push(data[i].bvl_area);
}
return $rootScope.searchItems;
});
};
//Sort Array
$rootScope.searchItems.sort();
//Define Suggestions List
$rootScope.suggestions = [];
//Define Selected Suggestion Item
$rootScope.selectedIndex = -1;
//Function To Call On ng-change
$rootScope.search = function(){
$rootScope.suggestions = [];
var myMaxSuggestionListLength = 0;
for(var i=0; i<$rootScope.searchItems.length; i++){
var searchItemsSmallLetters = angular.lowercase($rootScope.searchItems[i]);
var searchTextSmallLetters = angular.lowercase($scope.searchText);
if( searchItemsSmallLetters.indexOf(searchTextSmallLetters) !== -1){
$rootScope.suggestions.push(searchItemsSmallLetters);
myMaxSuggestionListLength += 1;
if(myMaxSuggestionListLength == 5){
break;
}
}
}
}
//Keep Track Of Search Text Value During The Selection From The Suggestions List
$rootScope.$watch('selectedIndex',function(val){
if(val !== -1) {
$scope.searchText = $rootScope.suggestions[$rootScope.selectedIndex];
}
});
//Text Field Events
//Function To Call on ng-keydown
$rootScope.checkKeyDown = function(event){
if(event.keyCode === 40){//down key, increment selectedIndex
event.preventDefault();
if($rootScope.selectedIndex+1 !== $rootScope.suggestions.length){
$rootScope.selectedIndex++;
}
}else if(event.keyCode === 38){ //up key, decrement selectedIndex
event.preventDefault();
if($rootScope.selectedIndex-1 !== -1){
$rootScope.selectedIndex--;
}
}else if(event.keyCode === 13){ //enter key, empty suggestions array
event.preventDefault();
$rootScope.suggestions = [];
}
}
//Function To Call on ng-keyup
$rootScope.checkKeyUp = function(event){
if(event.keyCode !== 8 || event.keyCode !== 46){//delete or backspace
if($scope.searchText == ""){
$rootScope.suggestions = [];
}
}
}
//======================================
//List Item Events
//Function To Call on ng-click
$rootScope.AssignValueAndHide = function(index){
$scope.searchText = $rootScope.suggestions[index];
$rootScope.suggestions=[];
}
//======================================
});
The value of searchTextSmallLetters variable has become undefined. First you check whether it can be undefined. If so change the line to
if(searchItemsSmallLetters && searchItemsSmallLetters.indexOf(searchTextSmallLetters) !== -1){
or else check the service on "ajax/getCountries.php" is working and return the expected output. Since you using a get request you can use browser to do that (%domain%/ajax/getCountries.php eg:-localhost:8080/ajax/getCountries.php)
Important: Always it is good to do validations. So it is good to change the line to
if(searchItemsSmallLetters && searchItemsSmallLetters.indexOf(searchTextSmallLetters) !== -1){
Change the below line for proper handling
if( searchItemsSmallLetters.indexOf(searchTextSmallLetters) !== -1){
To
if(searchItemsSmallLetters && searchItemsSmallLetters.indexOf(searchTextSmallLetters) !== -1){
When "searchItemsSmallLetters" is undefined, then your condition will throw such error.

after keyup event I want the change to be permanent

I want to add a class to a list using keyup event but when the keyup event is called it add the class to the list. After some seconds, the class from the list will be removed then i try to use setinterval on the callback function. It work but if i try to navigate through the list it behave funny.
Here is the code
(function(){
var AutocompleteActivities = {
init: function(config)
{
this.config = config;
$("#keywords").on("keyup", this.confirm);
},
confirm: function(e)
{
var self = AutocompleteActivities;
var con = self.config.div;
key = e.keyCode;
press = e.timeStamp
//check whether there is a key up invent in the search textfield and up or down arrow is press
if(press != "" && e.keyCode == 40 || e.keyCode == 38)
{
if(con.css("display") == "block")
{
setInterval(function(){
self.navigation(key)},500)
}
}
},
navigation: function(key)
{
var con = this.config.div, // #options - the container of the autocomplete
li = con.find("ul").find(">li"),
totalLi = li.length,
firstLi = li.first(),
current = 1; // it should be zero for debug reasons it is one
if(key == 40)
{
if(current != 0) // check whether the autoComplete selection is the first in the
{
firstLi.addClass("selection")
++current
con.find("ul li").filter(function() {
return $(this).hasClass("selection");
}).next().addClass("selection").end().removeClass("selection")
return false;
}
}
}
}
AutocompleteActivities.init({
div: $("#options"),
})
})()

Getting deleted character or text on pressing delete or backspace on a textbox

I have a text box, I want to get the deleted character when I press a backspace or delete key.
I have a key up event handler and i am capturing if the key is backspace. Now inside this I need to perform some tasks based on the key deleted. Please help.
After making a little tweak for the getCursorPosition function in this thread, you can get the characters deleted by tracking the current cursor selection.
The code handles the following conditions:
Type and then backspace at the end.
Move cursor in the middle of the text and delete/backspace.
Select a piece of text and then delete/backspace.
$.fn.getCursorPosition = function() {
var el = $(this).get(0);
var pos = 0;
var posEnd = 0;
if('selectionStart' in el) {
pos = el.selectionStart;
posEnd = el.selectionEnd;
} else if('selection' in document) {
el.focus();
var Sel = document.selection.createRange();
var SelLength = document.selection.createRange().text.length;
Sel.moveStart('character', -el.value.length);
pos = Sel.text.length - SelLength;
posEnd = Sel.text.length;
}
// return both selection start and end;
return [pos, posEnd];
};
$('#text').keydown(function (e) {
var position = $(this).getCursorPosition();
var deleted = '';
var val = $(this).val();
if (e.which == 8) {
if (position[0] == position[1]) {
if (position[0] == 0)
deleted = '';
else
deleted = val.substr(position[0] - 1, 1);
}
else {
deleted = val.substring(position[0], position[1]);
}
}
else if (e.which == 46) {
var val = $(this).val();
if (position[0] == position[1]) {
if (position[0] === val.length)
deleted = '';
else
deleted = val.substr(position[0], 1);
}
else {
deleted = val.substring(position[0], position[1]);
}
}
// Now you can test the deleted character(s) here
});
And here is Live Demo
You could use the keydown event handler instead so that the last character to be deleted is still available:
$('textarea').on('keydown',function(e) {
var deleteKeyCode = 8,
value = $(this).val(),
length = value.length,
lastChar = value.substring(length-1, length);
if (e.which === deleteKeyCode) {
alert(lastChar);
}
});
Live Demo
$('input').keydown(function(e){
$(this).data('prevVal', $(this).val());
}).keyup(function(e){
if(e.keyCode === 8) {//delete
var ele = $(this);
var val = ele.data('prevVal');
var newVal = ele.val();
var removedChar = val.substring(val.length-1);
alert(removedChar);
}
});

Jquery keydown() with number format not work correctly on android webview

I have encountered a strange behavior on android browser / webview. I was testing an input that will automatically format to phone number format "(xxx) xxx-xxxx". But then what happened was when I tapped or press any number on androids keyboard, the first input was like this "(x" but then the cursor was in between "(" and "x". Is there a way to put the cursor after "x" value?
I tested this on iPhone and windows web browsers and it works fine. Please let me know if there are mistakes on my jquery or javascripts.
Thanks
HTML CODE:
<html>
<head>
<title>Sample Phone Number Format</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
</head>
<body>
<input type="text" id="phone" />
<script type="text/javascript">
$('#phone').on('keydown', function (event) {
objval = $(this).val();
if (event.keyCode == 46 || event.keyCode == 8) {} else {
if (!((event.keyCode > 47 && event.keyCode < 58) || (event.keyCode > 95 && event.keyCode < 106) || (objval.length > 13))) {
event.preventDefault();
} else {
if (objval.length == 0) {
$(this).val(objval + '(');
alert(objval + '(');
} else if (objval.length == 4) {
$(this).val(objval + ') ');
alert(objval + ') ');
} else if (objval.length == 9) {
$(this).val(objval + '-');
alert(objval + '-');
} else if (objval.length >= 14) {
if (event.keyCode == 9) {
return;
} else {
event.preventDefault();
}
}
}
}
});
$('#phone').on('keydown', function (event) {
var objVal = $(this).val();
if(objVal.length < 14)
{
validateCallerForm(objVal + String.fromCharCode((96 <= event.keyCode && event.keyCode <= 105)? event.keyCode-48 : event.keyCode));
}
});
//Validates proper phone format, true if valid phone number, false otherwise
function isValidPhoneNumber(elementValue) {
var numberPattern = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/;
return numberPattern.test(elementValue);
}
//validates entire caller form, also updates css classes for proper response
function validateCallerForm(PhoneNumber) {
if (isValidPhoneNumber(PhoneNumber)) {
alert("true");
} else {
alert("false");
}
}
</script>
</body>
</html>
Giving +50 Bounty to who'm will answer this correctly
you have to first define listener for typing and copy-paste like below (not required) :
$("#phone").keyup( function() {
maskLine(this);
});
$("#phone").change( function() {
maskLine(this);
});
Then, to manage cursor placement, you have to cache previous phone number and then, you could compare difference and update cursor position if needed.
So declare, you have to declare a global array like this :
var _cacheElementValues = new Array();
At last, you can check the function below, it applies your mask to phone number field and manage cursor placement :
function maskLine( element ) {
element = $(element);
var maskedLine = '';
var line = element.attr('value');
// check the cache of the input and abord if no change since last treatment
if (_cacheElementValues[element.attr('id')] != undefined && _cacheElementValues[element.attr('id')] == line) {
return;
}
line = line.replace(/\D/g, ''); // remove all characters != digits
line = line.substring(0, 10);
if (line != '') {
// apply mask
if (line.length <= 2 ) {
maskedLine = "(" + line;
} else if (line.length < 6) {
maskedLine = line.replace(/^([0-9]{3})([0-9]{0,3})/g, '($1) $2');
} else {
// mask : '(XXX) XXX-XXXX'
maskedLine = line.replace(/^([0-9]{3})([0-9]{3})([0-9]{0,4})/g, '($1) $2-$3');
}
}
// define cursor position at the end of the input by default
var pos = maskedLine.length;
// Change cursor placement if necessary
if (typeof element[0].selectionStart != 'undefined') {
var start = element[0].selectionStart;
var end = element[0].selectionEnd;
var insText = element[0].value.substring(start, end);
// get current cursor placement
if (insText.length == 0) {
pos = start;
} else {
pos = start + insText.length;
}
// find how many digits typing since last mask application
var previousLength = 0;
if (_cacheElementValues[element.attr('id')] != undefined) {
previousLength = _cacheElementValues[element.attr('id')].replace(/\s/g, '').length;
}
var diff = maskedLine.replace(/\s/g, '').length - previousLength;
// if sum of new typing digit is > 0 : we change cursor placement
if (diff > 0) {
pos += (diff - 1) + Math.round((diff-1)/3);
if (pos%6 == 0 && maskedLine.length >= pos+1) pos++;
}
}
// update input data & cache
element.val(maskedLine);
_cacheElementValues[element.attr('id')] = maskedLine;
// update cursor placement
element[0].selectionStart = element[0].selectionEnd = pos;
}
You can find this example on jsFiddle : http://jsfiddle.net/UE9LB/5/
I hope this little explantion can solve your problem ;)
Enjoy !
ps: i apologize for my poor english :s
I'd recommend at least starting with an existing plugin rather than going through your own isolated rounds of solving issues.
http://digitalbush.com/projects/masked-input-plugin/
https://github.com/igorescobar/jQuery-Mask-Plugin
The short answer is to set the selectionStart and selectionEnd properties of the input element. After you format the value, set these properties to this.value.length.
this.selectionStart = this.value.length;
this.selectionEnd = this.value.length;
But, where you are going to run into trouble is when the cursor is not at the end of the text. Eg, the user has manually positioned the cursor to a position within the text. To prevent the cursor from jumping to the end, you will need to detect the cursor position before you format the input, then put the cursor back in the appropriate position after formatting.
Edit: This jsFiddle may get you started, but isn't perfect yet.
I rewrite the code on my #phone keydown event and this will work on iPhone, Android, webkit browsers.
$('#phone').on('keydown', function (event) {
if (event.keyCode == 8 || event.keyCode == 37 || event.keyCode == 39) {
// ignore if BKSPCE, left arrow, or right arrow
} else {
// validate if anything else
inputval = $(this).val();
var string = inputval.replace(/[^0-9]/g, "");
var first3 = string.substring(0,3);
var next3 = string.substring(3,6);
var next4 = string.substring(6,10);
var string = ("(" + first3 + ") " + next3 + "-" + next4);
$(this).val(string);
}
});

How do I keep my cursor/caret from moving inside of an input text element?

I'm trying to build a form that works in a manner similar to the Google Search page, as well as Gmail and several other pages (including the this page - see the "Tags" input on the Ask Question page). As text is input, a list of options will appear, and the user can use up and down arrows to select the option they want.
The problem I am having is that the up arrow puts the cursor/caret in position 0 of the field, and down puts it at the end. Is there an easy way of keeping it from moving to the beginning/end, or do I need to find the caret position before my function is run, and reposition it to that spot after?
This is the beginning of the function that runs when when text is entered in the input element (onkeyup).
var keycode = checkKeycode (event); // returns the keycode of the key pressed.
if (keycode == 38) { // up arrow
var selection = $(".optionsSelected").attr("id");
var selId = parseInt(selection.replace('briefOption', ''));
if (selId != 0) {
$(".optionsSelected").removeClass("optionsSelected");
var newSelId = selId - 1;
$("#briefOption"+newSelId).addClass("optionsSelected");
}
}
else if (keycode == 40) { // down arrow
var selection = $(".optionsSelected").attr("id");
var selId = parseInt(selection.replace('briefOption', ''));
if (selId != numAvEmps && numAvEmps != 0) {
$(".optionsSelected").removeClass("optionsSelected");
var newSelId = selId + 1;
$("#briefOption"+newSelId).addClass("optionsSelected");
}
}
else if (keycode == 27) { // Escape
$("#docDropDown").css("display","none");
}
else if (keycode == 9 || keycode == 13) { //tab or enter
/* Fill form */
}
Let me know if any more code would be useful. Thanks for the help.
You'd want to store the caret before and then when you're done doing things, set it back.
Something like this:
HTML body:
<input type="text" id="mytextbox" style="border: 1px solid black" />
Javascript:
function getCaretPosition(ctrl)
{
var caretPos = 0;
// IE
if (document.selection)
{
ctrl.focus ();
var sel = document.selection.createRange();
sel.moveStart ('character', -ctrl.value.length);
caretPos = sel.text.length;
}
// Firefox
else if (ctrl.selectionStart || ctrl.selectionStart == '0')
{
caretPos = ctrl.selectionStart;
}
return caretPos;
}
function setCaretPosition(ctrl, pos)
{
if(ctrl.setSelectionRange)
{
ctrl.focus();
ctrl.setSelectionRange(pos,pos);
}
else if (ctrl.createTextRange)
{
var range = ctrl.createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
}
}
$(document).ready(function ()
{
$("#mytextbox").keydown(function (e)
{
// Up
if(e.which == 38)
{
var pos = getCaretPosition(this);
// Do stuff here that changes the value/caret?
setCaretPosition(this, pos);
}
// Down
else if(e.which == 40)
{
var pos = getCaretPosition(this);
// Do stuff here that changes the value/caret?
setCaretPosition(this, pos);
}
});
});
Just prevent the default action of the event. You can do this by returning false if you're using an event handler property rather than addEventListener/attachEvent, which would be the easiest way:
var input = document.getElementById("your_input");
var suppressKeypress = false;
input.onkeydown = function(evt) {
evt = evt || window.event;
var keyCode = evt.keyCode;
if (keyCode == 38) {
// Do your stuff here
suppressKeypress = true; // For Opera; see below
return false;
}
// Other cases here
};
// This bit is for Opera, which only allows you to suppress the default
// action of a keypress in the keypress event (not keydown)
input.onkeypress = function() {
if (suppressKeypress) {
suppressKeypress = false;
return false;
}
};

Categories

Resources