jQuery autocomplete strange behavior - javascript

I've got an issue with jQuery's autocomplete. What I am trying to do is show a suggestions list based on input. So, for instance, on input class="font" I want to have a list of font sizes and on input class="color" to have a list of color predictions.
Here is what I have:
function suggestions(input, element) {
var suggestions = [];
if (element.hasClass("color") !== -1) {
var i = 0;
while (i < 100) {
suggestions.push("color" + i.toString()); // for testing purpose
i++;
}
} else {
var nr = 1;
while (nr < 1025) {
suggestions.push(nr.toString() + "px");
nr = nr + 1;
}
}
$(element).autocomplete({
minLength: 1,
source: function (request, response) {
var counter = 0;
var filteredArray = $.map(suggestions, function (item) {
if (item.startsWith(request.term) && counter < 10) {
counter = counter + 1;
return item;
} else {
return null;
}
});
response(filteredArray);
},
autoFocus: true
});
}
The thing is, it works perfectly when I test it for inputs having any class except 'color'. When it detects a class with 'color', it will build the suggestions array accordingly but will refuse to get into the anonymous function inside autocomplete - source. Which is odd to me, 'cause the array is always constructed and the autocomplete should always be hit.
Any ideas?
Thanks!

jQuery's .hasClass() returns boolean value, so you code should look like:
if (element.hasClass("color")) { ... }
Try this JSFiddle (type symbol "c")

Related

Checking a div for duplicates before appending to the list using jQuery

