Prototype. Restrict input to numbers - javascript

I have multiple inputs to which i am attaching event listener that listens to what key was pressed, tests it with regular expression and returns true if number and false if other charachter. The problem is that input is accepting letters even if my test function returns false.
$$(".number").each(function (element) {
return $(element).observe("keypress", function (event) {
return /[\d\s]/.test(String.fromCharCode(event.keyCode));
});
});
Example on JS Bin.

You are using prototypeJS, which as far as I know doesn't allow to use return false to cancel event propagation and default behavior (As jQuery does). Though it has Event.stop(event) method
$$(".number").each(function (element) {
return $(element).observe("keypress", function (event) {
var result = /[\d\s]/.test(String.fromCharCode(event.keyCode));
if (!result) {
Event.stop(event);
}
});
});
Event.stop(event)

You can do it by testing the value on keyup, then removing whatever was entered if it doesn't match your desired pattern:
$$(".number").each(function (element) {
return $(element).observe("keyup", function (event) {
var val = this.value
if(!/[\d\s]/.test(String.fromCharCode(event.keyCode))){
this.value = val.substring(0, val.length - 1);
}
});
});

Related

How do I prevent default behavior of link click for the first 4 click

I have a link points to external url. What I want to archive is to not redirect to the link for the first 4 clicks, but at the fifth click.
I know I can use
e.preventDefault() to prevent the redirection happens, but the question is how could I resume the normal behaviour at the 5th click? (To go to that specified link)
I have created a simple code snippet to demonstrate what I want to accomplish.
https://jsfiddle.net/fuyi/k62oo6uu/
$('a').click(function(e){ console.log('event captured'); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
A link
Inside a closure you can have a variable keeping track on the number of time it was clicked.
For instance :
node.addEventListener("click", (function () {
let nbOfClicks = 0;
return function (e) {
if (nbOfClicks < 4) {
nbOfClicks = nbOfClicks + 1;
e.preventDeault();
//do stuff
} else {
// do not prevent click, do stuff
}
};
} ()), false);
The var nbOfClicks is only accessible inside the IIFE. The IIFE returns the handler for the event and can access the variable to read / update its value.
Please note that this works for exactly one DOM element.
If you want to use it for a collection, and I think this closer to your problem : on each link, prevent default behavior on click exactly four times, otherwise allow default behavior.
In this case you have something like :
$("a").each(function () {
this.addEventListener("click", (function () {
let nbOfClicks = 0;
return function (e) {
if (nbOfClicks < 4) {
nbOfClicks = nbOfClicks + 1;
e.preventDeault();
//do stuff
} else {
// do not prevent click, do stuff
}
};
} ()), false);
});
Because the this lexical context is bound to the current DOM Element when you loop over a jQuery collection.
Of course there are tradeoffs, because if you have huge numbers of links then you just looped over your entire collection and created a closure for each, which is quite expensive (both the selection and the closures). You may need to look up event delegation.
If you want a more refined solution when you can decide precisely what to do you may want to think about a generic handler function which would like this :
function handleClick (nbOfTimes, e) {
if (nbOfTimes > 4) { // 4 being a magic number, but anykind of condition could go there,
//also don't forget that you can access the target nodes via e.target
}
// then you should return some kind of data indicating
// what to do afert, like event unbinding, redirection (careful XSS) and so on...
return { unbindEvent: true, redirect: false};
}
In the end you have the more generic version of this :
$("a").each(function () {
this.addEventListener("click", (function () {
let nbOfClicks = 0;
return function (e) {
nbOfClicks = nbOfClicks + 1;
let {unbindEvent, redirect} = handleClick(nbOfClicks, e);
if (unbindEvent === true) {
// do stuff
}
if (redirect === true) {
// do stuff
}
};
} ()), false);
});
From reading the comments, it appears that what you want is a debounce, in this case if we take the second example we have
$("a").each(function () {
this.addEventListener("click", (function () {
let lastClickStamp = 0;
return function (e) {
if (lastClickStamp === 0) {
// first click ever
// or since a long period of time
lastClickStamp = e.timeStamp;
// trigger your httprequest
} else {
let diff = e.timeStamp - lastClickStamp;
if (diff > threshold) {
// threshold is your debouncing value,
// which sets the rate at which your events
// wil be processed
// here you exceed the threshold so you can take
// action, but you need to update the last time
// your function was called
lastClickStamp = e.timeStamp;
} else {
// you called the function before enough
//time elapsed
// so you do nothing
}
}
};
} ()), false);
});
For reference, I suggest looking at lodash.debounce function and for more advanced stuff to Observables and the debounce operator in a library like RxJS for instance (or xstream, most.js...).
There is also the throttle operation you could look at (in lodash and Observables).
Please ask if you need more details.
The conclusion is : use the power of JS closures !
You could count the clicks and then unbind the event. For example:
var click_count = 0;
$('.myelement').bind('click', function(e) {
e.preventDefault();
++click_count;
if (click_count === 4) {
$(this).unbind('click');
}
});
$('a').click(function(e){
var times = parseInt($(this).data("times"));
if (isNaN(times)) times=0;
times++;
$(this).data("times",times)
console.log('event captured',times);
if (times<5) e.preventDefault(); // change to !=5 to only allow 5th click
else console.log("HURRAH");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
A link

Why does this "IF" always execute even the condition is false?

This is my code:
$('.btn-back').off('click').on('click', function (event) {
var valid = carInfo.validate();
if (valid) {
event.preventDefault();
}
});
the sentence event.preventDefault() always executes no matter what value valid is.
When I change the code to
$('.btn-back').off('click').on('click', function (event) {
var valid = carInfo.validate();
if (valid) {
event.preventDefault();
} else {
}
});
it runs the way it should be. Maybe it's something related to Automatic Semicolon Insertion (ASI).
Use the jQuery Validation plugin way:
carInfo.validate() will return Validator object.
So valid variable is always true. Use valid method from docs:
$('.btn-back').off('click').on('click', function (event) {
carInfo.validate();
if (carInfo.valid()) {
event.preventDefault();
}
});

Can I put a timer on a .on('keyup' to cut down the number of updates?

I have the following code working:
$wmdInput.on('keyup', function () {
var rawContent = $wmdInput.val();
scope.$apply(function () {
ngModel.$setViewValue(rawContent);
});
});
However it seems to slow down my entering of characters. Is there some way I could put a timeout on this so that all the data is saved but it just does not do it more than for example once every two seconds?
Pure AngularJS approach
var promise;
$wmdInput.on('keyup', function () {
$timeout.cancel(promise);
promise = $timeout(function() {
var rawContent = $wmdInput.val();
ngModel.$setViewValue(rawContent);
}, 2000);
});
If you can use lodash (which you should) just wrap the function in _.debounce:
$wmdInput.on('keyup', _.debounce(function () {
var rawContent = $wmdInput.val();
scope.$apply(function () {
ngModel.$setViewValue(rawContent);
});
}, 300));
This will cause the function to be called only when the user has stopped typing for 300 ms -- obviously you should tweak the wait to whatever works best for you.
IMO debouncing is more appropriate than throttling in this case.
Reactive Extensions for JavaScript might be an option for you to use. You can setup the keyup event as an event source and throttle the events. In fact the README has an example that seems similar to what you want...
var $input = $('#input'),
$results = $('#results');
/* Only get the value from each key up */
var keyups = Rx.Observable.fromEvent(input, 'keyup')
.map(function (e) {
return e.target.value;
})
.filter(function (text) {
return text.length > 2;
});
/* Now throttle/debounce the input for 500ms */
var throttled = keyups
.throttle(500 /* ms */);
/* Now get only distinct values, so we eliminate
the arrows and other control characters */
var distinct = throttled
.distinctUntilChanged();
In your case you may not want the filter for length > 2, so just remove that expression. Regardless, you can just tack on a subscribe at the end to process your event
distinct.subscribe(function(value) {
scope.$apply(function () {
ngModel.$setViewValue(value);
});
});
And there are bindings for AngularJS for you as well.
in the function above, add two lines to:
remove keyup event from wmdInput.
set timer to add keyup event to
wmdInput after 2 seconds.

Is it possible to get a return type from a keyup function?

My current code is this and what I'm trying to is see if I could get a return value after the key up is done.
$("#confirm").keyup(function(event){
event.preventDefault();
ajax_pass_check();
});
so I would end up with something like this because my ajax_pass_check(); function returns true/false.
$("#confirm").keyup(function(event){
event.preventDefault();
return ajax_pass_check();
});
I would like to see if I could do that, and try something like
var one = $("#confirm").keyup(function(event){
event.preventDefault();
return ajax_pass_check();
});
I'm new to javascript and I've looked on google for awhile and I haven't found what I needed so I thought I'd ask. However when I did try that, I didn't get the expected result I was hoping for. Since var one remained false, when it should have been true after the function ajax_pass_check();
~edit: I took advice one of you guys (thanks for all the replies!) I still can't figure out why my var one variable false even though I set it to true in the keyup function.
$(document).ready(function(){
var one = false;
$("#confirm").keyup(function(event){
event.preventDefault();
one = ajax_pass_check();
//one = true; //even if I do that it doesn't work.
});
if(one == true)
{
$('input[type="submit"]').removeAttr('disabled');
}
else
{
$('input[type="submit"]').attr('disabled','disabled');
}
});
Instead of returning a value, you should consider using global scope for the variable, and set its desired value inside your function:
var wasSuccessful = false;
$("#confirm").keyup(function(event){
event.preventDefault();
your_function();
});
function yourfunction() {
if your awesome code worked { wasSuccessful = true; }
else { wasSuccessful = false; }
}
No, but you could call another function to use the result of ajax_pass_check. For example,
$("#confirm").keyup(function(event){
event.preventDefault();
doSomethingElse(ajax_pass_check());
});
function doSomethingElse(keyUpOk) { // Do something ... }
Assuming (based on your function names) you are using this to do some form of validation this will allow you to display or clear an error message.
The reason you cant do
var one = $("#confirm").keyup(function(event){
event.preventDefault();
return ajax_pass_check();
});
is because the key up function is just binding you function to the event, so this code will have already been executed when the key is released. You will probably want to call a function so that something is done with the result of the keyup event handler after the keyup event is fired, not when you function is bound to the event.
Try this instead
$(document).ready(function(){
var one = false;
$("#confirm").keyup(function(event){
event.preventDefault();
one = ajax_pass_check();
//one = true; //even if I do that it doesn't work.
if(one == true)
{
$('input[type="submit"]').removeAttr('disabled');
}
else
{
$('input[type="submit"]').attr('disabled','disabled');
}
});
});
The Answer is: No
The jQuery Function keyup doesnt return a special value, only the jQuery Object see the API(at the top), since it is used only to bind functions.
Depence on what you want to achieve, one of the solutions, mentioned by the others could solve your issue.

How can I stop a default event when using a named function in addEvent?

Normally, if I wish to stop a default event in mootools I can do this:
$('form').addEvent('submit', function(e) {
e.stop();
//Do stuff here
});
However, I don't like using an anonymous function in events because I often want to reuse the code. Lets say I have a validate function. I could do this:
$('form').addEvent('submit', validate);
which works fine until I want to stop the default event. validate obviously doesn't know what e is so I can't just do e.stop(). Also I've tried passing the event as a variable to validate but whenever I use a named function with parameters, the function gets called automatically on domready, rather than on the event firing. Even worse, an error is thrown when the event is fired.
What am I doing wrong?
UPDATE: Here is the validate function in full, just in case. However, since the error is occurring after the first line, I doubt anything after is being called so it is probably irrelevant.
var validate = function(e) {
e.stop();
if(this.get('tag') === 'form') {
this.getElements('input.text').each(validate);
}
else {
element = this;
div = element.getParent();
input = element.get('value');
filter = JSON.decode(div.get('filter'));
if(!filter.some(function(value, key) {
if(value === 'required') if(!setAndNotEmpty(element, input)) return true;
if(value === 'email') if(!isEmail(element, input)) return true;
if(value === 'date') if(!isDate(element, input)) return true;
if(value === 'time') if(!isTime(element, input)) return true;
if(key === 'chars') if(!charsLessThan(element, input, value)) return true;
if(key === 'words') if(!wordsLessThan(element, input, value)) return true;
return false;
})) setFault('', element);
}
}
you need to declare "validate" as follows:
function validate(e){
}
Then you can use e.stop()
function validate(e){
e.stop();
}
$('form').addEvent('submit', validate);
of note is that in jQuery, you can also return a result from a method to stop propogation. I'm not sure if mootools allows this, but you could possibly do this by:
function validate(e){
return false;
}
$('form').addEvent('submit', validate);
To answer the "what am I doing wrong" part - you're simply misunderstanding what is happening when you pass in an anonymous method. Passing an anonymous method function(e) {} is not causing e to be passed, it is simply defining the name of the first argument to be passed in. The event object will be passed into the method whether or not the method names the argument, hence you will find that the following would also work:
function validate(){
arguments[0].stop();
}
$('form').addEvent('submit', validate);
$('form').addEvent('submit', function(e) {
e.stop();
//Do stuff here
}
I don't know if this is part of your issue, but watch your () in addEvent arguments.
$('form').addEvent('submit', function(e)) {
Your 'e' argument wasn't properly closed off in the first line.
bindWithEvent Changes the scope of this within the target function to refer to the bind parameter. It also makes "space" for an event. This allows the function to be used in conjunction with Element:addEvent and arguments.
preventDefault Cross browser method to prevent the default action of the event.
function validate(e)
{
e.preventDefault();
if(this.get('tag') === 'form') {
this.getElements('input.text').each(validate);
}
else {
element = this;
div = element.getParent();
input = element.get('value');
filter = JSON.decode(div.get('filter'));
if(!filter.some(function(value, key) {
if(value === 'required') if(!setAndNotEmpty(element, input)) return true;
if(value === 'email') if(!isEmail(element, input)) return true;
if(value === 'date') if(!isDate(element, input)) return true;
if(value === 'time') if(!isTime(element, input)) return true;
if(key === 'chars') if(!charsLessThan(element, input, value)) return true;
if(key === 'words') if(!wordsLessThan(element, input, value)) return true;
return false;
})) setFault('', element);
}
$('form').addEvent('submit', validate.bindWithEvent(this));
Pretty self explanitory when you see the words prevent default next to each other. So when you submit a form the page will not go to your action.

Categories