I'm trying to mimic the Google suggestions list with this:
function el(tid) {
return document.getElementById(tid);
}
function addScript(u) {
var head = document.getElementsByTagName('head')[0],
sc2 = document.createElement('script');
sc2.src = u;
head.appendChild(sc2);
setTimeout(function () {
head.removeChild(sc2);
sc2 = null;
}, 600);
} //end addScript()
function suggest(data) {
var sel = el("test");
sel.innerHTML = '';
for (x = 0; x < data[1].length; x++) {
sel.innerHTML += '<li class="uli" >' + data[1][x][0] + '</li>';
}
}
el("inp").onkeyup = function () {
addScript("http://www.google.nl/complete/search?callback=suggest&q=" + this.value);
};
The problem is that I want to be able to come down in the suggestions list using the arrow keys, and secondary I want to show the 'current' suggestion value inside the input field. So I tried something like this using Jquery:
$("#inp").live("keydown", function (e) {
var curr = $('#test').find('.current');
if (e.keyCode == 40) {
if (curr.length) {
$(curr).attr('class', 'uli');
curr = $(curr).next();
}
if (curr.length) {
curr.attr('class', 'uli current');
} else {
$('#center li:first-child').attr('class', 'uli current');
}
}
if (e.keyCode == 38) {
if (curr.length) {
$(curr).attr('class', 'uli');
curr = $(curr).prev();
}
if (curr.length) {
curr.attr('class', 'uli current');
} else {
$('#center li:last-child').attr('class', 'uli current');
}
}
$("#inp").live("keydown", function (e) {
var search_terms = $('li.current').text();
if (e.keyCode == 40) {
$('#inp').val(search_terms);
}
if (e.keyCode == 38) {
$('#inp').val(search_terms);
}
It doesn't work because (I think..) the 'current' suggestion is immediately being requested by the previous code.
I have put everything over here: JS Bin
Why recreate the wheel?
jQuery UI Autocomplete
Or look at other plugins
The problem with your code is you need to cancel out the arrow keypresses when you call to get the values.
el("inp").onkeyup = function () {
//if not the arrow keys, fetch the list
if( ... ){
addScript("http://www.google.nl/complete/search?callback=suggest&q=" + this.value);
}
}
Also what is with el("inp") when you are using jQuery? I expected to see $("foo").keyup( function(){} );
Related
I'm intending to build an autocomplete search box that searches through suggest_data which approximately consists of 12,000+ data. The jquery takes value from search box, searches though the data array and stores the suggestion values in array which will be further used to populate the suggestion.
<input type="text" id="Search_box" placeholder="Search Data">
$('#Search_box').on('input', function () {
suggester(document.getElementById("Search_box"));
});
The following function takes input value from search box and searches through search_array which has 12,000+ records and gives a list of data that have those characters in it as a suggestion.
function suggester(searchElement) {
var init;
searchElement.addEventListener("input", function(e) {
var x, y, i, val = this.value;
closeAllLists();
if (!val) { return false;}
init = -1;
var search_value = $('#Search_box').val();
suggest_data = [];
re = new RegExp("\\b\\w*" + search_value + "\\w*\\b", "ig");
for (i = 0; i < search_array.length; i++) {
if(search_array[i].match(re))
suggest_data = suggest_data.concat(search_array[i].match(re));
}
x = document.createElement("DIV");
x.setAttribute("id", this.id + "autocomplete-list");
x.setAttribute("class", "autocomplete-items");
x.setAttribute("onClick", "this.setSelectionRange(0, this.value.length)");
this.parentNode.appendChild(x);
for (i = 0; i < suggest_data.length; i++) {
y = document.createElement("DIV");
val = truncate(suggest_data[i],30);
reg = new RegExp(search_value, "ig");
n = val.search(reg);
res = suggest_data[i].substr(n,search_value.length);
val = val.replace(res, '<span class="text_highlighter">'+res+'</span>');
y.innerHTML = val;
y.innerHTML += "<input type='hidden' value='" + suggest_data[i] + "'>";
y.addEventListener("click", function(e) {
searchElement.value = this.getElementsByTagName("input")[0].value;
closeAllLists();
$('#Search_box').focus();
});
x.appendChild(y);
$('#Search_box').focus();
}
});
searchElement.addEventListener("keydown", function(e) {
var x = document.getElementById(this.id + "autocomplete-list");
if (x) x = x.getElementsByTagName("div");
if (e.keyCode == 40) {
init++;
addActive(x);
} else if (e.keyCode == 38) { //up
init--;
addActive(x);
} else if (e.keyCode == 13) {
e.preventDefault();
if (init > -1) {
if (x) x[init].click();
}
}
});
function addActive(x) {
if (!x) return false;
removeActive(x);
if (init >= x.length) init = 0;
if (init < 0) init = (x.length - 1);
x[init].classList.add("autocomplete-active");
}
function removeActive(x) {
for (var i = 0; i < x.length; i++) {
x[i].classList.remove("autocomplete-active");
}
}
function closeAllLists(elmnt) {
var x = document.getElementsByClassName("autocomplete-items");
for (var i = 0; i < x.length; i++) {
if (elmnt != x[i] && elmnt != searchElement) {
x[i].parentNode.removeChild(x[i]);
}
}
}
document.addEventListener("click", function (e) {
closeAllLists(e.target);
});
}
But this approach is slowing down when backspace is clicked. The addEventListener function is triggered multiple times hence it is looping inside the suggested function inorder to give result. Example if ASD is entered and if backspace is typed further on then it starts slowing down while giving result. What should be done inorder to speed up search?
On every keydown your eventListener is triggering your autocomplete function. When someone clicks backspace, you're getting a heap of keydown's at the same time, and right now, that means you're running the function a number of times at the same time. This is what's slowing everything down.
For example, if I were to type, "Hello my name is" and then delete the
entire string, I'd have triggered your search 16 times, searching
12,000 x 16 records! You can start to see why this would become an issue.
A debounce function gets around this issue quite easily as it sets a delay between running again. Ie. if someone has just erased the entire string, it may just trigger once.
This is a great example of a debounce function:
// http://davidwalsh.name/javascript-debounce-function
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) { func.apply(context, args); }
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) { func.apply(context, args); }
};
}
And this is how you would implement it on your code for autocomplete:
searchElement.addEventListener("keydown", debounce(function(e) {
var x = document.getElementById(this.id + "autocomplete-list");
if (x) x = x.getElementsByTagName("div");
if (e.keyCode == 40) {
init++;
addActive(x);
} else if (e.keyCode == 38) { //up
init--;
addActive(x);
} else if (e.keyCode == 13) {
e.preventDefault();
if (init > -1) {
if (x) x[init].click();
}
}
}, 500)); // Time set here as callback for how often it should be run, change this to whatever you want the time delay to be.
I am trying to display multiple child elements in jQuery but only the first child element gets displayed. I can force the browser to show all of them through Inspect and changing the css display from none to block. Can anyone help me find the problem in my code.I use Radio buttons, this is the code :
<script type="text/javascript">
$(document).ready(function () {
$('##radioButtonListSectionId input[type="radio"]
[checked="checked"]').each(function () {
ShowRadioButtonListDependentField(this, false);
});
});
$('##radioButtonListSectionId input[type="radio"]').change(function
() {
var result = $(this).val();
$('##uniqueID-hidden').val(result);
ShowRadioButtonListDependentField(this, false);
});
function ShowRadioButtonListDependentField(element, show) {
debugger;
var fieldKey = $(element).val(), children;
var currentId = element.attributes["currentid"].value;
if (currentId != 0) {
if ($('.main-dialogbox.modal.fade.in').length > 0)
children = $('.modal-body .control-group[parentid=' +
currentId + ']');
else if ($('.idea-task.open').length > 0)
children = $('.idea-task .control-group[parentid=' +
currentId + ']');
else
children = $('.control-group[parentid=' + currentId +
']');
if ($(element).is(":checked") && $(element).is(":visible"))
show = true;
else
show = false;
children.hide();
children.each(function () {
var keys =
this.attributes["parentOptionKey"].value.split("</br>");
var haschildren = this.attributes["haschildren"].value;
for (var i = 0; i < keys.length; i++) {
if (keys[i] == fieldKey) {
if (show) {
$(this).show();
show = false;
$(this.getElementsByClassName("ishidden")).val("False");
} else {
$(this).hide();
$(this.getElementsByClassName("ishidden")).val("True");
if (haschildren.toLowerCase() == "true") {
ShowCheckListDependentField(this,
false);
}
}
}
else {
$(this.getElementsByClassName("ishidden")).val("True");
}
}
});
}
}
</script>
I have a checkbox. If I checks it, it will select all results:
HTML:
<input type="checkbox" id="selectallcheckbox" onClick="toggle(this)" />
And Javascript:
function toggle(source = false) {
if(!source)
{
var source = document.getElementById('selectallcheckbox');
}
checkboxes = document.getElementsByName('id[]');
for(var i=0, n=checkboxes.length;i<n;i++) {
checkboxes[i].checked = source.checked;
var id = checkboxes[i].id;
var res = id.replace("checkbox", "tr");
if(source.checked)
{
$('#' + res + '').addClass('selected');
} else {
$('#' + res + '').removeClass('selected');
}
}
}
Now I am trying to select all results if I click CTRL+A on my keyboard. Here is my JavaScript:
jQuery(document).keydown(function(e) {
if (e.ctrlKey) {
if (e.keyCode == 65 || e.keyCode == 97) { // 'A' or 'a'
e.preventDefault();
toggle();
}
}
});
But this function is not works. How can I make ctrl + a to select all results
The toggle function requires an argument or nothing. When no argument is passed the argument itself can be got directly from the dom. In the html the toggle function get the this keyword. The element itself is passed. But if no argument is passed the argument is undefined and so it can be computed dynamically:
<input type="checkbox" id="selectallcheckbox" onClick="toggle(this)"/>
In the toggle function try to change from:
function toggle(source = false) {
if(!source)
{
var source = document.getElementById('selectallcheckbox');
}
...........
to:
function toggle(source) {
if (source === undefined) {
source = document.getElementById('selectallcheckbox');
}
and, finally, in your jQuery(document).keydown(function(e) { change to:
jQuery(document).keydown(function(e) {
if (e.ctrlKey) {
if (e.keyCode == 65 || e.keyCode == 97) { // 'A' or 'a'
e.preventDefault();
// get the argument to the toggle function
var eleObj = document.getElementById('selectallcheckbox');
// toggle the checkbox status
eleObj.checked = !eleObj.checked;
// call the toggle function with the correct argument
toggle(eleObj);
}
}
});
I found solution:
jQuery(document).keydown(function(e) {
if (e.ctrlKey) {
if (e.keyCode == 65 || e.keyCode == 97) { // 'A' or 'a'
e.preventDefault();
document.getElementById("selectallcheckbox").click();
}
}
});
From another stackoverflow post (How can I add a JavaScript keyboard shortcut to an existing JavaScript Function?) I have this hotkey code:
function doc_keyPress(e) {
if (e.shiftKey && e.keyCode == 80) {
//do something
}
}
document.addEventListener('keyup', doc_keyPress, false);
which works with two keys. But with three keys, shift + l + m for example, it does not work.
the if statement would be:
if (e.shiftKey && e.keyCode == 76 && e.keyCode == 77) {}
again this does not work.
How do I get this working for shift + l + m.
tricky, tricky, but I managed to get it working. Just be aware that browsers have their own hot keys (like chromes [ctrl]+[shift]+i) which may override the function.
<!DOCTYPE html>
<html>
<body>
<input id="myInput" onkeydown="keyDownEvent(event)" onkeyup="resetKeys()">
</body>
</html>
<script>
var key1Pressed=false;
var key2Pressed=false;
function resetKeys(){
key1Pressed=false;
key2Pressed=false;
}
function keyDownEvent(e){
e=e||event, chrCode=(typeof e.which=="number")?e.which:e.keyCode;
if (e.shiftKey && chrCode === 76) key1Pressed=true;
if (e.shiftKey && chrCode === 77) key2Pressed=true;
if(key1Pressed && key2Pressed){
alert('Three Keys Are Pressed');
key1Pressed=false;
key2Pressed=false;
}
}
document.getElementById('myInput').focus();
</script>
Using a closure, I would envisage you can do something like this
var doc_keypress = (function() {
var prevWasL = false;
return function(e) {
if (e.type == 'keypress') {
if (e.shiftKey && !(e.ctrlKey || e.metaKey)) {
if (prevWasL) {
if (e.charCode == 77) {
console.log('doing it');
prevWasL = false;
return;
}
}
if (e.charCode == 76) {
prevWasL = true;
return;
}
}
prevWasL = false;
} else { // keyup
if (e.key == 'Shift') {
prevWasL = false;
}
}
}
}());
document.addEventListener('keypress', doc_keypress);
document.addEventListener('keyup', doc_keypress);
Add both keypress AND keyup event listeners so that the scenario of
Shift + L, release both, Shift + M, doesn't trigger a false positive
This would require shift then L then M being pressed in that order ... if you want either order of L and M, then the code would be a little different, but you should be able to figure that out
NOTE: I use charCode, because firefox at least, keyCode is always 0 on keyPress event
If you're trying to double press or triple press keys and catch an event after this, I've written a simple helper:
function KeyPress(_opts) {
this.opts = Object.assign({}, {
counts: {},
timeouts: {},
timeBetweenPresses: 300
}, _opts || {});
}
KeyPress.prototype.bubbledReset = function bubbledReset(keyCode) {
var self = this;
if (this.opts.timeouts[keyCode]) {
clearTimeout(this.opts.timeouts[keyCode]);
this.opts.timeouts[keyCode] = 0;
}
this.opts.timeouts[keyCode] = setTimeout(function () {
self.opts.counts[keyCode] = 0;
}, this.opts.timeBetweenPresses);
};
KeyPress.prototype.onTap = function onTap(cb) {
var self = this;
return function handler(event) {
self.opts.counts[event.keyCode] = self.opts.counts[event.keyCode] || 0;
self.opts.counts[event.keyCode]++;
self.bubbledReset(event.keyCode);
cb(event.keyCode, self.opts.counts[event.keyCode]);
};
};
Usage
Simply use the onTap method to instance:
var keyPress = new KeyPress();
document.addEventListener('keyup', keyPress.onTap(function (keyCode, count) {
if (keyCode === 68 && count === 3) {
// 68 was tapped 3 times (D key)
}
if (keyCode === 13 && count === 6) {
// 13 was tapped 6 times (ENTER key)
}
}));
Hope this helps someone else!
Or if you prefer es6:
class KeyPress {
constructor(_opts) {
this.opts = Object.assign({}, {
counts: {},
timeouts: {},
timeBetweenPresses: 300
}, _opts || {});
}
bubbledReset(keyCode) {
if (this.timeouts[keyCode]) {
clearTimeout(this.timeouts[keyCode]);
this.timeouts[keyCode] = 0;
}
this.timeouts[keyCode] = setTimeout(() => {
this.counts[keyCode] = 0;
}, this.timeBetweenPresses);
}
onTap(cb) {
return event => {
this.counts[event.keyCode] = this.counts[event.keyCode] || 0;
this.counts[event.keyCode]++;
this.bubbledReset(event.keyCode);
cb(event.keyCode, this.counts[event.keyCode]);
};
}
}
I've searched but frankly, do not know enough about JS to make sense of all of the other "combine these 2 functions" posts already out there.
I am using a script to slide out a Contact Panel. I duplicated this script to then slide out the About Panel.
I want to consolidate both into one script to tidy things up. Possible?
Contact Panel:
<script type="text/javascript">
function showContactPanel() {
var elem = document.getElementById("contact-panel");
if (elem.classList) {
elem.classList.toggle("show");
} else {
var classes = elem.className;
if (classes.indexOf("show") >= 0) {
elem.className = classes.replace("show", "");
} else {
elem.className = classes + " show";
}
console.log(elem.className);
}
}
</script>
Duplicated for the About Panel:
<script type="text/javascript">
function showAboutPanel() {
var elem = document.getElementById("about-panel");
if (elem.classList) {
elem.classList.toggle("show");
} else {
var classes = elem.className;
if (classes.indexOf("show") >= 0) {
elem.className = classes.replace("show", "");
} else {
elem.className = classes + " show";
}
console.log(elem.className);
}
}
</script>
You could pass the panel ID as a parameter:
function showPanel(id) {
var elem = document.getElementById(id);
if (elem.classList) {
elem.classList.toggle("show");
} else {
var classes = elem.className;
if (classes.indexOf("show") >= 0) {
elem.className = classes.replace("show", "");
} else {
elem.className = classes + " show";
}
console.log(elem.className);
}
}
and then call it that way:
showPanel("about-panel");
or
showPanel("contact-panel");
So change your function signature to take a parameter for the element in question:
function showPanel(panelId) {
var elem = document.getElementById(panelId);
...
and call it:
showPanel("contact-panel");
You could pass the id of the panel as an argument to the function:
<script type="text/javascript">
function showPanel(panelId) {
var elem = document.getElementById(panelId);
if (elem.classList) {
elem.classList.toggle("show");
} else {
var classes = elem.className;
if (classes.indexOf("show") >= 0) {
elem.className = classes.replace("show", "");
} else {
elem.className = classes + " show";
}
console.log(elem.className);
}
}
</script>
See JavaScript Function Parameters for more information on function parameters.