This should be trivial but I'm having issues...
Basically what I am trying to do is append a new "div" to "selected-courses" when a user clicks on a "course". This should happen if and only if the current course is not already in the "selected-courses" box.
The problem I'm running into is that nothing is appended to the "selected-courses" section when this is executed. I have used alert statements to make sure the code is in fact being run. Is there something wrong with my understanding of the way .on and .each work ? can I use them this way.
Here is a fiddle http://jsfiddle.net/jq9dth4j/
$(document).on("click", "div.course", function() {
var title = $( this ).find("span").text();
var match_found = 0;
//if length 0 nothing in list, no need to check for a match
if ($(".selected-course").length > 0) {
match_found = match(title);
}
if (matched == 0) {
var out = '<div class="selected-course">' + '' + title + ''+'</div>';
$("#selected-box").append(out);
}
});
//checks to see if clicked course is already in list before adding.
function match(str) {
$(".selected-course").each(function() {
var retval = 0;
if(str == this.text()) {
//course already in selected-course section
retval = 1;
return false;
}
});
return retval;
}
There was a couple of little issues in your fiddle.
See fixed fiddle: http://jsfiddle.net/jq9dth4j/1/
function match(str) {
var retval = 0;
$(".selected-course").each(function() {
if(str == $(this).text()) {
retval = 1;
return false;
}
});
return retval;
}
You hadn't wrapped your this in a jquery object. So it threw an exception saying this had no method text().
Second your retval was declared inside the each so it wasn't available to return outside the each, wrong scope.
Lastly the if in the block:
if (matched== 0) {
var out = '';
out += '<div class="selected-course">' + '' + title + ''+'</div>';
$("#selected-box").append(out);
}
was looking at the wrong variable it was looking at matched which didn't exist causing an exception.
Relying on checking what text elements contain is not the best approach to solve this kind of question. It is prone to errors (as you have found out), it can be slow, it gives you long code and it is sensitive to small changes in the HTML. I would recommend using custom data-* attributes instead.
So you would get HTML like this:
<div class="course" data-course="Kite Flying 101">
<a href="#">
<span>Kite Flying 101</span>
</a>
</div>
Then the JS would be simple like this:
$(document).on('click', 'div.course', function() {
// Get the name of the course that was clicked from the attribute.
var title = $(this).attr('data-course');
// Create a selector that selects everything with class selected-course and the right data-course attribute.
var selector = '.selected-course[data-course="' + title + '"]';
if($(selector).length == 0) {
// If the selector didn't return anything, append the div.
// Do note that we need to add the data-course attribute here.
var out = '<div class="selected-course" data-course="' + title + '">' + title + '</div>';
$('#selected-box').append(out);
}
});
Beware of case sensitivity in course names, though!
Here is a working fiddle.
Try this code, read comment for where the changes are :
$(document).on("click", "div.course", function () {
var title = $(this).find("span").text().trim(); // use trim to remove first and end whitespace
var match_found = 0;
if ($(".selected-course").length > 0) {
match_found = match(title);
}
if (match_found == 0) { // should change into match_found
var out = '';
out += '<div class="selected-course">' + '' + title + '' + '</div>';
$("#selected-box").append(out);
}
});
function match(str) {
var retval = 0; // this variable should place in here
$(".selected-course").each(function () {
if (str == $(this).find('a').text().trim()) { // find a tag to catch values, and use $(this) instead of this
retval = 1;
return false;
}
});
return retval; // now can return variable, before will return undefined
}
Updated DEMO
Your Issues are :
1.this.text() is not valid. you have to use $(this).text().
2.you defined var retval = 0; inside each statement and trying to return it outside each statement. so move this line out of the each statement.
3.matched is not defined . it should be match_found in line if (matched == 0) {.
4. use trim() to get and set text, because text may contain leading and trailing spaces.
Your updated JS is
$(document).on("click", "div.course", function () {
var title = $(this).find("span").text();
var match_found = 0;
if ($(".selected-course").length > 0) {
match_found = match(title);
}
if (match_found == 0) {
var out = '<div class="selected-course">' + '' + title + '' + '</div>';
$("#selected-box").append(out);
}
});
function match(str) {
var retval = 0;
$(".selected-course").each(function () {
if (str.trim() == $(this).text().trim()) {
retval = 1;
return false;
}
});
return retval;
}
Updated you Fiddle

Hiding columns in Dynatable

I'm hoping to replace my tables with Dynatable, but I need to be able to hide and show certain groups of columns to do so. I do this in the existing, regular html table by giving a group to each , for example:
<td class = "group1">cell value</td>
Then I have some hide and show javascript functions:
function hideCol(columnClass){
$('table .'+columnClass).each(function(index) {
$(this).hide();
});
$('ul#hiddenCols').append('<li id="'+columnClass+'">Show '+columnClass+'</li>');
}
function showCol(columnClass){
$('table .'+columnClass).each(function(index) {
$(this).show();
});
$('li#'+columnClass).remove();
}
$(document).ready(function() {
hideCol("Group2");
hideCol("Group3");
hideCol("Group4");
$('#radio1').click(function() {
/*$(this).parent().addClass("active").siblings().removeClass("active")*/
showCol("Group1");
hideCol("Group2");
hideCol("Group3");
hideCol("Group4");
});
Is there a reasonably straight forward way I can adapt Dynatable to do something similar? Is there a way I can assign classes to each and in a Dynatable?
Thanks a lot,
Alex
There is a setting that makes the data in each column inherit the class of the column's header. However, when doing this there appears to be a bug when sorting hidden columns, as I mention in this question.
I couldn't make this work with Dynatable, so I switched to Datatables, where hiding/sorting columns seems to be much more stable.
You can edit the source code of dynatable.js as follows and then use show() and hide() methods...
function DomColumns(obj, settings) {
...
this.hide = function(indexOrId) {
var columns = settings.table.columns;
if (typeof indexOrId == "number")
columns[indexOrId].hidden = true;
else {
for (var i = columns.length - 1; i >= 0; i--) {
if (columns[i].id == indexOrId) {
columns[i].hidden = true;
break;
}
}
}
obj.$element.find(settings.table.headRowSelector).children('[data-dynatable-column="' + indexOrId + '"]')
.first().hide();
obj.dom.update();
};
this.show = function(indexOrId) {
var columns = settings.table.columns;
if (typeof indexOrId == "number")
columns[indexOrId].hidden = false;
else {
for (var i = columns.length - 1; i >= 0; i--) {
if (columns[i].id == indexOrId) {
columns[i].hidden = false;
break;
}
}
}
obj.$element.find(settings.table.headRowSelector).children('[data-dynatable-column="' + indexOrId + '"]')
.first().show();
obj.dom.update();
};
...
};

how to attach click function to multiple divs without ID

I have a fade in function im trying to understand better. It works fine when I set up the
My question is if I have 8 links that already have the separate ID and class names how can I attach this function to each clickable link?
Is there a function to getElementbyClass or something and then just add the class to all my links?
here is my javascript:
var done = true,
fading_div = document.getElementById('fading_div'),
fade_in_button = document.getElementById('fade_in'),
fade_out_button = document.getElementById('fade_out');
function function_opacity(opacity_value) {
fading_div.style.opacity = opacity_value / 100;
fading_div.style.filter = 'alpha(opacity=' + opacity_value + ')';
}
function function_fade_out(opacity_value) {
function_opacity(opacity_value);
if (opacity_value == 1) {
fading_div.style.display = 'none';
done = true;
}
}
function function_fade_in(opacity_value) {
function_opacity(opacity_value);
if (opacity_value == 1) {
fading_div.style.display = 'block';
}
if (opacity_value == 100) {
done = true;
}
}
// fade in button
fade_in_button.onclick = function () {
if (done && fading_div.style.opacity !== '1') {
done = false;
for (var i = 1; i <= 100; i++) {
setTimeout((function (x) {
return function () {
function_fade_in(x)
};
})(i), i * 10);
}
}
};
// fade out button
fade_out_button.onclick = function () {
if (done && fading_div.style.opacity !== '0') {
done = false;
for (var i = 1; i <= 100; i++) {
setTimeout((function (x) {
return function () {
function_fade_out(x)
};
})(100 - i), i * 10);
}
}
};
Correcting the answer from BLiu1:
var fadeDivs = document.getElementsByClassName('fade');
for (var i=0, i<fadeDivs.length, i++){
// do stuff to all fade-divs by accessing them with "fadeDivs[i].something"
}
Have you considered using a javascript library like jQuery to manage this. They have some extensive, very easy to use "selectors" that allow you to easily get access to elements in the DOM and animate them with things like "fade ins" and "slides", etc. If you need more animations there are tons of plugins available for this. It also helps to deal with browser compatibility challenges too.
If you want to rely on pure JavaScript, you can use the document.getElementsByClassName() function defined here, but that function is only defined in IE9 and above as well as Safari, Chrome, FF, and Opera.
As said in the comments, there is a getElementsByClassName() method. Here is how you would use it.
for(var i=0; i<document.getElementsByClassName("fade").length; i++ ){
/*apply fade in function*/
}
I'm not sure whether getElementsByClassName() can detect one class name at a time. You might need regex for that.

jQuery "keyup" crashing page when checking 'Word Count'

I have a word counter running on a DIV and after typing in a few words, the page crashes. The browser continues to work (par scrolling) and no errors are showing in Chrome's console. Not sure where I'm going wrong...
It all started when I passed "wordCount(q);" in "keyup". I only passed it there as it would split-out "NaN" instead of a number to countdown from.
JS:
wordCount();
$('#group_3_1').click(function(){
var spliced = 200;
wordCount(spliced);
}) ;
$('#group_3_2').click(function(){
var spliced = 600;
wordCount(spliced);
}) ;
function wordCount(q) {
var content_text = $('.message1').text(),
char_count = content_text.length;
if (char_count != 0)
var word_count = q - content_text.replace(/[^\w ]/g, "").split(/\s+/).length;
$('.word_count').html(word_count + " words remaining...");
$('.message1').keyup(function() {
wordCount(q);
});
try
{
if (new Number( word_count ) < 0) {
$(".word_count").attr("id","bad");
}
else {
$(".word_count").attr("id","good");
}
} catch (error)
{
//
}
};
HTML:
<input type="checkbox" name="entry.3.group" value="1/6" class="size1" id="group_3_1">
<input type="checkbox" name="entry.3.group" value="1/4" class="size1" id="group_3_2">
<div id="entry.8.single" class="message1" style="height: 400px; overflow-y:scroll; overflow-x:hidden;" contenteditable="true"> </div>
<span class="word_count" id="good"></span>
Thanks in advanced!
This is causing an infinite loop if (new Number(word_count) < 0) {.
Your code is a mess altogether. Just study and start with more basic concepts and start over. If you want to describe your project to me in a comment, I would be glad to show you a good, clean, readable approach.
Update:
Part of having a good architecture in your code is to keep different parts of your logic separate. No part of your code should know about or use anything that isn't directly relevant to it. Notice in my word counter that anything it does it immediately relevant to its word-counter-ness. Does a word counter care about what happens with the count? Nope. It just counts and sends the result away (wherever you tell it to, via the callback function). This isn't the only approach, but I just wanted to give you an idea of how to approach things more sensefully.
Live demo here (click).
/* what am I creating? A word counter.
* How do I want to use it?
* -Call a function, passing in an element and a callback function
* -Bind the word counter to that element
* -When the word count changes, pass the new count to the callback function
*/
window.onload = function() {
var countDiv = document.getElementById('count');
wordCounter.bind(countDiv, displayCount);
//you can pass in whatever function you want. I made one called displayCount, for example
};
var wordCounter = {
current : 0,
bind : function(elem, callback) {
this.ensureEditable(elem);
this.handleIfChanged(elem, callback);
var that = this;
elem.addEventListener('keyup', function(e) {
that.handleIfChanged(elem, callback);
});
},
handleIfChanged : function(elem, callback) {
var count = this.countWords(elem);
if (count !== this.current) {
this.current = count;
callback(count);
}
},
countWords : function(elem) {
var text = elem.textContent;
var words = text.match(/(\w+\b)/g);
return (words) ? words.length : 0;
},
ensureEditable : function(elem) {
if (
elem.getAttribute('contenteditable') !== 'true' &&
elem.nodeName !== 'TEXTAREA' &&
elem.nodeName !== 'INPUT'
) {
elem.setAttribute('contenteditable', true);
}
}
};
var display = document.getElementById('display');
function displayCount(count) {
//this function is called every time the word count changes
//do whatever you want...the word counter doesn't care.
display.textContent = 'Word count is: '+count;
}
I would do probably something like this
http://jsfiddle.net/6WW7Z/2/
var wordsLimit = 50;
$('#group_3_1').click(function () {
wordsLimit = 200;
wordCount();
});
$('#group_3_2').click(function () {
wordsLimit = 600;
wordCount();
});
$('.message1').keydown(function () {
wordCount();
});
function wordCount() {
var text = $('.message1').text(),
textLength = text.length,
wordsCount = 0,
wordsRemaining = wordsLimit;
if(textLength > 0) {
wordsCount = text.replace(/[^\w ]/g, '').split(/\s+/).length;
wordsRemaining = wordsRemaining - wordsCount;
}
$('.word_count')
.html(wordsRemaining + " words remaining...")
.attr('id', (parseInt(wordsRemaining) < 0 ? 'bad' : 'good'));
};
wordCount();
It's not perfect and complete but it may show you direction how to do this. You should use change event on checkboxes to change wordsLimit if checked/unchecked. For styling valid/invalid words remaining message use classes rather than ids.
I think you should use radio in place of checkboxes because you can limit 200 or 600 only at a time.
Try this like,
wordCount();
$('input[name="entry.3.group"]').click(function () {
wordCount();
$('.word_count').html($(this).data('val') + " words remaining...");
});
$('.message1').keyup(function () {
wordCount();
});
function wordCount() {
var q = $('input[name="entry.3.group"]:checked').data('val');
var content_text = $('.message1').text(),
char_count = content_text.length;
if (char_count != 0) var word_count = q - content_text.replace(/[^\w ]/g, "").split(/\s+/).length;
$('.word_count').html(word_count + " words remaining...");
try {
if (Number(word_count) < 0) {
$(".word_count").attr("id", "bad");
} else {
$(".word_count").attr("id", "good");
}
} catch (error) {
//
}
};
Also you can add if your span has bad id then key up should return false;
See Demo

show and hide several random divs using jquery

i am developing a jquery application. I have 10 divs with quotes. I am trying to create a function that takes a number and randomly displays that number of quotes from the 10 divs for 10 seconds and hide the divs. Then repeat the process again. I have not been able to do it please help me out. here is my code:
$(document).ready(function(){
var div_number = 4;
var used_numbers = new Array();
var todo = setInterval(showQuotes(),3000);
function showQuotes(){
used_numbers.splice(0,used_numbers.length);
$('.quotes').hide();
for(var inc = 0; inc<div_number; inc++) {
var random = get_random_number();
$('.quotes:eq('+random+')').show();
}
$('.quotes').fadeOut(3000);
}
function get_random_number(){
var number = randomFromTo(0,9);
if($.inArray(number, used_numbers) != -1) {
get_random_number();
}
else {
used_numbers.push(number);
return number;
}
}
function randomFromTo(from, to){
return Math.floor(Math.random() * (to - from + 1) + from);
}
});
Changes I made:
hide the .quotes on launch via a stylesheet
run showQuotes() once before setInterval(showQuotes,10000), and
add a .delay() before fading the quotes out
Py's 'return' added to get_random_number
http://jsfiddle.net/cMQdj/1/
the changed JavaScript:
$(document).ready(function () {
var div_number = 4;
var used_numbers = new Array();
showQuotes();
var todo = setInterval(showQuotes, 10000);
function showQuotes() {
used_numbers.splice(0, used_numbers.length);
$('.quotes').hide();
for (var inc = 0; inc < div_number; inc++) {
var random = get_random_number();
$('.quotes:eq(' + random + ')').show();
}
$('.quotes').delay(6000).fadeOut(3000);
}
function get_random_number() {
var number = randomFromTo(0, 9);
if ($.inArray(number, used_numbers) != -1) {
return get_random_number();
} else {
used_numbers.push(number);
return number;
}
}
function randomFromTo(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
});
and add to your stylesheet:
.quotes {display:none}
I didn't test everything, but i already see one point that might block you, the get_random_number does not always return a number. To do so, it should be
function get_random_number(){
var number = randomFromTo(0,9);
if($.inArray(number, used_numbers) != -1)
{
return get_random_number();
}
else
{
used_numbers.push(number);
return number;
}
}
Hope that helps.

Categories