I have a simple jq code to create autosuggestions (google like). It works fine and I just want to add keyboard events handlers. However I have some problems with it. When I want to choose the next suggestion with event 40 (arrow down) it get all the suggestions instead of just the next one. Any idea how to fix it?
$(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){
//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('<div id="res"></div>');
//Prevent redrawing/binding of list
drew = true;
//Bind click event to list elements in results
$("#res").on("click", "div", function(){
$("#search").val($(this).text());
$("#res").empty();
});
$("#search" ).keydown(function( event ) {
if ( event.which == 40 ) {
$("#search").val($(".suggestions").next().text());
}
});
}
//Clear old results
else{
$("#res").empty();
}
//Add results to the list
for(term in results){
$("#res").append("<div class = 'sugestions'>" + results[term] + "</div>");
}
}
//Handle backspace/delete so results don't remain
else if(drew){
$("#res").empty();
}
});
});
<input id="search" type="text">
You need to keep track of the currently selected suggestion. Simplest approach is probably to add/remove a className, something like this :
if(drew == false){
//Prevent redrawing/binding of list
drew = true;
//Create list for results, and bind click event to list elements in results
var $res = $('<div id="res"></div>').insertAfter("#search")
.on("click", "div", function() {
$(".suggestions").removeClass('selected');
$("#search").val($(this).addClass('selected').text());
$("#res").empty();
});
var $search = $("#search").keydown(function(event) {
var $suggestions, $selected, index;
if (event.which == 40) {
$suggestions = $(".suggestions");
$selected = $suggestions.find('.selected').eq(0);//.eq(0) shouldn't be necessary, but just in case ...
if($selected.length) {
index = ($selected.index() + 1) % $suggestions.length;//assuming the suggestions are siblings
} else {
index = 0;
}
$(".suggestions").removeClass('selected');
$search.val($(".selected").eq(index).addClass('selected').text());
}
});
} else { //Clear old results
$("#res").empty();
}
Not sure if that's 100% correct as I've had to make a couple of assumptions, but the approach should be about right.
Related
I have the JS code below which filters based on checkboxes being checked or not (I don't think you need to see all the HTML because my question is rather simple/general, I think). All this code works fine, but I added a new function at the bottom (I noted it in the code) that simply has an uncheck all button for one of the sets of checkboxes (because there are like 30 checkboxes and I don't want the user to have to uncheck them all manually).
Anyway, the new script works properly too, except that the overall unrelated script that compares all checkboxes needs to run each time the new Uncheck All/Check All button is clicked.
Is there a simple way to make sure all the other JS runs when this new script is run?
I could be wrong, but I think I just need to somehow trigger this function inside the NEW FUNCTION:
$checkboxes.on('change', function() {
but am not sure how to do that.
ALL JS:
<script>
$(window).load(function(){
Array.prototype.indexOfAny = function(array) {
return this.findIndex(function(v) {
return array.indexOf(v) != -1;
});
}
Array.prototype.containsAny = function(array) {
return this.indexOfAny(array) != -1;
}
function getAllChecked() {
// build a multidimensional array of checked values, organized by type
var values = [];
var $checked = $checkboxes.filter(':checked');
$checked.each(function() {
var $check = $(this);
var type = $check.data('type');
var value = $check.data('value');
if (typeof values[type] !== "object") {
values[type] = [];
}
values[type].push(value);
});
return values;
}
function evaluateReseller($reseller, checkedValues) {
// Evaluate a selected reseller against checked values.
// Determine whether at least one of the reseller's attributes for
// each type is found in the checked values.
var data = $reseller.data();
var found = false;
$.each(data, function(prop, values) {
values = values.split(',').map(function(value) {
return value.trim();
});
found = prop in checkedValues && values.containsAny(checkedValues[prop]);
if (!found) {
return false;
}
});
return found;
}
var $checkboxes = $('[type="checkbox"]');
var $resellers = $('.Row');
$checkboxes.on('change', function() {
// get all checked values.
var checkedValues = getAllChecked();
// compare each resellers attributes to the checked values.
$resellers.each(function(k, reseller) {
var $reseller = $(reseller);
var found = evaluateReseller($reseller, checkedValues);
// if at least one value of each type is checked, show this reseller.
// otherwise, hide it.
if (found) {
$reseller.show();
} else {
$reseller.hide();
}
});
});
//NEW FUNCTION for "UNCHECK ALL" Button
$(function() {
$(document).on('click', '#checkAll', function() {
if ($(this).val() == 'Check All') {
$('input.country').prop('checked', true);
$(this).val('Uncheck All');
} else {
$('input.country').prop('checked', false);
$(this).val('Check All');
}
});
});
});
New button HTML for the new UNCHECK portion:
<input id="checkAll" type="button" value="Uncheck All">
I kept researching and discovered the trigger() function to handle this.
http://api.jquery.com/trigger/
I’m using angular.js to get some form data from some input fields. I need to build a feature that prevents a user from entering duplicate fields. So if a user entered duplicate fields I need alert the user with a alert box and than remove the duplicate. I know how to do this with jQuery, look at the code below. What is the most efficient way to achieve this with angular.js? Any help is greatly appreciated, I have template fiddle here to make it easier.
/** Handle Duplicate Barcodes **/
$(".alldifferent").on('keyup paste',function(){
var $this = $(this);
keyupDelay(function() {
var val = $this.val();
$this.attr('value', val);
if (val != '') {
var $dupes = $('input[value="' + val + '"]:gt(0)').val('');
if ($dupes.length > 0) alert('Error: Duplicates barcodes are not allowed!');
}
}, 300);
});
var keyupDelay = (function() {
var timer = 0;
return function(callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
Try ng-blur as a blur event fires when an element has lost focus.
If you can know the index at which the duplicate entry is being entered you can filter that item in the array and clear it.
$scope.check = function(val, index){
if(val !== '') {
for(var i = 0; i < $scope.num.length; i++ ){
var itm = $scope.num[i].text;
if(itm == val && i != index) {
$scope.num[index].text = '';
alert('Error: Duplicates barcodes are not allowed!');
break;
}
}
}
}
Working Plunker
Need your help and Thanks alot in advance.
I am trying to do the Add, Edit and delete the node using Dyna tree. Following things am trying.
When i click Add button by selecting any node then new node with textbox to be added and should take node name & on blur it should set value
If no name entered then textbox should disappear from tree node.
If existing nodes then edit the node - This is working.
Some functionalities i have achieved please review below jsfiddle and help me please
Below is my jsfiddle URL , Please help
$(function(){
$("#tree").dynatree({
onActivate: function(node) {
$("#info").text("You activated " + node);
},
children: [
{title: "Item 1"},
{title: "Folder 2", isFolder: true,
children: [
{title: "Sub-item 2.1"},
{title: "Sub-item 2.2"}
]
},
{title: "Item 3"}
],selectMode: 1,
checkbox: true,
onSelect: function(select, node) {
// Display list of selected nodes
var s = node.tree.getSelectedNodes().join(", ");
selectedNode = node;
},
onClick: function(node, event) {
if( event.shiftKey ){
editNode(node);
return false;
}
},
onDblClick: function(node, event) {
editNode(node);
return false;
},
onKeydown: function(node, event) {
switch( event.which ) {
case 113: // [F2]
editNode(node);
return false;
case 13: // [enter]
if( isMac ){
editNode(node);
return false;
}
}
}
});
var nodeExists = false;
var selectedNode = null;
function validateForm(){
if( selectedNode == null){
alert("Please select node to add folder");
return false;
}
if(selectedNode != null){
nodeExists = findNodeByTitle(selectedNode,$("#newFolderName").val());
return nodeExists;
}
}
function findNodeByTitle(tree, title){
var match = true;
tree.visit(function(node){
if(node.data.title == title) {
//match = node.data.title;
alert("Folder : "+title +" already exists")
match = false;
return false;
}
}, true);
return match;
}
function editNode(node){
var prevTitle = node.data.title,
tree = node.tree;
// Disable dynatree mouse- and key handling
tree.$widget.unbind();
// Replace node with <input>
$(".dynatree-title", node.span).html("<input id='editNode' value='" + prevTitle + "'>");
// Focus <input> and bind keyboard handler
$("input#editNode")
.focus()
.keydown(function(event){
switch( event.which ) {
case 27: // [esc]
// discard changes on [esc]
$("input#editNode").val(prevTitle);
$(this).blur();
break;
case 13: // [enter]
// simulate blur to accept new value
$(this).blur();
break;
}
}).blur(function(event){
// Accept new value, when user leaves <input>
var title = $("input#editNode").val();
console.log("onblur",title);
console.log("prevTitle",prevTitle);
if(title == ''){
openChildFunction(selectedNode);
}else{
node.setTitle(title);
// Re-enable mouse and keyboard handlling
tree.$widget.bind();
node.focus();
}
});
}
$("#btnAddCode").click(function(event){
// Sample: add an hierarchic branch using code.
// This is how we would add tree nodes programatically
event.preventDefault();
var node = $("#tree").dynatree("getActiveNode");
if( validateForm()){
var rx = /[<>:"\/\\|?*\x00-\x1F]|^(?:aux|con|clock\$|nul|prn|com[1-9]|lpt[1-9])$/i;
if(rx.test($("#newFolderName").val())) {
alert("Error: Input contains invalid characters!");
return false;
}
var node = $("#tree").dynatree("getActiveNode");
var childNode = selectedNode.addChild({
title: '',
});
$(".dynatree-title", childNode.span).html("<input id='editNode' value=''>");
var dict = $("#tree").dynatree("getTree").toDict();
}
});
});
Code
Jsfiddle tried example
Add removeNode function like this to delete the selected node if its empty:
function removeNode(node){
node.remove();
}
change the blur event like this to call removeNode on empty titles:
.blur(function(event){
var title = $("input#editNode").val();
//removes the node if title is empty
if(title == ""){
removeNode(node);
tree.$widget.bind();
return;
}
....
});
finally change btnAddCode's click event like this to manage adding:
get the selected node using selectedNode = $("#tree").dynatree("getActiveNode")
add child element using addChild method
expand the parent node like this :selectedNode.expand(true)
and finally call the editNode function for newly added node
The btnAddCode's click event should look like this:
$("#btnAddCode").click(function(event){
event.preventDefault();
selectedNode = $("#tree").dynatree("getActiveNode");
if( validateForm()){
var rx = /[<>:"\/\\|?*\x00-\x1F]|^(?:aux|con|clock\$|nul|prn|com[1-9]|lpt[1-9])$/i;
if(rx.test($("#newFolderName").val())) {
alert("Error: Input contains invalid characters!");
return false;
}
var childNode = selectedNode.addChild({
title: "My new node",
tooltip: "This folder and all child nodes were added programmatically."
});
selectedNode.expand(true);
editNode(childNode);
}
});
Edit:
you should change the blur event to prevent a tree category having multiple nodes with the same title.get child list of the parent node and check if any of them except the editing node,has same title as the editing node or not,if so, let the user know and return.
so adding this code to blur event should do the trick:
var parentChilds = node.parent.childList;
var titleAvalible = false;
$.each(parentChilds,function(_index){
if(this.data.key != node.data.key && this.data.title == title){
titleAvalible = true;
}
});
if(titleAvalible){
alert("A node with same title is avalible");
return;
}
I also updated the fiddle.
hope that helps.
The page moves back up to the top when you click any of the buttons under the second "Clients" heading here: http://kodiakgroup.com/clients.php
I have tried the preventDefault function as well as return false per suggestion here in the on change functions you can see below. See what I can do to prevent this behavior?
Part I changed:
//Toggle select all/deselect function
$('#vertical-filters input').change(function (e) {
$('.selectAllBoxes').prop('checked', false);
getCustomers();
e.preventDefault();
return false;
});
$('.selectAllBoxes').change(function (e) {
$('#vertical-filters input').prop('checked', false);
getCustomers();
e.preventDefault();
return false;
});
All of the javascript:
$(function () {
$('.selectAllBoxes').prop('checked', true);//Set checkboxes as checked by default
getCustomers(); //Initially call all customers
function getCustomers()
{
$('ul#customers').html('');//empty list
var definedCategoryArray=new Array();
var categoriesPlural= new Array();
var customerSplit=new Array();
for(var x=0; x< $('#vertical-filters input').length; x++){
var thisItem=$('#vertical-filters input')[x];
var thisItemName=$(thisItem).attr('id');
if($('.selectAllBoxes').is(':checked')){
definedCategoryArray[thisItemName]=true;
}
else{
if ($(thisItem).is(':checked'))
definedCategoryArray[thisItemName]=true;
else
definedCategoryArray[thisItemName]=false;
}
}
$.getJSON('customers.json', function(data) {
for(var index in definedCategoryArray){ //cycle through categories array
for(var i=0; i<data.customers.length; i++){ //cycle through json data
if (definedCategoryArray[index]==true){//if the value in the array is true (item checked)
//console.log(data.customers[i].customerName+ ' : ' + data.customers[i].category);
if(data.customers[i].category.indexOf(',') != -1) //if there is more than one category, detect the comma seperating them
categoriesPlural = data.customers[i].category.split(',');
else //there is only one category
categoriesPlural[0]=data.customers[i].category;
for (var y = 0; y<categoriesPlural.length; y++){
//console.log(categoriesPlural[y]);
if(categoriesPlural[y] == index){ //match category (from definedCategoryArray index) to items in json object to parse
$('ul#customers').append('<li class="' +data.customers[i].customerName.replace(/\s+/g, '-') + '" id="'+data.customers[i].customerName.replace(/\s+/g, '-')+'"><img src="'+ data.customers[i].imageLink +'" alt="'+ data.customers[i].customerName +'" /></li>');
checkDuplicates(data.customers[i].customerName.replace(/\s+/g, '-'));
}
}
}
}
}
}).fail(function() { console.log( "error" ); });
}
function checkDuplicates(customerName){
for(var x=0; x< $('#customers li').length; x++){//loop through clients already on the page to prevent duplicates
var thisClient=$('#customers li')[x];
var thisClientName=$(thisClient).attr('id');
if(thisClientName == customerName){
var superClient1=$('.'+customerName)[1];
var superClient2=$('.'+customerName)[2];
if (superClient1)
$(superClient1).css('display','none');
if(superClient2)
$(superClient2).css('display','none');
//console.log(customerName + '=' + thisClientName + ' emptied');
}
}
}
//Toggle select all/deselect function
$('#vertical-filters input').change(function (e) {
$('.selectAllBoxes').prop('checked', false);
getCustomers();
e.preventDefault();
return false;
});
$('.selectAllBoxes').change(function (e) {
$('#vertical-filters input').prop('checked', false);
getCustomers();
e.preventDefault();
return false;
});
});
It's not actually going back to top, but you are removing items. The page is shrinking and scroll disapear then you add item and the page expand without scrolling back.
An easy hack to do is to fix the heigh of the ul before removing items and then remove the attribute style. Like that :
$('#vertical-filters input').change(function (e) {
$('.selectAllBoxes').prop('checked', false);
$('ul#customers').height($('ul#customers').height()); //fix the height
getCustomers();
$('ul#customers').removeAttr('style'); //Reset the height
});
Repeat for all the .change() functions.
It is not tested but in theory, it should work
That's because you are removing the content from the ul#customers container, check this line in your HTML
function getCustomers()
{
$('ul#customers').html('');//empty list
...
}
There are some workarounds to avoid this scroll, you can check this post
As another answer here suggests, try setting a height on the ul. This would be my approach:
function getCustomers() {
var $customers = $('ul#customers');
$customers.css('height', $customers.height());
$customers.html(''); //empty list
// the rest of your getCustomers() function
// at the very end, remove the height
$customers.css('height', '');
}
So, you start with explicitly setting a height on the ul. This will keep it from collapsing. Then, you can empty it and add in the new content. At the very end, you remove the height, and the ul will collapse to whatever height its contents require.
This will probably still be a little jolting. You could consider animating the height either with jQuery $.animate() or CSS3 animations
Thanks guys! Both were good answers so I wasn't sure which one to mark. I actually just set a min-height on the container and it fixed it! :)
I have a search suggestion div that appears when you keyUp an input. This works fine, but now I am trying to make keyboard shortcuts in action.
I want a behavior like when you click down keyboard arrow button a span gets selected and if it is selected then another span that is after gets selected, similarly, if you click up arrow an upward span gets selected, when you click enter then link opens.
I am stuck because I could not remove a:hover and could not add classes to it. Even after I have basically no idea how to do it. But I really tried hard and a lot..
Here is a jsfiddle link (type anything in field). maybe somebody will help me.
This code should go when the request is made and data is being returned:
<script type="text/javascript">
$(document).ready(function(){
total = 3;
$(".result-item").mouseenter(
function(){
hovered = $(this).attr("id");
total = 3;
$(".result-item").each(function(){
$(this).children("a").css({
'background-color':'#e4e4e4',
'color':'#000000'
});
$(this).find(".searchheading").css({
'color':'#191919'
});
$(this).find(".searchcaption").css({
'color':'#555555'
});
});
$(this).children("a").css({
'background-color':'#b7b7b7',
'color':'#ffffff'
});
$(this).find(".searchheading").css({
'color':'#ffffff'
});
$(this).find(".searchcaption").css({
'color':'#f1f1f1'
});
}
);
});
</script>
And this code on a page where request is made:
$("#suggestions").hide();
$("#search").bind('keyup', function(event){
if (event.which == 40 || event.which == 38 || event.which == 13) return false;
else
{
hovered = undefined;
lookup($(this).val());
}
});
$("#search").bind('keydown', 'down', function(evt){
if ($("#suggestions").is(":visible"))
{
if (typeof hovered == 'undefined')
{
$("#result-item-0").trigger("mouseenter");
return;
}
count = parseInt($("#"+hovered).attr("count"));
next = (count + 1);
if (next == total)
next = 0;
$("#result-item-"+next).trigger("mouseenter");
}
});
$("#search").bind('keydown', 'up', function(evt){
if ($("#suggestions").is(":visible"))
{
if (typeof hovered == 'undefined')
{
$("#result-item-"+(total-1)).trigger("mouseenter");
return;
}
count = parseInt($("#"+hovered).attr("count"));
prev = (count - 1);
if (prev == -1)
prev = (total-1);
$("#result-item-"+prev).trigger("mouseenter");
}
});
$("#search").bind('keydown', 'return', function(evt){
if ($("#suggestions").is(":visible"))
{
if (typeof hovered == 'undefined')
{
str = $("#search").val();
window.location.href = urlencode(str); // urlencode is a custom function
return false;
}
count = parseInt($("#"+hovered).attr("count"));
current = count;
$("#result-item-"+current).trigger("mouseenter");
$("#suggestions").fadeOut();
window.location.href = $("#"+hovered).children("a").attr("href");
}
});
})
;
Also I removed onkeyup="" attribute on element, this approach is nicer.