I want to check the amount of typing characters in an input using keyup and keydown.
I've done it earlier but not as separate function and it was a big mess in a code. I use
event and when I did it without separate function everything worked great, but now I have a problem with send it through the functions. Maybe it's caused by totally wrong method to do this, so please give me some advice, how to do it properly.
This is my separate function:
function char_check($input_id, $div_id, $char_max, event)
{
if(($char_max-$($input_id).val().length)<=0)
{
var key = event.keyCode || event.charCode;
if(key!=8)
{
event.preventDefault();
}
}
if(($char_max-$($input_id).val().length)<0)
{
$($div_id).text("Your data will be cut short!");
}
else
{
$($div_id).text($char_max-$($input_id).val().length);
}
}
And this is the way I call this function:
$('input[name=author]').keydown(function(e){
char_check($(this), "#chars_auth", 50, e);
});
Problem: Event doesn't work.
As I understand it the code does not work, though I am not sure what does not work. Here are some suggestions. First I would create a function that builds your "check function". Something like:
var createCharCheck = function (div_id, char_max) {
return function (event) {
if ((char_max - $(this).val().length) <= 0) {
var key = event.keyCode || event.charCode;
if(key != 8) {
event.preventDefault();
}
}
if((char_max - $(this).val().length) < 0) {
$(div_id).text("Your data will be cut short!");
} else {
$(div_id).text(char_max - $(this).val().length);
}
}
}
$('input[name=author]').keydown(createCharCheck('#chars_auth', 50));
This is not to be fancy, but save you some code and take advantage of closure. Now you have a function that you can use on any input. Hope this helps.
Related
i am complete newbie in jquery/js
I am trying to create web interface for my robotcar and check for multiple keypresses, whenever i release all keys robotcar would stop.
http://jsfiddle.net/gFcuU/1105/
var keys = {};
$(document).keydown(function (e) {
keys[e.which] = true;
keypr();
printKeys();
});
$(document).keyup(function (e) {
delete keys[e.which];
printKeys();
});
function printKeys() {
var html = '';
for (var i in keys) {
if (!keys.hasOwnProperty(i)) continue;
html += '<p>' + i + '</p>';
}
$('#out').html(html);
}
function keypr(){
if (keys[87] && keys[68] == true){
alert('shit works');
}
}
Multiple keypress detection works but if i press W+D stated in function keypr it stops working properly.
Thanks for help
Dude. Your code is working perfectly, but when you do the alert while detecting keypresses, it gets stuck.
Just remove the alert and everything is gonna be fine. if you don't wanna remove the alert i guess you have to remove the object, before displaying the alert.
function keypr(){
if (keys[87] && keys[68] == true){
delete keys[68];
delete keys[87];
alert('shit works');
}
I have created two functions. To keep it simple lets take for an example the following:
I got functions firing different events for the same objects. You can activate them using your keyboard arrows
$("body").keydown(function(e) {
if (event.which == 39) open_second_layer();
});
$("body").keydown(function(e) {
if (event.which == 37) open_first_layer();
});
As soon as I have fired one function and press the same key again it fires the animation one more time (unnecessarily).
Because of that as soon as the function open_second_layer has been fired, it should not be able to be fired again, until open_first_layer is fired again. The same should be the case the other way round.
I found .bind and .when as possible solutions, but can't figure out how to use them the right way for that case. I appreciate every suggestions or keywords to google.
You can keep a state variable and track when changes are made to it:
var state_changed = (function() {
var current = null;
return function(state) {
if (state == current) {
return false;
}
current = state;
return true;
};
}());
function open_first_layer()
{
if (!state_changed(1)) {
return;
}
// rest of code
}
function open_second_layer()
{
if (!state_changed(2)) {
return;
}
// rest of code
}
$("body").keydown(function(e) {
if (event.which == 39) {
open_second_layer();
} else if (event.which == 37) {
open_first_layer();
}
});
You can use jQuery's one().
In your first click handler, you bind the second one.
In your second click handler, you bind the first one.
sample
<div id=activate-first>first</div>
<div id=activate-second style="display:none;">second</div>
$(document).ready(function () {
function slide_first(){
$('#activate-first').show();
$('#activate-second').hide();
$('#activate-second').one('click', slide_first);
};
function slide_second(){
$('#activate-first').hide();
$('#activate-second').show();
$('#activate-first').one('click', slide_second);
};
$('#activate-first').one('click', slide_second);
$('#activate-second').one('click', slide_first);
});
Put the other function inside slide_first, like:
function slide_first(){
// other code
$('#activate_second').one('click', slide_second);
}
$('#activate_first').one('click', slide_first);
or use an Anonymous function to do the same:
$('#activate_first').one('click', function(){
// slide_first code here
$('#activate_second').one('click', function(){
// slide_second code here
});
});
Maybe your really want:
function recursiveSlider(){
$('#activate_first').one('click', function(){
// slide_first code here
$('#activate_second').one('click', function(){
// slide_second code here
recursiveSlider();
});
});
}
recursiveSlider();
This is a perfect use case for delegation. You have a single click event, and whenever the event happens, you determine what has been clicked, and you take action accordingly:
$(document.body).on("click", function(ev) {
var $targ = $(ev.target);
if ($targ.is('#button_1')) {
// someone clicked #button_1
}
if ($targ.is('.page-2 *')) {
// something inside of .page-2 was clicked!!
}
});
UPDATE: now the OP has included more code, I'm not sure the issue is - there's no need to bind and unbind events...
http://jsfiddle.net/ryanwheale/uh63rzbp/1/
function open_first_layer() {
$('#first_panel').addClass('active');
$('#second_panel').removeClass('active');
}
function open_second_layer() {
$('#first_panel').removeClass('active');
$('#second_panel').addClass('active');
}
// one event === good
$("body").keydown(function(e) {
if (event.which == 39) open_second_layer();
if (event.which == 37) open_first_layer();
});
... or if you're trying to build a slider, I suggest changing your naming convention:
http://jsfiddle.net/ryanwheale/uh63rzbp/2/
var current_layer = 1,
$all_layers = $('[id^="panel_"]'),
total_layers = $all_layers.length;
function move_layer (dir) {
current_layer += dir;
if (current_layer < 1) current_layer = total_layers;
else if (current_layer > total_layers) current_layer = 1;
$all_layers.removeClass('active');
$('#panel_' + current_layer).addClass('active');
}
// one event === good
$("body").keydown(function(e) {
if (event.which == 39) move_layer(1);
if (event.which == 37) move_layer(-1);
});
move_layer(0);
(edited some spelling errors)
So here's a piece of code that repeats itself with every answer the user gives to an MPC-question:
It fires the question, generates 4 answers, binds 'click' and 'mouseover' to the answers, and waits for the user to actually click on one of them.
When he does, it checks if it was right or wrong, displays that to the user, and then waits for another input (anywhere in the document, this time) before it proceeds and repeats.
Now, the idea is that a user can either click with the cursor, OR use QWER to answer. And as I'm am quite new to manipulation of an event and its data, I found a (flawed, apparently) way to separate the two, as you'll find below.
However, when a user 'answers' by clicking, and 'proceeds' by QWER, it waltzes through the whole thing too fast, by actually firing the question as intended, and then immediately answering it AND firing another question AGAIN. (And after extracting the time it takes for this into an array, I found that it sometimes does this 3-6 times in a row, with no apparent reason for this how many times at all...)
Now, as mentioned, I am new when it comes to events, so there may be redundancies and/or wrong uses of (e), but bear with me. I expect the bug to be related to this, of course. Also, some functions are irrelevant here, because they lead back to whatever function their call is in (right(); for example, does not fire anything after itself).
It's the loop that's the point here.
Thanks in advance!
Here's the code:
function keyAns() {
answered="no";
draw(0);
}
function keyNotAns (A) {
B = $(A).children('.answer');
if ($(B).hasClass('right')) {
// do something
}
else if ($(B).hasClass('wrong')) {
// do something
}
answered = "yes";
}
function waitForInput() {
// MOUSE CLICK
$(document).click(function() {
if (answered == "yes") {
answered = "no";
draw(0);
}
});
$('.answer').click(function(e) {
$('.answer').unbind('click');
if (answered == "no") {
calcTime(1);
e.stopPropagation();
if ($(this).hasClass('right')) {
// do something
}
else if ($(this).hasClass('wrong')) {
// do something
}
answered = "yes";
}
});
// KEYPRESSES
$(document).bind('keyup', function(e){
$(document).unbind('keyup');
if (answered == "yes") {
e.stopPropagation();
keyAns();
}
else if ( answered == "no") {
calcTime(1); // irrelevant
if(e.which == 81 || e.keyCode == 81) { // Q
AAA = '#ansQ';
e.stopPropagation();
keyNotAns(AAA);
}
else if(e.which == 87 || e.keyCode == 87) { // W
AAA = '#ansW';
e.stopPropagation();
keyNotAns(AAA);
}
else if(e.which == 69 || e.keyCode == 69) { // E
AAA = '#ansE';
e.stopPropagation();
keyNotAns(AAA);
}
else if(e.which == 82 || e.keyCode == 82) { // R
AAA = '#ansR';
e.stopPropagation();
keyNotAns(AAA);
}
else {
}
waitForInput();
}
});
}
Every time that there's a keypress event, you rebind everything.
This means that if you push 10 keys, you will have 10 onclick listeners. So when a user then clicks, the callback will run 10 times in a row.
The code that you posted doesn't include the original call to the waitForInput function, but you only need to call it once, so you can delete it from this code.
A quick introduction to jquery events:
When you bind, every time that event occurs (on the element you put it to), the callback function you provided will run.
Another tip is that in more recent versions of jQuery, there in an alternative to bind named one. It does the same, but it will only run the first time. Although in this situation, you don't need it.
Your problem is that inside your keybind, you're calling waitForInput.
Asynchronous programming takes some getting used to, but what the functions do inside waitForInput is set up event listeners, and any time the event happens, those listeners fire.
The problem you're seeing is that after handling an event, you're adding more event listeners, and next time the event fires, the listener will fire multiple times.
Simply take the waitForInput() line out of the function, and put it at the bottom of your code. Then it will run only once, and you'll be fine. (It wouldn't hurt to rename it to something like setupEventListeners, to avoid confusion.)
Found it!
All thanks to Scott Mermelstein and ColBeseder, both equally invaluable to my solution! Not only did they find the flaws, they made me understand how events and binds work. So thanks a million, you two!
Indeed:
binds were being stacked, therefore IF it fired, it would fire excessively, also;
the self-call of waitForinput() was indeed unneeded, however;
one e.stopPropagation() was also needed, to prevent the loop from working with wrong event-data, which lead the function to interpret it as the next answer and fire itself again.
Plus: due to laziness - I didn't feel like too much trial and error - I wanted to stay on the safe side, so I added all unbind()'s as first-thing when the function fires.
The result, which works flawlessly, for those who're interested:
function keyNotAns (A) {
B = $(A).children('.answer');
if ($(B).hasClass('right')) {
// do something
}
else if ($(B).hasClass('wrong')) {
// do something
}
answered = "yes";
}
function waitForInput() {
$(document).unbind('keyup');
$(document).unbind('click');
$('.answer').unbind('click');
// MOUSE CLICKS
$(document).click(function() {
if (answered == "yes") {
answered = "no";
draw(0);
}
});
$('.answer').click(function(e) {
if (answered == "no") {
calcTime(1);
if ($(this).hasClass('right')) {
// do something
}
else if ($(this).hasClass('wrong')) {
// do something
}
e.stopPropagation();
answered = "yes";
}
});
// KEYPRESSES
$(document).bind('keyup', function(e){
if (answered == "yes") {
answered="no";
draw(0);
}
else if (answered == "no") {
if(e.which == 81 || e.keyCode == 81) { // Q
keyNotAns('#ansQ');
}
else if(e.which == 87 || e.keyCode == 87) { // W
keyNotAns('#ansW');
}
else if(e.which == 69 || e.keyCode == 69) { // E
keyNotAns('#ansE');
}
else if(e.which == 82 || e.keyCode == 82) { // R
keyNotAns('#ansR');
}
}
});
}
There are two ways to find such bugs. The first one is using the debugger - which can be pretty tedious when you have loops.
The second approach is logging: Write a report / log of all the important actions that you can read later to find out why it failed at some point in the past.
Have a look at console.log() or a JavaScript logging framework.
I know it is not the smartest idea, but I still have to do it.
Our users want to use ENTER like TAB.
So, the best I came up with is this:
Ext.override(Ext.form.field.Base, {
initComponent: function() {
this.callParent(arguments);
this.on('afterrender', function() {
var me=this;
this.getEl().on('keypress',function (e){
if(e.getKey() == 13) {
me.nextNode().focus();
}
});
});
}
});
But it still does not work exactly the same way as TAB.
I mean, it works OK with input fields, but not other controls.
May be there is some low-level solution.
Any ideas?
In the past I've attached the listener to the document, something like this:
Ext.getDoc().on('keypress', function(event, target) {
// get the form field component
var targetEl = Ext.get(target.id),
fieldEl = targetEl.up('[class*=x-field]') || {},
field = Ext.getCmp(fieldEl.id);
if (
// the ENTER key was pressed...
event.ENTER == event.getKey() &&
// from a form field...
field &&
// which has valid data.
field.isValid()
) {
// get the next form field
var next = field.next('[isFormField]');
// focus the next field if it exists
if (next) {
event.stopEvent();
next.focus();
}
}
});
For Ext.form.field.Text and similar xtypes there is a extra config enableKeyEvents that needs to be set before the keypress/keydown/keyup events fire.
The enableKeyEvents config option needs to be set to true as it's default to false.
ExtJS API Doc
Disclaimer: I'm not an expert on ExtJs.
That said, maybe try something like:
if (e.getKey() === 13) {
me.blur();
return false; // cancel key event to prevent the [Enter] behavior
}
You could try this
if (e.getKey() === 13) {
e.keyCode = Ext.EventObject.TAB
this.fireEvent(e, {// any additional options
});
}
Haven't really tried this ever myself.
Apologize if this is answered already. Went through some of the related questions and google, but ultimately failed to see why this isn't working.
My code is as follows
<iframe id="editor"></iframe>
editorWindow = document.getElementById('editor').contentWindow;
isCtrlDown = false;
function loadEditor()
{
editorWindow.document.designMode = "on";
editorWindow.document.onkeyup = function(e) {
if (e.which == 91) isCtrlDown = false;
}
editorWindow.document.onkeydown = handleKeyDown;
}
function handleKeyDown(e)
{
if (e.which == 91) isCtrlDown = true;
if (e.which == 66 && isCtrlDown) editFont('bold');
if (e.which == 73 && isCtrlDown) editFont('italic');
}
function editFont(a,b)
{
editorWindow.document.execCommand(a,false,b);
editorWindow.focus();
}
This code works perfectly in Chrome, but the keyboard shortcuts do not work in Firefox. In fact, in Firefox it does not seem to register the events for keyup/keydown at all.
Am I doing something grossly wrong here that is mucking up Firefox?
For editable documents, you need to use addEventListener to attach key events rather than DOM0 event handler properties:
editorWindow.document.addEventListener("keydown", handleKeyDown, false);
If you care about IE 6-8, you will need to test for the existence addEventListener and add the attachEvent equivalent if it is missing.
Try using:
editorWindow = document.getElementById('editor').frameElement;
I'm not sure this will solve the issue, it may also be:
editorWindow = document.getElementById('editor').contentDocument;
Or even possibly:
editorWindow = document.getElementById('editor').frameElement.contentDocument;
One thing you can do is put the entire string in a try statement to catch any errors and see if the content is being grabbed from within the iframe.
try { editorWindow = document.getElementById('editor').contentWindow; } catch(e) { alert(e) };
The only other thought I have is that you're typing into a textbox which is within an iframe, and you may possibly have to add the onkeydown event to that specific item, such as:
var editorWindow = document.getElementById('editor').contentDocument;
var textbox = editorWindow.getElementById('my_textbox');
function loadEditor()
{
editorWindow.document.designMode = "on";
textbox.onkeydown = function(e) {
alert('hello there');
}
}
I hope one of these is the solution. I often find when it comes to cross-platform functionality it often boils down to a little trial and error.
Good Luck!